turbo-editors/turbo-corepublic Fork 0
28d59854361aeda8541d853093e732126f3d7bff
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

cursor_test.go · 266 lines · 6.7 KBGo Blame HistoryRaw
🛟 Updated. 28d5985 k33g 21h ago1package buffer
2
3import "testing"
4
5func TestSetCursorClampsIntoTheBuffer(t *testing.T) {
6 b := NewFromString("ab\ncdef")
7
8 tests := []struct {
9 name string
10 give Position
11 want Position
12 }{
13 {"past the end of a line", Position{Line: 0, Col: 99}, Position{Line: 0, Col: 2}},
14 {"past the last line", Position{Line: 99, Col: 0}, Position{Line: 1, Col: 4}},
15 {"negative line", Position{Line: -1, Col: 3}, Position{Line: 0, Col: 0}},
16 {"negative column", Position{Line: 1, Col: -3}, Position{Line: 1, Col: 0}},
17 {"already valid", Position{Line: 1, Col: 2}, Position{Line: 1, Col: 2}},
18 }
19
20 for _, tc := range tests {
21 t.Run(tc.name, func(t *testing.T) {
22 b.SetCursor(tc.give)
23 if got := b.Cursor(); got != tc.want {
24 t.Errorf("SetCursor(%+v) left the cursor at %+v, want %+v", tc.give, got, tc.want)
25 }
26 })
27 }
28}
29
30func TestMoveLeftWrapsOntoThePreviousLine(t *testing.T) {
31 b := NewFromString("ab\ncd")
32 b.SetCursor(Position{Line: 1, Col: 0})
33
34 b.MoveLeft()
35
36 if got := b.Cursor(); got != (Position{Line: 0, Col: 2}) {
37 t.Errorf("Cursor() = %+v, want the end of line 0", got)
38 }
39}
40
41func TestMoveLeftStopsAtTheStartOfTheBuffer(t *testing.T) {
42 b := NewFromString("ab")
43 b.SetCursor(Position{})
44
45 b.MoveLeft()
46
47 if got := b.Cursor(); got != (Position{}) {
48 t.Errorf("Cursor() = %+v, want it to stay at the origin", got)
49 }
50}
51
52func TestMoveRightWrapsOntoTheNextLine(t *testing.T) {
53 b := NewFromString("ab\ncd")
54 b.SetCursor(Position{Line: 0, Col: 2})
55
56 b.MoveRight()
57
58 if got := b.Cursor(); got != (Position{Line: 1, Col: 0}) {
59 t.Errorf("Cursor() = %+v, want the start of line 1", got)
60 }
61}
62
63func TestMoveRightStopsAtTheEndOfTheBuffer(t *testing.T) {
64 b := NewFromString("ab")
65 b.MoveBufferEnd()
66
67 b.MoveRight()
68
69 if got := b.Cursor(); got != (Position{Line: 0, Col: 2}) {
70 t.Errorf("Cursor() = %+v, want it to stay at the end", got)
71 }
72}
73
74func TestMoveUpAndDownClampTheColumn(t *testing.T) {
75 b := NewFromString("longer line\nab")
76 b.SetCursor(Position{Line: 0, Col: 9})
77
78 b.MoveDown()
79 if got := b.Cursor(); got != (Position{Line: 1, Col: 2}) {
80 t.Errorf("after MoveDown, Cursor() = %+v, want the column clamped to 2", got)
81 }
82
83 b.MoveUp()
84 if got := b.Cursor(); got != (Position{Line: 0, Col: 2}) {
85 t.Errorf("after MoveUp, Cursor() = %+v, want the clamped column kept", got)
86 }
87}
88
89func TestMoveToLineAndBufferBounds(t *testing.T) {
90 b := NewFromString("abc\ndefgh")
91 b.SetCursor(Position{Line: 1, Col: 2})
92
93 b.MoveLineStart()
94 if got := b.Cursor(); got != (Position{Line: 1, Col: 0}) {
95 t.Errorf("MoveLineStart left the cursor at %+v", got)
96 }
97
98 b.MoveLineEnd()
99 if got := b.Cursor(); got != (Position{Line: 1, Col: 5}) {
100 t.Errorf("MoveLineEnd left the cursor at %+v", got)
101 }
102
103 b.MoveBufferStart()
104 if got := b.Cursor(); got != (Position{}) {
105 t.Errorf("MoveBufferStart left the cursor at %+v", got)
106 }
107
108 b.MoveBufferEnd()
109 if got := b.Cursor(); got != (Position{Line: 1, Col: 5}) {
110 t.Errorf("MoveBufferEnd left the cursor at %+v", got)
111 }
112}
113
114func TestMoveWordLeft(t *testing.T) {
115 tests := []struct {
116 name string
117 text string
118 from int
119 wantCol int
120 }{
121 {"from the end of a word", "foo bar", 7, 4},
122 {"from the start of a word", "foo bar", 4, 0},
123 {"over several separators", "foo bar", 9, 6},
124 {"from just after separators", "foo bar", 6, 0},
125 {"punctuation is a separator", "a.b", 3, 2},
126 }
127
128 for _, tc := range tests {
129 t.Run(tc.name, func(t *testing.T) {
130 b := NewFromString(tc.text)
131 b.SetCursor(Position{Line: 0, Col: tc.from})
132
133 b.MoveWordLeft()
134
135 if got := b.Cursor().Col; got != tc.wantCol {
136 t.Errorf("Cursor().Col = %d, want %d", got, tc.wantCol)
137 }
138 })
139 }
140}
141
142func TestMoveWordLeftAtTheStartOfTheBufferDoesNothing(t *testing.T) {
143 b := NewFromString("foo")
144 b.SetCursor(Position{})
145
146 b.MoveWordLeft()
147
148 if got := b.Cursor(); got != (Position{}) {
149 t.Errorf("Cursor() = %+v, want the origin", got)
150 }
151}
152
153func TestMoveWordRight(t *testing.T) {
154 tests := []struct {
155 name string
156 text string
157 from int
158 wantCol int
159 }{
160 {"from the start of a word", "foo bar", 0, 4},
161 {"from inside a word", "foo bar", 1, 4},
162 {"over several separators", "foo bar", 0, 6},
163 {"no word left, stops at the end", "foo bar", 4, 7},
164 }
165
166 for _, tc := range tests {
167 t.Run(tc.name, func(t *testing.T) {
168 b := NewFromString(tc.text)
169 b.SetCursor(Position{Line: 0, Col: tc.from})
170
171 b.MoveWordRight()
172
173 if got := b.Cursor().Col; got != tc.wantCol {
174 t.Errorf("Cursor().Col = %d, want %d", got, tc.wantCol)
175 }
176 })
177 }
178}
179
180func TestMoveWordRightAtTheEndOfALineWrapsOver(t *testing.T) {
181 b := NewFromString("foo\nbar")
182 b.SetCursor(Position{Line: 0, Col: 3})
183
184 b.MoveWordRight()
185
186 if got := b.Cursor(); got != (Position{Line: 1, Col: 0}) {
187 t.Errorf("Cursor() = %+v, want the start of the next line", got)
188 }
189}
190
191func TestDisplayColumnExpandsTabs(t *testing.T) {
192 b := NewFromString("\tab\tc")
193 b.SetTabWidth(4)
194
195 tests := []struct {
196 runeCol int
197 screenCol int
198 }{
199 {0, 0},
200 {1, 4}, // just after the leading tab
201 {2, 5},
202 {3, 6},
203 {4, 8}, // the second tab fills columns 6 and 7
204 {5, 9},
205 }
206
207 for _, tc := range tests {
208 if got := b.DisplayColumn(0, tc.runeCol); got != tc.screenCol {
209 t.Errorf("DisplayColumn(0, %d) = %d, want %d", tc.runeCol, got, tc.screenCol)
210 }
211 }
212}
213
214func TestDisplayColumnClampsItsArguments(t *testing.T) {
215 b := NewFromString("ab")
216
217 if got := b.DisplayColumn(0, 99); got != 2 {
218 t.Errorf("DisplayColumn(0, 99) = %d, want 2", got)
219 }
220 if got := b.DisplayColumn(0, -1); got != 0 {
221 t.Errorf("DisplayColumn(0, -1) = %d, want 0", got)
222 }
223 if got := b.DisplayColumn(99, 0); got != 0 {
224 t.Errorf("DisplayColumn(99, 0) = %d, want 0", got)
225 }
226}
227
228func TestRuneColumnIsTheInverseOfDisplayColumn(t *testing.T) {
229 b := NewFromString("\tab\tc")
230 b.SetTabWidth(4)
231
232 for runeCol := 0; runeCol <= b.LineLen(0); runeCol++ {
233 screenCol := b.DisplayColumn(0, runeCol)
234 if got := b.RuneColumn(0, screenCol); got != runeCol {
235 t.Errorf("RuneColumn(0, DisplayColumn(0, %d)=%d) = %d, want %d",
236 runeCol, screenCol, got, runeCol)
237 }
238 }
239}
240
241func TestRuneColumnInsideATabPicksThatTab(t *testing.T) {
242 b := NewFromString("\tx")
243 b.SetTabWidth(4)
244
245 for screenCol := 0; screenCol < 4; screenCol++ {
246 if got := b.RuneColumn(0, screenCol); got != 0 {
247 t.Errorf("RuneColumn(0, %d) = %d, want 0 — a click inside a tab lands on it", screenCol, got)
248 }
249 }
250 if got := b.RuneColumn(0, 99); got != 2 {
251 t.Errorf("RuneColumn(0, 99) = %d, want 2 — past the end lands after the last rune", got)
252 }
253}
254
255func TestIsWordRuneAcceptsIdentifierCharacters(t *testing.T) {
256 for _, r := range []rune{'a', 'Z', '0', '_', 'é', 'π'} {
257 if !IsWordRune(r) {
258 t.Errorf("IsWordRune(%q) = false, want true", r)
259 }
260 }
261 for _, r := range []rune{' ', '\t', '.', '(', '-'} {
262 if IsWordRune(r) {
263 t.Errorf("IsWordRune(%q) = true, want false", r)
264 }
265 }
266}