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.
243 lines
5.6 KiB
Go
243 lines
5.6 KiB
Go
package syntax
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.gophernest.net/azpect/TextEditor/internal/core"
|
|
"git.gophernest.net/azpect/TextEditor/internal/style"
|
|
"github.com/charmbracelet/lipgloss"
|
|
)
|
|
|
|
func TestTreeSitterEngineHighlightsGoKeywordAndString(t *testing.T) {
|
|
b := core.NewBufferBuilder().
|
|
WithFilename("sample.go").
|
|
WithFiletype("go").
|
|
WithLines([]string{
|
|
"package main",
|
|
"func main() {",
|
|
" s := \"hi\"",
|
|
"}",
|
|
}).
|
|
Build()
|
|
buf := &b
|
|
|
|
engine := NewTreeSitterEngine(style.DefaultStyles())
|
|
engine.PrepareBuffer(buf)
|
|
|
|
base := engine.styles.LineStyle
|
|
|
|
line0 := buf.Line(0)
|
|
map0 := engine.LineStyleMap(buf, 0)
|
|
if len(map0) != len([]rune(line0)) {
|
|
t.Fatalf("line 0 style map length mismatch")
|
|
}
|
|
if len(map0) == 0 || styleEquivalent(map0[0], base) {
|
|
t.Fatalf("expected 'package' keyword to be highlighted")
|
|
}
|
|
|
|
line2 := buf.Line(2)
|
|
stringStart := strings.Index(line2, "\"hi\"")
|
|
if stringStart < 0 {
|
|
t.Fatalf("test setup failed: string literal not found")
|
|
}
|
|
map2 := engine.LineStyleMap(buf, 2)
|
|
if styleEquivalent(map2[stringStart+1], base) {
|
|
t.Fatalf("expected string contents to be highlighted")
|
|
}
|
|
}
|
|
|
|
func TestTreeSitterEngineHighlightsMultilineRawString(t *testing.T) {
|
|
b := core.NewBufferBuilder().
|
|
WithFilename("sample.go").
|
|
WithFiletype("go").
|
|
WithLines([]string{
|
|
"package main",
|
|
"func main() {",
|
|
" s := `hello",
|
|
"world`",
|
|
" println(s)",
|
|
"}",
|
|
}).
|
|
Build()
|
|
buf := &b
|
|
|
|
engine := NewTreeSitterEngine(style.DefaultStyles())
|
|
engine.PrepareBuffer(buf)
|
|
|
|
base := engine.styles.LineStyle
|
|
map3 := engine.LineStyleMap(buf, 3)
|
|
if len(map3) == 0 {
|
|
t.Fatalf("expected style map on multiline raw string line")
|
|
}
|
|
if styleEquivalent(map3[0], base) {
|
|
t.Fatalf("expected multiline raw string line to be highlighted")
|
|
}
|
|
}
|
|
|
|
func TestTreeSitterEngineApplyEditUpdatesStyleCategory(t *testing.T) {
|
|
b := core.NewBufferBuilder().
|
|
WithFilename("sample.go").
|
|
WithFiletype("go").
|
|
WithLines([]string{
|
|
"package main",
|
|
"func main() {",
|
|
" x := 123",
|
|
"}",
|
|
}).
|
|
Build()
|
|
buf := &b
|
|
|
|
engine := NewTreeSitterEngine(style.DefaultStyles())
|
|
engine.PrepareBuffer(buf)
|
|
|
|
oldLine := buf.Line(2)
|
|
oldIdx := strings.Index(oldLine, "123")
|
|
if oldIdx < 0 {
|
|
t.Fatalf("test setup failed: number not found")
|
|
}
|
|
oldMap := engine.LineStyleMap(buf, 2)
|
|
oldStyle := oldMap[oldIdx]
|
|
|
|
var edit *core.BufferEdit
|
|
buf.OnChange = func(change core.BufferChange) {
|
|
edit = change.Edit
|
|
}
|
|
|
|
buf.SetLine(2, " x := \"abc\"")
|
|
if edit == nil {
|
|
t.Fatalf("expected edit metadata from SetLine")
|
|
}
|
|
|
|
engine.ApplyEdit(buf, edit)
|
|
engine.PrepareBuffer(buf)
|
|
|
|
newLine := buf.Line(2)
|
|
newIdx := strings.Index(newLine, "abc")
|
|
if newIdx < 0 {
|
|
t.Fatalf("test setup failed: string not found")
|
|
}
|
|
newMap := engine.LineStyleMap(buf, 2)
|
|
newStyle := newMap[newIdx]
|
|
|
|
if styleEquivalent(newStyle, engine.styles.LineStyle) {
|
|
t.Fatalf("expected updated string to be highlighted")
|
|
}
|
|
if styleEquivalent(oldStyle, newStyle) {
|
|
t.Fatalf("expected style category to change from number to string")
|
|
}
|
|
}
|
|
|
|
func TestTreeSitterEngineApplyEditLineCountChangeForcesFullDirty(t *testing.T) {
|
|
b := core.NewBufferBuilder().
|
|
WithFilename("sample.go").
|
|
WithFiletype("go").
|
|
WithLines([]string{"package main", "func main() {}"}).
|
|
Build()
|
|
buf := &b
|
|
|
|
engine := NewTreeSitterEngine(style.DefaultStyles())
|
|
engine.PrepareBuffer(buf)
|
|
bc := engine.getCache(buf)
|
|
|
|
var edit *core.BufferEdit
|
|
buf.OnChange = func(change core.BufferChange) {
|
|
edit = change.Edit
|
|
}
|
|
|
|
buf.InsertLine(1, "var x = 1")
|
|
if edit == nil {
|
|
t.Fatalf("expected edit metadata from InsertLine")
|
|
}
|
|
|
|
engine.ApplyEdit(buf, edit)
|
|
if !bc.dirtyAll {
|
|
t.Fatalf("expected line count change to set dirtyAll")
|
|
}
|
|
|
|
engine.PrepareBuffer(buf)
|
|
if !bc.built {
|
|
t.Fatalf("expected cache rebuilt after prepare")
|
|
}
|
|
if bc.count != buf.LineCount() {
|
|
t.Fatalf("expected cache line count to match buffer")
|
|
}
|
|
if bc.dirtyAll {
|
|
t.Fatalf("expected dirtyAll to clear after rebuild")
|
|
}
|
|
}
|
|
|
|
func TestTreeSitterEngineUnsupportedBufferFallsBackToDefaultStyles(t *testing.T) {
|
|
b := core.NewBufferBuilder().
|
|
WithFilename("notes.txt").
|
|
WithFiletype("txt").
|
|
WithLines([]string{"just text", "with no language"}).
|
|
Build()
|
|
buf := &b
|
|
|
|
engine := NewTreeSitterEngine(style.DefaultStyles())
|
|
engine.PrepareBuffer(buf)
|
|
|
|
base := engine.styles.LineStyle
|
|
line := buf.Line(0)
|
|
m := engine.LineStyleMap(buf, 0)
|
|
if len(m) != len([]rune(line)) {
|
|
t.Fatalf("style map length mismatch on fallback buffer")
|
|
}
|
|
for i := range m {
|
|
if !styleEquivalent(m[i], base) {
|
|
t.Fatalf("expected default style for unsupported filetype at rune %d", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTreeSitterEngineLastLineEditDoesNotPanicAndRebuilds(t *testing.T) {
|
|
b := core.NewBufferBuilder().
|
|
WithFilename("sample.go").
|
|
WithFiletype("go").
|
|
WithLines([]string{"package main", "func main() {", " return", "}"}).
|
|
Build()
|
|
buf := &b
|
|
|
|
engine := NewTreeSitterEngine(style.DefaultStyles())
|
|
engine.PrepareBuffer(buf)
|
|
bc := engine.getCache(buf)
|
|
|
|
var edit *core.BufferEdit
|
|
buf.OnChange = func(change core.BufferChange) {
|
|
edit = change.Edit
|
|
}
|
|
|
|
buf.SetLine(3, "// end")
|
|
if edit == nil {
|
|
t.Fatalf("expected edit metadata for last line change")
|
|
}
|
|
|
|
engine.ApplyEdit(buf, edit)
|
|
engine.PrepareBuffer(buf)
|
|
|
|
if !bc.built {
|
|
t.Fatalf("expected cache built after last-line edit")
|
|
}
|
|
if len(bc.dirty) != 0 {
|
|
t.Fatalf("expected dirty ranges cleared after rebuild")
|
|
}
|
|
}
|
|
|
|
func styleEquivalent(a, b lipgloss.Style) bool {
|
|
return styleSignature(a) == styleSignature(b)
|
|
}
|
|
|
|
func styleSignature(s lipgloss.Style) string {
|
|
return fmt.Sprintf(
|
|
"fg=%v,bg=%v,bold=%v,italic=%v,underline=%v,reverse=%v",
|
|
s.GetForeground(),
|
|
s.GetBackground(),
|
|
s.GetBold(),
|
|
s.GetItalic(),
|
|
s.GetUnderline(),
|
|
s.GetReverse(),
|
|
)
|
|
}
|