All checks were successful
Run Test Suite / test (push) Successful in 56s
Not sure if this is perfect, but it seems to be working
1675 lines
56 KiB
Go
1675 lines
56 KiB
Go
package editor
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"git.gophernest.net/azpect/TextEditor/internal/core"
|
|
)
|
|
|
|
func TestPasteLinewiseBasic(t *testing.T) {
|
|
t.Run("p pastes single line after cursor line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 3 {
|
|
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "line 1" {
|
|
t.Errorf("Line(0) = %q, want 'line 1'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "inserted" {
|
|
t.Errorf("Line(1) = %q, want 'inserted'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "line 2" {
|
|
t.Errorf("Line(2) = %q, want 'line 2'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p moves cursor to first pasted line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2", "line 3"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 5}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveWindow().Cursor.Line != 1 {
|
|
t.Errorf("CursorY() = %d, want 1", m.ActiveWindow().Cursor.Line)
|
|
}
|
|
})
|
|
|
|
t.Run("p from middle of file", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2", "line 3"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 4 {
|
|
t.Errorf("LineCount() = %d, want 4", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "inserted" {
|
|
t.Errorf("Line(2) = %q, want 'inserted'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
if m.ActiveWindow().Cursor.Line != 2 {
|
|
t.Errorf("CursorY() = %d, want 2", m.ActiveWindow().Cursor.Line)
|
|
}
|
|
})
|
|
|
|
t.Run("p at end of file", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 3 {
|
|
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "inserted" {
|
|
t.Errorf("Line(2) = %q, want 'inserted'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPasteLinewiseMultipleLines(t *testing.T) {
|
|
t.Run("p pastes multiple lines in correct order", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"first", "second", "third"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 5 {
|
|
t.Errorf("LineCount() = %d, want 5", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "line 1" {
|
|
t.Errorf("Line(0) = %q, want 'line 1'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "first" {
|
|
t.Errorf("Line(1) = %q, want 'first'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "second" {
|
|
t.Errorf("Line(2) = %q, want 'second'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[3].String() != "third" {
|
|
t.Errorf("Line(3) = %q, want 'third'", m.ActiveBuffer().Lines[3].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[4].String() != "line 2" {
|
|
t.Errorf("Line(4) = %q, want 'line 2'", m.ActiveBuffer().Lines[4].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p with multiple lines moves cursor to first pasted line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"first", "second"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveWindow().Cursor.Line != 1 {
|
|
t.Errorf("CursorY() = %d, want 1", m.ActiveWindow().Cursor.Line)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPasteLinewiseWithCount(t *testing.T) {
|
|
t.Run("2p pastes content twice", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "2", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 4 {
|
|
t.Errorf("LineCount() = %d, want 4", m.ActiveBuffer().LineCount())
|
|
}
|
|
// Both "inserted" lines should appear after line 1
|
|
if m.ActiveBuffer().Lines[1].String() != "inserted" {
|
|
t.Errorf("Line(1) = %q, want 'inserted'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "inserted" {
|
|
t.Errorf("Line(2) = %q, want 'inserted'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[3].String() != "line 2" {
|
|
t.Errorf("Line(3) = %q, want 'line 2'", m.ActiveBuffer().Lines[3].String())
|
|
}
|
|
})
|
|
|
|
t.Run("3p pastes content three times", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"original"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"pasted"}),
|
|
)
|
|
sendKeys(tm, "3", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 4 {
|
|
t.Errorf("LineCount() = %d, want 4", m.ActiveBuffer().LineCount())
|
|
}
|
|
for i := 1; i <= 3; i++ {
|
|
if m.ActiveBuffer().Lines[i].String() != "pasted" {
|
|
t.Errorf("Line(%d) = %q, want 'pasted'", i, m.ActiveBuffer().Lines[i].String())
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("2p with multiple lines pastes all lines twice in order", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"original"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"first", "second"}),
|
|
)
|
|
sendKeys(tm, "2", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Should be: original, first, second, first, second
|
|
if m.ActiveBuffer().LineCount() != 5 {
|
|
t.Errorf("LineCount() = %d, want 5", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "original" {
|
|
t.Errorf("Line(0) = %q, want 'original'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "first" {
|
|
t.Errorf("Line(1) = %q, want 'first'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "second" {
|
|
t.Errorf("Line(2) = %q, want 'second'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[3].String() != "first" {
|
|
t.Errorf("Line(3) = %q, want 'first'", m.ActiveBuffer().Lines[3].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[4].String() != "second" {
|
|
t.Errorf("Line(4) = %q, want 'second'", m.ActiveBuffer().Lines[4].String())
|
|
}
|
|
})
|
|
|
|
t.Run("count paste moves cursor to first pasted line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "3", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveWindow().Cursor.Line != 1 {
|
|
t.Errorf("CursorY() = %d, want 1", m.ActiveWindow().Cursor.Line)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Tests for P (paste before)
|
|
|
|
func TestPasteBeforeLinewiseBasic(t *testing.T) {
|
|
t.Run("P pastes single line before cursor line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 3 {
|
|
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "line 1" {
|
|
t.Errorf("Line(0) = %q, want 'line 1'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "inserted" {
|
|
t.Errorf("Line(1) = %q, want 'inserted'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "line 2" {
|
|
t.Errorf("Line(2) = %q, want 'line 2'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
})
|
|
|
|
t.Run("P moves cursor to first pasted line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2", "line 3"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 5}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveWindow().Cursor.Line != 1 {
|
|
t.Errorf("CursorY() = %d, want 1", m.ActiveWindow().Cursor.Line)
|
|
}
|
|
})
|
|
|
|
t.Run("P at first line pastes at very top", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 3 {
|
|
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "inserted" {
|
|
t.Errorf("Line(0) = %q, want 'inserted'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "line 1" {
|
|
t.Errorf("Line(1) = %q, want 'line 1'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveWindow().Cursor.Line != 0 {
|
|
t.Errorf("CursorY() = %d, want 0", m.ActiveWindow().Cursor.Line)
|
|
}
|
|
})
|
|
|
|
t.Run("P from middle of file", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2", "line 3"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 4 {
|
|
t.Errorf("LineCount() = %d, want 4", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "inserted" {
|
|
t.Errorf("Line(1) = %q, want 'inserted'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "line 2" {
|
|
t.Errorf("Line(2) = %q, want 'line 2'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPasteBeforeLinewiseMultipleLines(t *testing.T) {
|
|
t.Run("P pastes multiple lines in correct order", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"first", "second", "third"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 5 {
|
|
t.Errorf("LineCount() = %d, want 5", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "line 1" {
|
|
t.Errorf("Line(0) = %q, want 'line 1'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "first" {
|
|
t.Errorf("Line(1) = %q, want 'first'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "second" {
|
|
t.Errorf("Line(2) = %q, want 'second'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[3].String() != "third" {
|
|
t.Errorf("Line(3) = %q, want 'third'", m.ActiveBuffer().Lines[3].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[4].String() != "line 2" {
|
|
t.Errorf("Line(4) = %q, want 'line 2'", m.ActiveBuffer().Lines[4].String())
|
|
}
|
|
})
|
|
|
|
t.Run("P with multiple lines moves cursor to first pasted line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"first", "second"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveWindow().Cursor.Line != 1 {
|
|
t.Errorf("CursorY() = %d, want 1", m.ActiveWindow().Cursor.Line)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPasteBeforeLinewiseWithCount(t *testing.T) {
|
|
t.Run("2P pastes content twice", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "2", "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 4 {
|
|
t.Errorf("LineCount() = %d, want 4", m.ActiveBuffer().LineCount())
|
|
}
|
|
// Both "inserted" lines should appear before line 2
|
|
if m.ActiveBuffer().Lines[1].String() != "inserted" {
|
|
t.Errorf("Line(1) = %q, want 'inserted'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "inserted" {
|
|
t.Errorf("Line(2) = %q, want 'inserted'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[3].String() != "line 2" {
|
|
t.Errorf("Line(3) = %q, want 'line 2'", m.ActiveBuffer().Lines[3].String())
|
|
}
|
|
})
|
|
|
|
t.Run("3P pastes content three times", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"original"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"pasted"}),
|
|
)
|
|
sendKeys(tm, "3", "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 4 {
|
|
t.Errorf("LineCount() = %d, want 4", m.ActiveBuffer().LineCount())
|
|
}
|
|
for i := 0; i < 3; i++ {
|
|
if m.ActiveBuffer().Lines[i].String() != "pasted" {
|
|
t.Errorf("Line(%d) = %q, want 'pasted'", i, m.ActiveBuffer().Lines[i].String())
|
|
}
|
|
}
|
|
if m.ActiveBuffer().Lines[3].String() != "original" {
|
|
t.Errorf("Line(3) = %q, want 'original'", m.ActiveBuffer().Lines[3].String())
|
|
}
|
|
})
|
|
|
|
t.Run("2P with multiple lines pastes all lines twice in order", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"original"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"first", "second"}),
|
|
)
|
|
sendKeys(tm, "2", "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Should be: first, second, first, second, original
|
|
if m.ActiveBuffer().LineCount() != 5 {
|
|
t.Errorf("LineCount() = %d, want 5", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "first" {
|
|
t.Errorf("Line(0) = %q, want 'first'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "second" {
|
|
t.Errorf("Line(1) = %q, want 'second'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "first" {
|
|
t.Errorf("Line(2) = %q, want 'first'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[3].String() != "second" {
|
|
t.Errorf("Line(3) = %q, want 'second'", m.ActiveBuffer().Lines[3].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[4].String() != "original" {
|
|
t.Errorf("Line(4) = %q, want 'original'", m.ActiveBuffer().Lines[4].String())
|
|
}
|
|
})
|
|
|
|
t.Run("count paste before moves cursor to first pasted line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "3", "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveWindow().Cursor.Line != 1 {
|
|
t.Errorf("CursorY() = %d, want 1", m.ActiveWindow().Cursor.Line)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPasteBeforeLinewiseEdgeCases(t *testing.T) {
|
|
t.Run("P on single line buffer", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"only line"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 2 {
|
|
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "inserted" {
|
|
t.Errorf("Line(0) = %q, want 'inserted'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "only line" {
|
|
t.Errorf("Line(1) = %q, want 'only line'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
})
|
|
|
|
t.Run("P with empty register content does nothing", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 2 {
|
|
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount())
|
|
}
|
|
})
|
|
|
|
t.Run("P preserves indentation in pasted lines", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{" indented", "\ttabbed"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != " indented" {
|
|
t.Errorf("Line(0) = %q, want ' indented'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "\ttabbed" {
|
|
t.Errorf("Line(1) = %q, want '\\ttabbed'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
})
|
|
|
|
t.Run("P with large count", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"original"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"x"}),
|
|
)
|
|
sendKeys(tm, "1", "0", "P") // 10P
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 11 {
|
|
t.Errorf("LineCount() = %d, want 11", m.ActiveBuffer().LineCount())
|
|
}
|
|
// Original should be at the end
|
|
if m.ActiveBuffer().Lines[10].String() != "original" {
|
|
t.Errorf("Line(10) = %q, want 'original'", m.ActiveBuffer().Lines[10].String())
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPasteLinewiseEdgeCases(t *testing.T) {
|
|
t.Run("p on single line buffer", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"only line"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"inserted"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 2 {
|
|
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "only line" {
|
|
t.Errorf("Line(0) = %q, want 'only line'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "inserted" {
|
|
t.Errorf("Line(1) = %q, want 'inserted'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p with empty register content does nothing", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 2 {
|
|
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount())
|
|
}
|
|
})
|
|
|
|
t.Run("p preserves indentation in pasted lines", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{" indented", "\ttabbed"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[1].String() != " indented" {
|
|
t.Errorf("Line(1) = %q, want ' indented'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "\ttabbed" {
|
|
t.Errorf("Line(2) = %q, want '\\ttabbed'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p with large count", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"original"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"x"}),
|
|
)
|
|
sendKeys(tm, "1", "0", "p") // 10p
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 11 {
|
|
t.Errorf("LineCount() = %d, want 11", m.ActiveBuffer().LineCount())
|
|
}
|
|
})
|
|
}
|
|
|
|
// =============================================================================
|
|
// Charwise Paste (p) Tests
|
|
// =============================================================================
|
|
|
|
func TestPasteCharwiseBasic(t *testing.T) {
|
|
t.Run("p pastes text after cursor", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 4}), // on 'o'
|
|
WithRegister('"', core.CharwiseRegister, []string{"XYZ"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Should insert after 'o': "helloXYZ world"
|
|
if m.ActiveBuffer().Lines[0].String() != "helloXYZ world" {
|
|
t.Errorf("Line(0) = %q, want 'helloXYZ world'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p at start of line pastes after first char", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"X"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "hXello" {
|
|
t.Errorf("Line(0) = %q, want 'hXello'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p at end of line pastes at end", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 4}), // on 'o'
|
|
WithRegister('"', core.CharwiseRegister, []string{"!"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "hello!" {
|
|
t.Errorf("Line(0) = %q, want 'hello!'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p on empty line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{""}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"text"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "text" {
|
|
t.Errorf("Line(0) = %q, want 'text'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p does not add new lines", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello", "world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 2}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"X"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 2 {
|
|
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount())
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPasteCharwiseWithCount(t *testing.T) {
|
|
t.Run("2p pastes content twice", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"X"}),
|
|
)
|
|
sendKeys(tm, "2", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "hXXello" {
|
|
t.Errorf("Line(0) = %q, want 'hXXello'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("3p pastes word three times", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"start end"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 4}), // on 't'
|
|
WithRegister('"', core.CharwiseRegister, []string{"-"}),
|
|
)
|
|
sendKeys(tm, "3", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "start--- end" {
|
|
t.Errorf("Line(0) = %q, want 'start--- end'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("count paste with multi-char content", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"ab"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"XY"}),
|
|
)
|
|
sendKeys(tm, "2", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "aXYXYb" {
|
|
t.Errorf("Line(0) = %q, want 'aXYXYb'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPasteCharwiseCursorPosition(t *testing.T) {
|
|
t.Run("p moves cursor to end of pasted text", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"XYZ"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Cursor should be at position after pasted text
|
|
if m.ActiveWindow().Cursor.Col != 3 {
|
|
t.Errorf("CursorX() = %d, want 3", m.ActiveWindow().Cursor.Col)
|
|
}
|
|
})
|
|
|
|
t.Run("p cursor stays on same line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line 1", "line 2"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"X"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveWindow().Cursor.Line != 1 {
|
|
t.Errorf("CursorY() = %d, want 1", m.ActiveWindow().Cursor.Line)
|
|
}
|
|
})
|
|
}
|
|
|
|
// =============================================================================
|
|
// Charwise Paste Before (P) Tests
|
|
// =============================================================================
|
|
|
|
func TestPasteBeforeCharwiseBasic(t *testing.T) {
|
|
t.Run("P pastes text before cursor", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 5}), // on space
|
|
WithRegister('"', core.CharwiseRegister, []string{"XYZ"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Should insert before space: "helloXYZ world"
|
|
if m.ActiveBuffer().Lines[0].String() != "helloXYZ world" {
|
|
t.Errorf("Line(0) = %q, want 'helloXYZ world'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("P at start of line pastes at beginning", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"X"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "Xhello" {
|
|
t.Errorf("Line(0) = %q, want 'Xhello'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("P at end of line pastes before last char", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 4}), // on 'o'
|
|
WithRegister('"', core.CharwiseRegister, []string{"X"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "hellXo" {
|
|
t.Errorf("Line(0) = %q, want 'hellXo'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("P on empty line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{""}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"text"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "text" {
|
|
t.Errorf("Line(0) = %q, want 'text'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPasteBeforeCharwiseWithCount(t *testing.T) {
|
|
t.Run("2P pastes content twice", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 2}), // on first 'l'
|
|
WithRegister('"', core.CharwiseRegister, []string{"X"}),
|
|
)
|
|
sendKeys(tm, "2", "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "heXXllo" {
|
|
t.Errorf("Line(0) = %q, want 'heXXllo'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("3P pastes word three times", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"ab"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 1}), // on 'b'
|
|
WithRegister('"', core.CharwiseRegister, []string{"-"}),
|
|
)
|
|
sendKeys(tm, "3", "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "a---b" {
|
|
t.Errorf("Line(0) = %q, want 'a---b'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
}
|
|
|
|
// =============================================================================
|
|
// Multi-line Charwise Paste Tests (from visual mode yank like vi{)
|
|
// =============================================================================
|
|
|
|
func TestPasteCharwiseMultiLine(t *testing.T) {
|
|
t.Run("p with 2-line charwise content inserts correctly", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 1}), // on 'e'
|
|
WithRegister('"', core.CharwiseRegister, []string{"AAA", "BBB"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Should paste after 'e': "heAAA\nBBBllo"
|
|
if m.ActiveBuffer().LineCount() != 2 {
|
|
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "heAAA" {
|
|
t.Errorf("Line(0) = %q, want 'heAAA'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "BBBllo" {
|
|
t.Errorf("Line(1) = %q, want 'BBBllo'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p with 3-line charwise content inserts correctly", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"test"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 1}), // on 'e'
|
|
WithRegister('"', core.CharwiseRegister, []string{"AAA", "BBB", "CCC"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Should paste: "teAAA\nBBB\nCCCst"
|
|
if m.ActiveBuffer().LineCount() != 3 {
|
|
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "teAAA" {
|
|
t.Errorf("Line(0) = %q, want 'teAAA'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "BBB" {
|
|
t.Errorf("Line(1) = %q, want 'BBB'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "CCCst" {
|
|
t.Errorf("Line(2) = %q, want 'CCCst'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p with multi-line at start of line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}), // on 'h'
|
|
WithRegister('"', core.CharwiseRegister, []string{"X", "Y"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// After 'h': "hX\nYello"
|
|
if m.ActiveBuffer().Lines[0].String() != "hX" {
|
|
t.Errorf("Line(0) = %q, want 'hX'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "Yello" {
|
|
t.Errorf("Line(1) = %q, want 'Yello'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p with multi-line at end of line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 4}), // on 'o'
|
|
WithRegister('"', core.CharwiseRegister, []string{"X", "Y"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// After 'o': "helloX\nY"
|
|
if m.ActiveBuffer().Lines[0].String() != "helloX" {
|
|
t.Errorf("Line(0) = %q, want 'helloX'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "Y" {
|
|
t.Errorf("Line(1) = %q, want 'Y'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p with multi-line on empty line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{""}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"AAA", "BBB"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "AAA" {
|
|
t.Errorf("Line(0) = %q, want 'AAA'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "BBB" {
|
|
t.Errorf("Line(1) = %q, want 'BBB'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
})
|
|
|
|
t.Run("P with multi-line charwise content before cursor", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 2}), // on first 'l'
|
|
WithRegister('"', core.CharwiseRegister, []string{"X", "Y"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Before 'l': "heX\nYllo"
|
|
if m.ActiveBuffer().Lines[0].String() != "heX" {
|
|
t.Errorf("Line(0) = %q, want 'heX'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "Yllo" {
|
|
t.Errorf("Line(1) = %q, want 'Yllo'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
})
|
|
|
|
t.Run("P with multi-line at start of line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"X", "Y"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Before 'h': "X\nYhello"
|
|
if m.ActiveBuffer().Lines[0].String() != "X" {
|
|
t.Errorf("Line(0) = %q, want 'X'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "Yhello" {
|
|
t.Errorf("Line(1) = %q, want 'Yhello'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p with multi-line and count", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"test"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 1}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"A", "B"}),
|
|
)
|
|
sendKeys(tm, "2", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// 2p should paste twice: "teA\nBA\nBst"
|
|
if m.ActiveBuffer().LineCount() != 3 {
|
|
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "teA" {
|
|
t.Errorf("Line(0) = %q, want 'teA'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "BA" {
|
|
t.Errorf("Line(1) = %q, want 'BA'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "Bst" {
|
|
t.Errorf("Line(2) = %q, want 'Bst'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
})
|
|
|
|
t.Run("real world: vi{ then y then p", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{
|
|
"function() {",
|
|
" body",
|
|
"}",
|
|
"test",
|
|
}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
)
|
|
// Yank the content inside braces
|
|
sendKeys(tm, "v", "i", "{", "y")
|
|
// Move to test line and paste
|
|
sendKeys(tm, "j", "j", "$", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// The yanked content should be multi-line charwise
|
|
reg, ok := m.GetRegister('"')
|
|
if !ok {
|
|
t.Fatal("register not found")
|
|
}
|
|
if reg.Type != core.CharwiseRegister {
|
|
t.Errorf("register type = %v, want CharwiseRegister", reg.Type)
|
|
}
|
|
// Should paste after 't' in "test"
|
|
// Depending on what vi{ yanks, this verifies multi-line paste works
|
|
if m.ActiveBuffer().LineCount() < 4 {
|
|
t.Errorf("LineCount() = %d, want at least 4", m.ActiveBuffer().LineCount())
|
|
}
|
|
})
|
|
}
|
|
|
|
// =============================================================================
|
|
// Blockwise Paste Tests
|
|
// =============================================================================
|
|
|
|
func TestPasteBlockwiseBasic(t *testing.T) {
|
|
t.Run("p with blockwise content errors gracefully", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"aaaa", "bbbb", "cccc"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.BlockwiseRegister, []string{"XX", "YY", "ZZ"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Current implementation errors - lines should be unchanged
|
|
if m.ActiveBuffer().Lines[0].String() != "aaaa" {
|
|
t.Errorf("Line(0) = %q, want 'aaaa' (unchanged due to error)", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("P with blockwise content errors gracefully", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"aaaa", "bbbb", "cccc"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.BlockwiseRegister, []string{"XX", "YY", "ZZ"}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Current implementation errors - lines should be unchanged
|
|
if m.ActiveBuffer().Lines[0].String() != "aaaa" {
|
|
t.Errorf("Line(0) = %q, want 'aaaa' (unchanged due to error)", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
}
|
|
|
|
// =============================================================================
|
|
// Charwise Paste Edge Cases
|
|
// =============================================================================
|
|
|
|
func TestPasteCharwiseEdgeCases(t *testing.T) {
|
|
t.Run("p with empty charwise register does nothing", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "hello" {
|
|
t.Errorf("Line(0) = %q, want 'hello'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p with empty string in register", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{""}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "hello" {
|
|
t.Errorf("Line(0) = %q, want 'hello'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p preserves special characters", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"ab"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"\t"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "a\tb" {
|
|
t.Errorf("Line(0) = %q, want 'a\\tb'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p with spaces", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"ab"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{" "}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "a b" {
|
|
t.Errorf("Line(0) = %q, want 'a b'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("p on line with only whitespace", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{" "}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 2}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"X"}),
|
|
)
|
|
sendKeys(tm, "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != " X " {
|
|
t.Errorf("Line(0) = %q, want ' X '", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("P with empty charwise register does nothing", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{}),
|
|
)
|
|
sendKeys(tm, "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "hello" {
|
|
t.Errorf("Line(0) = %q, want 'hello'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("large count paste", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"ab"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"X"}),
|
|
)
|
|
sendKeys(tm, "1", "0", "p") // 10p
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "aXXXXXXXXXXb" {
|
|
t.Errorf("Line(0) = %q, want 'aXXXXXXXXXXb'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
}
|
|
|
|
// =============================================================================
|
|
// Integration: Yank then Paste
|
|
// =============================================================================
|
|
|
|
func TestYankThenPasteCharwise(t *testing.T) {
|
|
t.Run("yw then p pastes yanked word", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
)
|
|
sendKeys(tm, "y", "w") // yank "hello "
|
|
sendKeys(tm, "$") // go to end
|
|
sendKeys(tm, "p") // paste
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "hello worldhello " {
|
|
t.Errorf("Line(0) = %q, want 'hello worldhello '", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("ye then p pastes yanked word", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
)
|
|
sendKeys(tm, "y", "e") // yank "hello"
|
|
sendKeys(tm, "$") // go to end
|
|
sendKeys(tm, "p") // paste
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "hello worldhello" {
|
|
t.Errorf("Line(0) = %q, want 'hello worldhello'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("visual select then y then p", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
)
|
|
sendKeys(tm, "v", "l", "l", "y") // select and yank "hel"
|
|
sendKeys(tm, "$") // go to end
|
|
sendKeys(tm, "p") // paste
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "hello worldhel" {
|
|
t.Errorf("Line(0) = %q, want 'hello worldhel'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
}
|
|
|
|
// =============================================================================
|
|
// Visual Mode Paste Tests
|
|
// =============================================================================
|
|
// In visual mode, 'p' should replace the selected text with register content.
|
|
// This is different from normal mode where 'p' inserts after cursor.
|
|
|
|
func TestVisualModePasteCharwise(t *testing.T) {
|
|
// -------------------------------------------------------------------------
|
|
// Basic Visual Mode Paste with Charwise Register
|
|
// -------------------------------------------------------------------------
|
|
|
|
t.Run("vp replaces selection with charwise register", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"REPLACED"}),
|
|
)
|
|
sendKeys(tm, "v", "l", "l", "l", "l", "p") // select "hello", paste
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "REPLACED world" {
|
|
t.Errorf("Line(0) = %q, want 'REPLACED world'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("vp replaces single character", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"X"}),
|
|
)
|
|
sendKeys(tm, "v", "p") // select "h", paste
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "Xello world" {
|
|
t.Errorf("Line(0) = %q, want 'Xello world'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("vp replaces word in middle of line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world goodbye"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 6}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"EARTH"}),
|
|
)
|
|
sendKeys(tm, "v", "e", "p") // select "world", paste
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "hello EARTH goodbye" {
|
|
t.Errorf("Line(0) = %q, want 'hello EARTH goodbye'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("vp replaces to end of line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 6}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"universe"}),
|
|
)
|
|
sendKeys(tm, "v", "$", "p") // select "world", paste
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "hello universe" {
|
|
t.Errorf("Line(0) = %q, want 'hello universe'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("vp with empty register deletes selection", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{""}),
|
|
)
|
|
sendKeys(tm, "v", "l", "l", "l", "l", "p") // select "hello", paste empty
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != " world" {
|
|
t.Errorf("Line(0) = %q, want ' world'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Visual Mode Paste Spanning Multiple Lines
|
|
// -------------------------------------------------------------------------
|
|
|
|
t.Run("vp replaces selection spanning multiple lines", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line one", "line two", "line three"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 5}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"REPLACED"}),
|
|
)
|
|
// v at (0,5), j goes to (1,5), h goes to (1,4) = space after "line"
|
|
// Selection: "one\nline " (from 'o' to space)
|
|
sendKeys(tm, "v", "j", "h", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Should join: "line " + "REPLACED" + "two"
|
|
if m.ActiveBuffer().Lines[0].String() != "line REPLACEDtwo" {
|
|
t.Errorf("Line(0) = %q, want 'line REPLACEDtwo'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().LineCount() != 2 {
|
|
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount())
|
|
}
|
|
})
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Cursor Position After Visual Paste
|
|
// -------------------------------------------------------------------------
|
|
|
|
t.Run("vp cursor at start of pasted content", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 6}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"EARTH"}),
|
|
)
|
|
sendKeys(tm, "v", "e", "p") // select "world", paste "EARTH"
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Cursor should be at start of pasted content (or end, depending on impl)
|
|
// In Vim, cursor goes to last char of pasted text
|
|
if m.ActiveWindow().Cursor.Col != 10 {
|
|
t.Errorf("CursorX() = %d, want 10", m.ActiveWindow().Cursor.Col)
|
|
}
|
|
})
|
|
|
|
t.Run("vp exits visual mode", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"X"}),
|
|
)
|
|
sendKeys(tm, "v", "l", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.Mode() != core.NormalMode {
|
|
t.Errorf("Mode() = %v, want NormalMode", m.Mode())
|
|
}
|
|
})
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Visual Mode Paste with Linewise Register
|
|
// -------------------------------------------------------------------------
|
|
|
|
t.Run("vp with linewise register replaces selection", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"NEW LINE"}),
|
|
)
|
|
sendKeys(tm, "v", "l", "l", "l", "l", "p") // select "hello", paste linewise content
|
|
|
|
m := getFinalModel(t, tm)
|
|
// Linewise register content is pasted inline replacing the selection
|
|
if m.ActiveBuffer().Lines[0].String() != "NEW LINE world" {
|
|
t.Errorf("Line(0) = %q, want 'NEW LINE world'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestVisualLinePaste(t *testing.T) {
|
|
// -------------------------------------------------------------------------
|
|
// Visual Line Mode Paste
|
|
// -------------------------------------------------------------------------
|
|
|
|
t.Run("Vp replaces line with charwise register", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line one", "line two", "line three"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"REPLACED"}),
|
|
)
|
|
sendKeys(tm, "V", "p") // select line two, paste
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 3 {
|
|
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "REPLACED" {
|
|
t.Errorf("Line(1) = %q, want 'REPLACED'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
})
|
|
|
|
t.Run("Vp replaces line with linewise register", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line one", "line two", "line three"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"NEW LINE"}),
|
|
)
|
|
sendKeys(tm, "V", "p") // select line two, paste
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 3 {
|
|
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "NEW LINE" {
|
|
t.Errorf("Line(1) = %q, want 'NEW LINE'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
})
|
|
|
|
t.Run("Vp replaces multiple lines with linewise register", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line one", "line two", "line three", "line four"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"REPLACED"}),
|
|
)
|
|
sendKeys(tm, "V", "j", "p") // select lines two and three, paste
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 3 {
|
|
t.Errorf("LineCount() = %d, want 3", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "line one" {
|
|
t.Errorf("Line(0) = %q, want 'line one'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "REPLACED" {
|
|
t.Errorf("Line(1) = %q, want 'REPLACED'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "line four" {
|
|
t.Errorf("Line(2) = %q, want 'line four'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
})
|
|
|
|
t.Run("Vp replaces multiple lines with multiple linewise register lines", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line one", "line two", "line three", "line four"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"NEW A", "NEW B"}),
|
|
)
|
|
sendKeys(tm, "V", "j", "p") // select lines two and three, paste two lines
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 4 {
|
|
t.Errorf("LineCount() = %d, want 4", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "line one" {
|
|
t.Errorf("Line(0) = %q, want 'line one'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "NEW A" {
|
|
t.Errorf("Line(1) = %q, want 'NEW A'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[2].String() != "NEW B" {
|
|
t.Errorf("Line(2) = %q, want 'NEW B'", m.ActiveBuffer().Lines[2].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[3].String() != "line four" {
|
|
t.Errorf("Line(3) = %q, want 'line four'", m.ActiveBuffer().Lines[3].String())
|
|
}
|
|
})
|
|
|
|
t.Run("Vp on first line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line one", "line two"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"FIRST"}),
|
|
)
|
|
sendKeys(tm, "V", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 2 {
|
|
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "FIRST" {
|
|
t.Errorf("Line(0) = %q, want 'FIRST'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "line two" {
|
|
t.Errorf("Line(1) = %q, want 'line two'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
})
|
|
|
|
t.Run("Vp on last line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line one", "line two"}),
|
|
WithCursorPos(core.Position{Line: 1, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"LAST"}),
|
|
)
|
|
sendKeys(tm, "V", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 2 {
|
|
t.Errorf("LineCount() = %d, want 2", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "line one" {
|
|
t.Errorf("Line(0) = %q, want 'line one'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
if m.ActiveBuffer().Lines[1].String() != "LAST" {
|
|
t.Errorf("Line(1) = %q, want 'LAST'", m.ActiveBuffer().Lines[1].String())
|
|
}
|
|
})
|
|
|
|
t.Run("Vp exits visual line mode", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line one", "line two"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"REPLACED"}),
|
|
)
|
|
sendKeys(tm, "V", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.Mode() != core.NormalMode {
|
|
t.Errorf("Mode() = %v, want NormalMode", m.Mode())
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestVisualPasteRegisterBehavior(t *testing.T) {
|
|
// -------------------------------------------------------------------------
|
|
// Register Behavior - deleted text should go to register
|
|
// -------------------------------------------------------------------------
|
|
|
|
t.Run("vp puts deleted text into register", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"NEW"}),
|
|
)
|
|
sendKeys(tm, "v", "l", "l", "l", "l", "p") // select "hello", paste "NEW"
|
|
|
|
m := getFinalModel(t, tm)
|
|
// After visual paste, the deleted "hello" should be in the register
|
|
reg, ok := m.GetRegister('"')
|
|
if !ok {
|
|
t.Fatal("unnamed register not found")
|
|
}
|
|
if len(reg.Content) != 1 || reg.Content[0] != "hello" {
|
|
t.Errorf("register content = %q, want 'hello'", reg.Content)
|
|
}
|
|
})
|
|
|
|
t.Run("Vp puts deleted line into register", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line one", "line two"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"REPLACED"}),
|
|
)
|
|
sendKeys(tm, "V", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
reg, ok := m.GetRegister('"')
|
|
if !ok {
|
|
t.Fatal("unnamed register not found")
|
|
}
|
|
if len(reg.Content) != 1 || reg.Content[0] != "line one" {
|
|
t.Errorf("register content = %q, want 'line one'", reg.Content)
|
|
}
|
|
if reg.Type != core.LinewiseRegister {
|
|
t.Errorf("register type = %v, want LinewiseRegister", reg.Type)
|
|
}
|
|
})
|
|
|
|
t.Run("vP does not put deleted text into register", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"NEW"}),
|
|
)
|
|
sendKeys(tm, "v", "l", "l", "l", "l", "P") // select "hello", paste "NEW"
|
|
|
|
m := getFinalModel(t, tm)
|
|
// After visual paste, the deleted "hello" should be in the register
|
|
reg, ok := m.GetRegister('"')
|
|
if !ok {
|
|
t.Fatal("unnamed register not found")
|
|
}
|
|
if len(reg.Content) != 1 || reg.Content[0] != "NEW" {
|
|
t.Errorf("register content = %q, want 'NEW'", reg.Content)
|
|
}
|
|
})
|
|
|
|
t.Run("VP does not put deleted line into register", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"line one", "line two"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"REPLACED"}),
|
|
)
|
|
sendKeys(tm, "V", "P")
|
|
|
|
m := getFinalModel(t, tm)
|
|
reg, ok := m.GetRegister('"')
|
|
if !ok {
|
|
t.Fatal("unnamed register not found")
|
|
}
|
|
if len(reg.Content) != 1 || reg.Content[0] != "REPLACED" {
|
|
t.Errorf("register content = %q, want 'REPLACED'", reg.Content)
|
|
}
|
|
if reg.Type != core.LinewiseRegister {
|
|
t.Errorf("register type = %v, want LinewiseRegister", reg.Type)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestVisualPasteEdgeCases(t *testing.T) {
|
|
// -------------------------------------------------------------------------
|
|
// Edge Cases
|
|
// -------------------------------------------------------------------------
|
|
|
|
t.Run("vp on empty line", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"", "line two"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"NEW"}),
|
|
)
|
|
sendKeys(tm, "v", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "NEW" {
|
|
t.Errorf("Line(0) = %q, want 'NEW'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("vp selecting entire line content", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello", "world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"REPLACED"}),
|
|
)
|
|
sendKeys(tm, "v", "$", "p") // select entire "hello"
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "REPLACED" {
|
|
t.Errorf("Line(0) = %q, want 'REPLACED'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("vp with backwards selection", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"hello world"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 5}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"X"}),
|
|
)
|
|
sendKeys(tm, "v", "h", "h", "h", "h", "h", "p") // select backwards "hello ", paste
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "Xworld" {
|
|
t.Errorf("Line(0) = %q, want 'Xworld'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("vp in single character file", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"a"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.CharwiseRegister, []string{"XYZ"}),
|
|
)
|
|
sendKeys(tm, "v", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().Lines[0].String() != "XYZ" {
|
|
t.Errorf("Line(0) = %q, want 'XYZ'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
|
|
t.Run("Vp on single line file", func(t *testing.T) {
|
|
tm := newTestModel(t,
|
|
WithLines([]string{"only line"}),
|
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
|
WithRegister('"', core.LinewiseRegister, []string{"REPLACED"}),
|
|
)
|
|
sendKeys(tm, "V", "p")
|
|
|
|
m := getFinalModel(t, tm)
|
|
if m.ActiveBuffer().LineCount() != 1 {
|
|
t.Errorf("LineCount() = %d, want 1", m.ActiveBuffer().LineCount())
|
|
}
|
|
if m.ActiveBuffer().Lines[0].String() != "REPLACED" {
|
|
t.Errorf("Line(0) = %q, want 'REPLACED'", m.ActiveBuffer().Lines[0].String())
|
|
}
|
|
})
|
|
}
|