Gim/internal/editor/integration_join_test.go
2026-04-06 19:24:47 -07:00

180 lines
5.6 KiB
Go

package editor
import (
"testing"
"git.gophernest.net/azpect/TextEditor/internal/core"
)
func TestJoinLines(t *testing.T) {
t.Run("J joins current line with next line using a space", func(t *testing.T) {
tm := newTestModelWithLines(t, []string{"hello", "world"})
sendKeys(tm, "J")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"hello world"})
if m.ActiveWindow().Cursor.Line != 0 {
t.Errorf("Cursor.Line = %d, want 0", m.ActiveWindow().Cursor.Line)
}
if m.ActiveWindow().Cursor.Col != 0 {
t.Errorf("Cursor.Col = %d, want 0", m.ActiveWindow().Cursor.Col)
}
})
t.Run("J joins from the cursor line, not always the first line", func(t *testing.T) {
tm := newTestModelWithLinesAndCursorPos(t, []string{"one", "two", "three"}, core.Position{Line: 1, Col: 2})
sendKeys(tm, "J")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"one", "two three"})
if m.ActiveWindow().Cursor.Line != 1 {
t.Errorf("Cursor.Line = %d, want 1", m.ActiveWindow().Cursor.Line)
}
if m.ActiveWindow().Cursor.Col != 2 {
t.Errorf("Cursor.Col = %d, want 2", m.ActiveWindow().Cursor.Col)
}
})
t.Run("J trims indentation from the joined line", func(t *testing.T) {
tm := newTestModelWithLines(t, []string{"foo", " bar"})
sendKeys(tm, "J")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"foo bar"})
})
}
func TestJoinLinesNoSpace(t *testing.T) {
t.Run("gJ joins without inserting a space", func(t *testing.T) {
tm := newTestModelWithLinesAndCursorPos(t, []string{"hello", "world"}, core.Position{Line: 0, Col: 3})
sendKeys(tm, "g", "J")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"helloworld"})
if m.ActiveWindow().Cursor.Line != 0 {
t.Errorf("Cursor.Line = %d, want 0", m.ActiveWindow().Cursor.Line)
}
if m.ActiveWindow().Cursor.Col != 3 {
t.Errorf("Cursor.Col = %d, want 3", m.ActiveWindow().Cursor.Col)
}
})
t.Run("gJ preserves leading whitespace on the next line", func(t *testing.T) {
tm := newTestModelWithLines(t, []string{"foo", " bar"})
sendKeys(tm, "g", "J")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"foo bar"})
})
}
func TestJoinLinesWithCount(t *testing.T) {
t.Run("3J joins three lines with spaces", func(t *testing.T) {
tm := newTestModelWithLinesAndCursorPos(t, []string{"a", "b", "c", "d"}, core.Position{Line: 0, Col: 1})
sendKeys(tm, "3", "J")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"a b c", "d"})
if m.ActiveWindow().Cursor.Line != 0 {
t.Errorf("Cursor.Line = %d, want 0", m.ActiveWindow().Cursor.Line)
}
if m.ActiveWindow().Cursor.Col != 1 {
t.Errorf("Cursor.Col = %d, want 1", m.ActiveWindow().Cursor.Col)
}
})
t.Run("3gJ joins three lines without spaces", func(t *testing.T) {
tm := newTestModelWithLines(t, []string{"a", "b", "c", "d"})
sendKeys(tm, "3", "g", "J")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"abc", "d"})
})
t.Run("count larger than remaining lines joins through end of file", func(t *testing.T) {
tm := newTestModelWithLinesAndCursorPos(t, []string{"a", "b", "c"}, core.Position{Line: 1, Col: 0})
sendKeys(tm, "9", "J")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"a", "b c"})
})
}
func TestJoinLinesEdgeCases(t *testing.T) {
t.Run("J on last line does nothing", func(t *testing.T) {
tm := newTestModelWithLinesAndCursorPos(t, []string{"one", "two"}, core.Position{Line: 1, Col: 2})
sendKeys(tm, "J")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"one", "two"})
if m.ActiveWindow().Cursor.Line != 1 {
t.Errorf("Cursor.Line = %d, want 1", m.ActiveWindow().Cursor.Line)
}
if m.ActiveWindow().Cursor.Col != 2 {
t.Errorf("Cursor.Col = %d, want 2", m.ActiveWindow().Cursor.Col)
}
})
t.Run("gJ on single-line buffer does nothing", func(t *testing.T) {
tm := newTestModelWithLines(t, []string{"solo"})
sendKeys(tm, "g", "J")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"solo"})
})
t.Run("J across many empty lines keeps a single separating space", func(t *testing.T) {
tm := newTestModelWithLines(t, []string{"foo", "", "", "", "bar"})
sendKeys(tm, "5", "J")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"foo bar"})
})
}
func TestJoinLinesRepeatAndUndo(t *testing.T) {
t.Run("dot repeats J join", func(t *testing.T) {
tm := newTestModelWithLines(t, []string{"a", "b", "c", "d"})
sendKeys(tm, "J", "j", ".")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"a b", "c d"})
})
t.Run("dot repeats gJ join", func(t *testing.T) {
tm := newTestModelWithLines(t, []string{"a", "b", "c", "d"})
sendKeys(tm, "g", "J", "j", ".")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"ab", "cd"})
})
t.Run("undo restores lines after J", func(t *testing.T) {
tm := newTestModelWithLines(t, []string{"left", "right"})
sendKeys(tm, "J", "u")
m := getFinalModel(t, tm)
assertBufferLines(t, m.ActiveBuffer(), []string{"left", "right"})
})
}
func assertBufferLines(t *testing.T, buf *core.Buffer, want []string) {
t.Helper()
got := make([]string, buf.LineCount())
for i := 0; i < buf.LineCount(); i++ {
got[i] = buf.Line(i)
}
if len(got) != len(want) {
t.Errorf("lines = %q (len=%d), want %q (len=%d)", got, len(got), want, len(want))
return
}
for i := range got {
if got[i] != want[i] {
t.Errorf("lines = %q (len=%d), want %q (len=%d)", got, len(got), want, len(want))
return
}
}
}