This is so vibe coded, but in the interest of time, its a bit necessary. Plus this is a complex problem that I don't have the mental bandwidth to invest right now.
113 lines
2.9 KiB
Go
113 lines
2.9 KiB
Go
package syntax
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"git.gophernest.net/azpect/TextEditor/internal/core"
|
|
"git.gophernest.net/azpect/TextEditor/internal/style"
|
|
)
|
|
|
|
type seqOp func(*core.Buffer, *core.Window)
|
|
|
|
func TestTreeSitterEngineEditSequences(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
lines []string
|
|
opList []seqOp
|
|
}{
|
|
{
|
|
name: "setline and insertline",
|
|
lines: []string{"package main", "func main() {", " x := 1", "}"},
|
|
opList: []seqOp{
|
|
func(b *core.Buffer, _ *core.Window) { b.SetLine(2, " x := 2") },
|
|
func(b *core.Buffer, _ *core.Window) { b.InsertLine(3, " println(x)") },
|
|
},
|
|
},
|
|
{
|
|
name: "delete middle line",
|
|
lines: []string{"package main", "func main() {", " x := 1", " y := 2", "}"},
|
|
opList: []seqOp{
|
|
func(b *core.Buffer, _ *core.Window) { b.DeleteLine(3) },
|
|
},
|
|
},
|
|
{
|
|
name: "multiline string evolve",
|
|
lines: []string{"package main", "func main() {", " s := `a", "b`", " _ = s", "}"},
|
|
opList: []seqOp{
|
|
func(b *core.Buffer, _ *core.Window) { b.SetLine(2, " s := `alpha") },
|
|
func(b *core.Buffer, _ *core.Window) { b.SetLine(3, "beta`") },
|
|
},
|
|
},
|
|
{
|
|
name: "undo redo sequence",
|
|
lines: []string{"package main", "func main() {", " v := 3", "}"},
|
|
opList: []seqOp{
|
|
func(b *core.Buffer, w *core.Window) {
|
|
b.UndoStack.BeginBlock(w.Cursor)
|
|
b.SetLine(2, " v := 9")
|
|
b.UndoStack.EndBlock(core.Position{Line: 2, Col: 8})
|
|
},
|
|
func(b *core.Buffer, w *core.Window) { _ = b.Undo(w) },
|
|
func(b *core.Buffer, w *core.Window) { _ = b.Redo(w) },
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
b := core.NewBufferBuilder().
|
|
WithFilename("seq.go").
|
|
WithFiletype("go").
|
|
WithLines(tc.lines).
|
|
Build()
|
|
buf := &b
|
|
|
|
w := core.NewWindowBuilder().WithBuffer(buf).WithDimensions(120, 40).Build()
|
|
win := &w
|
|
|
|
engine := NewTreeSitterEngine(style.DefaultStyles())
|
|
|
|
buf.OnChange = func(change core.BufferChange) {
|
|
if change.Edit != nil {
|
|
engine.ApplyEdit(buf, change.Edit)
|
|
} else {
|
|
engine.InvalidateBuffer(buf)
|
|
}
|
|
}
|
|
|
|
engine.PrepareBuffer(buf)
|
|
assertEngineInvariants(t, engine, buf, "initial")
|
|
|
|
for i, op := range tc.opList {
|
|
op(buf, win)
|
|
engine.PrepareBuffer(buf)
|
|
assertEngineInvariants(t, engine, buf, fmt.Sprintf("after op %d", i+1))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func assertEngineInvariants(t *testing.T, engine *TreeSitterEngine, buf *core.Buffer, phase string) {
|
|
t.Helper()
|
|
|
|
bc := engine.getCache(buf)
|
|
if !bc.built {
|
|
t.Fatalf("%s: expected built cache", phase)
|
|
}
|
|
if bc.dirtyAll {
|
|
t.Fatalf("%s: expected dirtyAll=false after prepare", phase)
|
|
}
|
|
if len(bc.dirty) != 0 {
|
|
t.Fatalf("%s: expected no pending dirty ranges", phase)
|
|
}
|
|
|
|
for i := 0; i < buf.LineCount(); i++ {
|
|
line := buf.Line(i)
|
|
m := engine.LineStyleMap(buf, i)
|
|
if len(m) != len([]rune(line)) {
|
|
t.Fatalf("%s: line %d style length mismatch: got %d want %d", phase, i, len(m), len([]rune(line)))
|
|
}
|
|
}
|
|
}
|