Add editor packages
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user