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) } }