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
|
package editor
import (
"testing"
tea "github.com/charmbracelet/bubbletea"
)
func altUp(e *Editor) { e.HandleKey(tea.KeyMsg{Type: tea.KeyUp, Alt: true}) }
func altDown(e *Editor) { e.HandleKey(tea.KeyMsg{Type: tea.KeyDown, Alt: true}) }
func TestMoveLineUpSwapsWithLineAbove(t *testing.T) {
e := New()
e.SetContent([]byte("a\nb\nc"))
e.Cursor = Position{Row: 1, Col: 1}
if !e.MoveLineUp() {
t.Fatal("MoveLineUp returned false")
}
if e.Lines[0] != "b" || e.Lines[1] != "a" {
t.Fatalf("lines = %q, want [b a c]", e.Lines)
}
if e.Cursor.Row != 0 || e.Cursor.Col != 1 {
t.Fatalf("cursor = %+v, want row 0 col 1 (follows the moved line)", e.Cursor)
}
}
func TestMoveLineDownSwapsWithLineBelow(t *testing.T) {
e := New()
e.SetContent([]byte("a\nb\nc"))
e.Cursor = Position{Row: 1, Col: 1}
if !e.MoveLineDown() {
t.Fatal("MoveLineDown returned false")
}
if e.Lines[1] != "c" || e.Lines[2] != "b" {
t.Fatalf("lines = %q, want [a c b]", e.Lines)
}
if e.Cursor.Row != 2 || e.Cursor.Col != 1 {
t.Fatalf("cursor = %+v, want row 2 col 1", e.Cursor)
}
}
func TestMoveLineUpNoOpAtTop(t *testing.T) {
e := New()
e.SetContent([]byte("a\nb"))
e.Cursor = Position{Row: 0, Col: 0}
if e.MoveLineUp() {
t.Fatal("MoveLineUp at top should be a no-op")
}
if e.Lines[0] != "a" || e.Lines[1] != "b" {
t.Fatalf("lines = %q, want unchanged", e.Lines)
}
}
func TestMoveLineDownNoOpAtBottom(t *testing.T) {
e := New()
e.SetContent([]byte("a\nb"))
e.Cursor = Position{Row: 1, Col: 0}
if e.MoveLineDown() {
t.Fatal("MoveLineDown at bottom should be a no-op")
}
if e.Lines[0] != "a" || e.Lines[1] != "b" {
t.Fatalf("lines = %q, want unchanged", e.Lines)
}
}
// Repeated presses walk a line several positions in one direction.
func TestMoveLineUpRepeatsWalkUp(t *testing.T) {
e := New()
e.SetContent([]byte("a\nb\nc\nd"))
e.Cursor = Position{Row: 3, Col: 0} // "d"
altUp(e)
altUp(e)
altUp(e)
if e.Lines[0] != "d" {
t.Fatalf("lines = %q, want d walked to top", e.Lines)
}
if e.Cursor.Row != 0 {
t.Fatalf("cursor row = %d, want 0", e.Cursor.Row)
}
}
// Each move is one undo step that reverts exactly one swap.
func TestMoveLineDownIsOneUndoStep(t *testing.T) {
e := New()
e.SetContent([]byte("a\nb\nc"))
e.Cursor = Position{Row: 0, Col: 0}
altDown(e)
if e.Lines[0] != "b" || e.Lines[1] != "a" {
t.Fatalf("after move lines = %q, want [b a c]", e.Lines)
}
e.Undo()
if e.Lines[0] != "a" || e.Lines[1] != "b" || e.Lines[2] != "c" {
t.Fatalf("after undo lines = %q, want [a b c]", e.Lines)
}
}
// A move past the document edge records no undo checkpoint (no-op key).
func TestMoveLineUpAtTopRecordsNoUndo(t *testing.T) {
e := New()
e.SetContent([]byte("a\nb"))
e.Cursor = Position{Row: 0, Col: 0}
altUp(e)
if len(e.undo) != 0 {
t.Fatalf("undo stack = %d, want 0 (no-op move records nothing)", len(e.undo))
}
}
|