100 lines
2.0 KiB
Go
100 lines
2.0 KiB
Go
package buffer
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"time"
|
|
|
|
"code.chimeric.al/dwrz/wen/internal/editor/buffer/line"
|
|
)
|
|
|
|
type Buffer struct {
|
|
cursor *Cursor
|
|
lines []*line.Line
|
|
log *slog.Logger
|
|
name string
|
|
offset *offset
|
|
saved time.Time
|
|
}
|
|
|
|
// offset is the 0-indexed first visible display column (glyph) and row (line)
|
|
// of the buffer viewport.
|
|
type offset struct {
|
|
glyph int
|
|
line int
|
|
}
|
|
|
|
func (b *Buffer) CursorLine() *line.Line {
|
|
return b.lines[b.cursor.line]
|
|
}
|
|
|
|
func (b *Buffer) Name() string {
|
|
return b.name
|
|
}
|
|
|
|
type NewBufferParams struct {
|
|
Name string
|
|
Log *slog.Logger
|
|
}
|
|
|
|
// Create creates or truncates the named file on disk.
|
|
// It returns a Buffer initialized with a single empty line,
|
|
// so CursorLine is always valid.
|
|
func Create(p NewBufferParams) (*Buffer, error) {
|
|
var b = &Buffer{
|
|
cursor: &Cursor{},
|
|
lines: []*line.Line{{}},
|
|
log: p.Log,
|
|
name: p.Name,
|
|
offset: &offset{},
|
|
}
|
|
|
|
// Create the file.
|
|
f, err := os.Create(b.name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create file: %w", err)
|
|
}
|
|
if err := f.Close(); err != nil {
|
|
return nil, fmt.Errorf("failed to close file: %w", err)
|
|
}
|
|
|
|
return b, nil
|
|
}
|
|
|
|
// Open reads the named file into a Buffer. If the file is empty, the Buffer
|
|
// is initialized with a single empty line so CursorLine is always valid.
|
|
func Open(p NewBufferParams) (*Buffer, error) {
|
|
var b = &Buffer{
|
|
cursor: &Cursor{},
|
|
lines: []*line.Line{},
|
|
log: p.Log,
|
|
name: p.Name,
|
|
offset: &offset{},
|
|
}
|
|
|
|
data, err := os.ReadFile(b.name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open file: %w", err)
|
|
}
|
|
|
|
// Trim a single trailing newline so saving doesn't add an extra blank line.
|
|
data = bytes.TrimSuffix(data, []byte("\n"))
|
|
data = bytes.TrimSuffix(data, []byte("\r"))
|
|
|
|
if len(data) > 0 {
|
|
for _, raw := range bytes.Split(data, []byte("\n")) {
|
|
raw = bytes.TrimSuffix(raw, []byte("\r"))
|
|
b.lines = append(b.lines, line.New(string(raw)))
|
|
}
|
|
}
|
|
|
|
// Add a line to write in, if the file is empty.
|
|
if len(b.lines) == 0 {
|
|
b.lines = append(b.lines, &line.Line{})
|
|
}
|
|
|
|
return b, nil
|
|
}
|