1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
package editor
import (
"testing"
"glint/internal/theme"
"github.com/charmbracelet/lipgloss"
)
func segText(segs []segment) string {
s := ""
for _, sg := range segs {
s += sg.text
}
return s
}
func TestWrapLineFitsReturnsOneSegment(t *testing.T) {
segs := wrapLine("short line", 80)
if len(segs) != 1 || segs[0].text != "short line" || segs[0].start != 0 {
t.Fatalf("segs = %#v", segs)
}
}
func TestWrapLineEmpty(t *testing.T) {
segs := wrapLine("", 80)
if len(segs) != 1 || segs[0].text != "" || segs[0].start != 0 {
t.Fatalf("empty line segs = %#v", segs)
}
}
func TestWrapLineWordBreakPartition(t *testing.T) {
line := "aaaa bbbb cccc"
segs := wrapLine(line, 9)
if segText(segs) != line {
t.Errorf("partition broken: %q != %q", segText(segs), line)
}
if len(segs) != 2 {
t.Fatalf("want 2 segments, got %d: %#v", len(segs), segs)
}
if segs[0].text != "aaaa " || segs[0].start != 0 {
t.Errorf("seg0 = %#v", segs[0])
}
if segs[1].text != "bbbb cccc" || segs[1].start != 5 {
t.Errorf("seg1 = %#v", segs[1])
}
}
func TestWrapLineHardBreaksLongWord(t *testing.T) {
segs := wrapLine("abcdefghij", 4)
if segText(segs) != "abcdefghij" {
t.Errorf("partition broken: %q", segText(segs))
}
want := []segment{{"abcd", 0}, {"efgh", 4}, {"ij", 8}}
if len(segs) != len(want) {
t.Fatalf("got %d segments, want %d", len(segs), len(want))
}
for i := range want {
if segs[i] != want[i] {
t.Errorf("seg[%d] = %#v, want %#v", i, segs[i], want[i])
}
}
}
func TestWrapLineMultibytePartition(t *testing.T) {
line := "héllo wörld café"
segs := wrapLine(line, 7)
if segText(segs) != line {
t.Errorf("multibyte partition broken: %q != %q", segText(segs), line)
}
}
func TestSliceSpansAcrossBoundary(t *testing.T) {
red := lipgloss.NewStyle().Foreground(lipgloss.Color("#ff0000"))
blue := lipgloss.NewStyle().Foreground(lipgloss.Color("#0000ff"))
spans := []Span{{Text: "abc", Style: red}, {Text: "def", Style: blue}}
out := sliceSpans(spans, 2, 5) // "c" (red) + "de" (blue)
got := ""
for _, sp := range out {
got += sp.Text
}
if got != "cde" {
t.Errorf("sliced text = %q, want cde", got)
}
if len(out) != 2 || out[0].Style.GetForeground() != lipgloss.Color("#ff0000") ||
out[1].Style.GetForeground() != lipgloss.Color("#0000ff") {
t.Errorf("styles not preserved: %#v", out)
}
}
func TestBuildVisualAndCursorIndex(t *testing.T) {
e := New()
e.SetTheme(theme.FlexokiDark())
e.Lines = []string{"aaaa bbbb cccc", "x"}
e.Width = 9
rows := e.buildVisual()
// line 0 wraps into 2 visual rows, line 1 is 1 → 3 total.
if len(rows) != 3 {
t.Fatalf("visual rows = %d, want 3", len(rows))
}
// cursor at end of seg2 of line 0 maps to the second visual row.
if got := cursorVIndex(rows, Position{Row: 0, Col: 10}); got != 1 {
t.Errorf("cursorVIndex = %d, want 1", got)
}
// cursor on line 1 maps to row index 2.
if got := cursorVIndex(rows, Position{Row: 1, Col: 0}); got != 2 {
t.Errorf("cursorVIndex(line1) = %d, want 2", got)
}
}
|