test: more tests and renamed update default reg
This commit is contained in:
parent
0f0de17dff
commit
1aed168369
@ -93,7 +93,7 @@ type Model interface {
|
||||
Registers() map[rune]Register
|
||||
GetRegister(name rune) (Register, bool)
|
||||
SetRegister(name rune, t RegisterType, cnt []string) error
|
||||
UpdateDefault(t RegisterType, cnt []string)
|
||||
UpdateDefaultRegister(t RegisterType, cnt []string)
|
||||
|
||||
// Mode
|
||||
Mode() Mode
|
||||
|
||||
@ -6,6 +6,8 @@ import (
|
||||
"git.gophernest.net/azpect/TextEditor/internal/action"
|
||||
)
|
||||
|
||||
// NOTE: Lots of AI tests here
|
||||
|
||||
// --- Visual Mode Selection State Tests ---
|
||||
|
||||
func TestVisualModeSelectionState(t *testing.T) {
|
||||
@ -270,3 +272,316 @@ func TestVisualModeDelete(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// --- Visual Mode with Word Motions ---
|
||||
|
||||
func TestVisualModeWordMotions(t *testing.T) {
|
||||
t.Run("test 'vw' selects to next word", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"hello world"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 0}),
|
||||
)
|
||||
sendKeys(tm, "v", "w")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.AnchorX() != 0 {
|
||||
t.Errorf("AnchorX() = %d, want 0", m.AnchorX())
|
||||
}
|
||||
// w moves to start of "world" at col 6
|
||||
if m.CursorX() != 6 {
|
||||
t.Errorf("CursorX() = %d, want 6", m.CursorX())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'vwd' deletes word plus space", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"hello world"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 0}),
|
||||
)
|
||||
sendKeys(tm, "v", "w", "d")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
// Deletes from 0 to 6 inclusive = "hello w", leaves "orld"
|
||||
if m.Line(0) != "orld" {
|
||||
t.Errorf("Line(0) = %q, want 'orld'", m.Line(0))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 've' selects to end of word", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"hello world"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 0}),
|
||||
)
|
||||
sendKeys(tm, "v", "e")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.AnchorX() != 0 {
|
||||
t.Errorf("AnchorX() = %d, want 0", m.AnchorX())
|
||||
}
|
||||
// e moves to end of "hello" at col 4
|
||||
if m.CursorX() != 4 {
|
||||
t.Errorf("CursorX() = %d, want 4", m.CursorX())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'ved' deletes word", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"hello world"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 0}),
|
||||
)
|
||||
sendKeys(tm, "v", "e", "d")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
// Deletes "hello"
|
||||
if m.Line(0) != " world" {
|
||||
t.Errorf("Line(0) = %q, want ' world'", m.Line(0))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'vb' selects backward word", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"hello world"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 6}), // on 'w'
|
||||
)
|
||||
sendKeys(tm, "v", "b")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.AnchorX() != 6 {
|
||||
t.Errorf("AnchorX() = %d, want 6", m.AnchorX())
|
||||
}
|
||||
// b moves to start of "hello" at col 0
|
||||
if m.CursorX() != 0 {
|
||||
t.Errorf("CursorX() = %d, want 0", m.CursorX())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'vbd' deletes backward to word start", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"hello world"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 6}), // on 'w'
|
||||
)
|
||||
sendKeys(tm, "v", "b", "d")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
// Deletes from "h" (0) to "w" (6) inclusive
|
||||
if m.Line(0) != "orld" {
|
||||
t.Errorf("Line(0) = %q, want 'orld'", m.Line(0))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'v2w' selects two words", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"one two three"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 0}),
|
||||
)
|
||||
sendKeys(tm, "v", "2", "w")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
// 2w moves past "one " and "two " to start of "three" at col 8
|
||||
if m.CursorX() != 8 {
|
||||
t.Errorf("CursorX() = %d, want 8", m.CursorX())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// --- Visual Mode with Jump Motions ---
|
||||
|
||||
func TestVisualModeJumpMotions(t *testing.T) {
|
||||
t.Run("test 'v$' selects to end of line", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"hello world"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 0}),
|
||||
)
|
||||
sendKeys(tm, "v", "$")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.AnchorX() != 0 {
|
||||
t.Errorf("AnchorX() = %d, want 0", m.AnchorX())
|
||||
}
|
||||
// $ moves past end of line
|
||||
if m.CursorX() != 11 {
|
||||
t.Errorf("CursorX() = %d, want 11", m.CursorX())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'v$d' deletes to end of line", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"hello world"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 6}), // on 'w'
|
||||
)
|
||||
sendKeys(tm, "v", "$", "d")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.Line(0) != "hello " {
|
||||
t.Errorf("Line(0) = %q, want 'hello '", m.Line(0))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'v0' selects to beginning of line", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"hello world"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 6}),
|
||||
)
|
||||
sendKeys(tm, "v", "0")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.AnchorX() != 6 {
|
||||
t.Errorf("AnchorX() = %d, want 6", m.AnchorX())
|
||||
}
|
||||
if m.CursorX() != 0 {
|
||||
t.Errorf("CursorX() = %d, want 0", m.CursorX())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'v0d' deletes to beginning of line", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"hello world"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 6}), // on 'w'
|
||||
)
|
||||
sendKeys(tm, "v", "0", "d")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
// Deletes from 'h' (0) to 'w' (6) inclusive
|
||||
if m.Line(0) != "orld" {
|
||||
t.Errorf("Line(0) = %q, want 'orld'", m.Line(0))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'v_' selects to first non-whitespace", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{" hello world"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 10}), // on 'w'
|
||||
)
|
||||
sendKeys(tm, "v", "_")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.AnchorX() != 10 {
|
||||
t.Errorf("AnchorX() = %d, want 10", m.AnchorX())
|
||||
}
|
||||
// _ moves to first non-ws at col 4
|
||||
if m.CursorX() != 4 {
|
||||
t.Errorf("CursorX() = %d, want 4", m.CursorX())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'vG' selects to bottom of file", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"line 1", "line 2", "line 3"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 0}),
|
||||
)
|
||||
sendKeys(tm, "v", "G")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.AnchorY() != 0 {
|
||||
t.Errorf("AnchorY() = %d, want 0", m.AnchorY())
|
||||
}
|
||||
if m.CursorY() != 2 {
|
||||
t.Errorf("CursorY() = %d, want 2", m.CursorY())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'vGd' deletes to bottom of file", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"line 1", "line 2", "line 3"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 3}), // on 'e' of "line"
|
||||
)
|
||||
sendKeys(tm, "v", "G", "d")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
// G goes to last line at same col, deletes from (0,3) to (2,3)
|
||||
// Keeps "lin" from first line + "e 3" from last line = "lin 3"
|
||||
if m.LineCount() != 1 {
|
||||
t.Errorf("LineCount() = %d, want 1", m.LineCount())
|
||||
}
|
||||
if m.Line(0) != "lin 3" {
|
||||
t.Errorf("Line(0) = %q, want 'lin 3'", m.Line(0))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'vgg' selects to top of file", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"line 1", "line 2", "line 3"}),
|
||||
WithCursorPos(action.Position{Line: 2, Col: 0}),
|
||||
)
|
||||
sendKeys(tm, "v", "g", "g")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.AnchorY() != 2 {
|
||||
t.Errorf("AnchorY() = %d, want 2", m.AnchorY())
|
||||
}
|
||||
if m.CursorY() != 0 {
|
||||
t.Errorf("CursorY() = %d, want 0", m.CursorY())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'vggd' deletes to top of file", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"line 1", "line 2", "line 3"}),
|
||||
WithCursorPos(action.Position{Line: 2, Col: 3}),
|
||||
)
|
||||
sendKeys(tm, "v", "g", "g", "d")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
// gg goes to first line at same col, deletes selection
|
||||
// Keeps "lin" from first line + " 3" from last line = "lin 3"
|
||||
if m.LineCount() != 1 {
|
||||
t.Errorf("LineCount() = %d, want 1", m.LineCount())
|
||||
}
|
||||
if m.Line(0) != "lin 3" {
|
||||
t.Errorf("Line(0) = %q, want 'lin 3'", m.Line(0))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// --- Visual Line Mode with Jump Motions ---
|
||||
|
||||
func TestVisualLineModeJumpMotions(t *testing.T) {
|
||||
t.Run("test 'VG' selects all lines to bottom", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"line 1", "line 2", "line 3"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 0}),
|
||||
)
|
||||
sendKeys(tm, "V", "G")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.AnchorY() != 0 {
|
||||
t.Errorf("AnchorY() = %d, want 0", m.AnchorY())
|
||||
}
|
||||
if m.CursorY() != 2 {
|
||||
t.Errorf("CursorY() = %d, want 2", m.CursorY())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'VGd' deletes all lines", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"line 1", "line 2", "line 3"}),
|
||||
WithCursorPos(action.Position{Line: 0, Col: 0}),
|
||||
)
|
||||
sendKeys(tm, "V", "G", "d")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
// All lines deleted, should have empty buffer
|
||||
if m.LineCount() != 1 {
|
||||
t.Errorf("LineCount() = %d, want 1", m.LineCount())
|
||||
}
|
||||
if m.Line(0) != "" {
|
||||
t.Errorf("Line(0) = %q, want ''", m.Line(0))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test 'Vgg' selects lines to top", func(t *testing.T) {
|
||||
tm := newTestModel(t,
|
||||
WithLines([]string{"line 1", "line 2", "line 3"}),
|
||||
WithCursorPos(action.Position{Line: 2, Col: 0}),
|
||||
)
|
||||
sendKeys(tm, "V", "g", "g")
|
||||
|
||||
m := getFinalModel(t, tm)
|
||||
if m.AnchorY() != 2 {
|
||||
t.Errorf("AnchorY() = %d, want 2", m.AnchorY())
|
||||
}
|
||||
if m.CursorY() != 0 {
|
||||
t.Errorf("CursorY() = %d, want 0", m.CursorY())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -213,8 +213,7 @@ func (m *Model) SetRegister(name rune, t action.RegisterType, cnt []string) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Errors?
|
||||
func (m *Model) UpdateDefault(t action.RegisterType, cnt []string) {
|
||||
func (m *Model) UpdateDefaultRegister(t action.RegisterType, cnt []string) {
|
||||
// Shift numbered registers: 0 -> 1 -> 2 -> ... -> 9 -> _ (discarded)
|
||||
for i := rune('9'); i > '0'; i-- {
|
||||
m.registers[i] = m.registers[i-1]
|
||||
|
||||
@ -51,7 +51,7 @@ func (o DeleteOperator) DoublePress(m action.Model, count int) tea.Cmd {
|
||||
}
|
||||
|
||||
// Put her in the register!
|
||||
m.UpdateDefault(action.LinewiseRegister, lines)
|
||||
m.UpdateDefaultRegister(action.LinewiseRegister, lines)
|
||||
// m.SetRegister('"', action.LinewiseRegister, lines)
|
||||
|
||||
return nil
|
||||
@ -137,7 +137,7 @@ func deleteLineSelection(m action.Model, start, end action.Position) {
|
||||
m.ClampCursorX()
|
||||
|
||||
// Update registers
|
||||
m.UpdateDefault(action.LinewiseRegister, lines)
|
||||
m.UpdateDefaultRegister(action.LinewiseRegister, lines)
|
||||
}
|
||||
|
||||
func deleteBlockSelection(m action.Model, start, end action.Position) {
|
||||
|
||||
@ -45,7 +45,7 @@ func (o YankOperator) DoublePress(m action.Model, count int) tea.Cmd {
|
||||
}
|
||||
|
||||
// Put her in the register!
|
||||
m.UpdateDefault(action.LinewiseRegister, lines)
|
||||
m.UpdateDefaultRegister(action.LinewiseRegister, lines)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -72,7 +72,7 @@ func yankNormalMode(m action.Model, start, end action.Position, mtype action.Mot
|
||||
endX = min(endX, len(line)) // Catch overflow
|
||||
|
||||
cnt := line[startX:endX]
|
||||
m.UpdateDefault(action.CharwiseRegister, []string{cnt})
|
||||
m.UpdateDefaultRegister(action.CharwiseRegister, []string{cnt})
|
||||
|
||||
case mtype == action.Linewise:
|
||||
// This shouldn't happen
|
||||
@ -86,7 +86,7 @@ func yankNormalMode(m action.Model, start, end action.Position, mtype action.Mot
|
||||
endY := max(start.Line, end.Line)
|
||||
|
||||
cnt := m.Lines()[startY : endY+1]
|
||||
m.UpdateDefault(action.LinewiseRegister, cnt)
|
||||
m.UpdateDefaultRegister(action.LinewiseRegister, cnt)
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ func yankVisualMode(m action.Model, start, end action.Position) {
|
||||
endCol := min(end.Col+1, len(line)) // +1 because visual selection is inclusive
|
||||
startCol := min(start.Col, len(line))
|
||||
cnt := line[startCol:endCol]
|
||||
m.UpdateDefault(action.CharwiseRegister, []string{cnt})
|
||||
m.UpdateDefaultRegister(action.CharwiseRegister, []string{cnt})
|
||||
return
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ func yankVisualMode(m action.Model, start, end action.Position) {
|
||||
endCol := min(end.Col+1, len(lastLine))
|
||||
content = append(content, lastLine[:endCol])
|
||||
|
||||
m.UpdateDefault(action.CharwiseRegister, content)
|
||||
m.UpdateDefaultRegister(action.CharwiseRegister, content)
|
||||
}
|
||||
|
||||
func yankVisualLineMode(m action.Model, start, end action.Position) {
|
||||
@ -139,7 +139,7 @@ func yankVisualLineMode(m action.Model, start, end action.Position) {
|
||||
endY := max(start.Line, end.Line)
|
||||
|
||||
cnt := m.Lines()[startY : endY+1]
|
||||
m.UpdateDefault(action.LinewiseRegister, cnt)
|
||||
m.UpdateDefaultRegister(action.LinewiseRegister, cnt)
|
||||
|
||||
}
|
||||
|
||||
@ -165,5 +165,5 @@ func yankVisualBlockMode(m action.Model, start, end action.Position) {
|
||||
content = append(content, line[startX:lineEndX])
|
||||
}
|
||||
|
||||
m.UpdateDefault(action.BlockwiseRegister, content)
|
||||
m.UpdateDefaultRegister(action.BlockwiseRegister, content)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user