1 -- major tests for text editing flows
2 -- Arguably this should be called edit_tests.lua,
3 -- but that would mess up the git blame at this point.
5 function test_initial_state()
6 App
.screen
.init
{width
=120, height
=60}
7 Editor_state
= edit
.initialize_test_state()
8 Editor_state
.lines
= load_array
{}
9 Text
.redraw_all(Editor_state
)
10 edit
.draw(Editor_state
)
11 check_eq(#Editor_state
.lines
, 1, '#lines')
12 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
13 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
14 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
15 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
18 function test_backspace_from_start_of_final_line()
19 -- display final line of text with cursor at start of it
20 App
.screen
.init
{width
=120, height
=60}
21 Editor_state
= edit
.initialize_test_state()
22 Editor_state
.lines
= load_array
{'abc', 'def'}
23 Editor_state
.screen_top1
= {line
=2, pos
=1}
24 Editor_state
.cursor1
= {line
=2, pos
=1}
25 Text
.redraw_all(Editor_state
)
26 -- backspace scrolls up
27 edit
.run_after_keychord(Editor_state
, 'backspace', 'backspace')
28 check_eq(#Editor_state
.lines
, 1, '#lines')
29 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
30 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
33 function test_insert_first_character()
34 App
.screen
.init
{width
=120, height
=60}
35 Editor_state
= edit
.initialize_test_state()
36 Editor_state
.lines
= load_array
{}
37 Text
.redraw_all(Editor_state
)
38 edit
.draw(Editor_state
)
39 edit
.run_after_text_input(Editor_state
, 'a')
40 local y
= Editor_state
.top
41 App
.screen
.check(y
, 'a', 'screen:1')
44 function test_press_ctrl()
45 -- press ctrl while the cursor is on text
46 App
.screen
.init
{width
=50, height
=80}
47 Editor_state
= edit
.initialize_test_state()
48 Editor_state
.lines
= load_array
{''}
49 Text
.redraw_all(Editor_state
)
50 Editor_state
.cursor1
= {line
=1, pos
=1}
51 Editor_state
.screen_top1
= {line
=1, pos
=1}
52 edit
.run_after_keychord(Editor_state
, 'C-m', 'm')
55 function test_move_left()
56 App
.screen
.init
{width
=120, height
=60}
57 Editor_state
= edit
.initialize_test_state()
58 Editor_state
.lines
= load_array
{'a'}
59 Text
.redraw_all(Editor_state
)
60 Editor_state
.cursor1
= {line
=1, pos
=2}
61 edit
.draw(Editor_state
)
62 edit
.run_after_keychord(Editor_state
, 'left', 'left')
63 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
66 function test_move_right()
67 App
.screen
.init
{width
=120, height
=60}
68 Editor_state
= edit
.initialize_test_state()
69 Editor_state
.lines
= load_array
{'a'}
70 Text
.redraw_all(Editor_state
)
71 Editor_state
.cursor1
= {line
=1, pos
=1}
72 edit
.draw(Editor_state
)
73 edit
.run_after_keychord(Editor_state
, 'right', 'right')
74 check_eq(Editor_state
.cursor1
.pos
, 2, 'check')
77 function test_move_left_to_previous_line()
78 App
.screen
.init
{width
=120, height
=60}
79 Editor_state
= edit
.initialize_test_state()
80 Editor_state
.lines
= load_array
{'abc', 'def'}
81 Text
.redraw_all(Editor_state
)
82 Editor_state
.cursor1
= {line
=2, pos
=1}
83 edit
.draw(Editor_state
)
84 edit
.run_after_keychord(Editor_state
, 'left', 'left')
85 check_eq(Editor_state
.cursor1
.line
, 1, 'line')
86 check_eq(Editor_state
.cursor1
.pos
, 4, 'pos') -- past end of line
89 function test_move_right_to_next_line()
90 App
.screen
.init
{width
=120, height
=60}
91 Editor_state
= edit
.initialize_test_state()
92 Editor_state
.lines
= load_array
{'abc', 'def'}
93 Text
.redraw_all(Editor_state
)
94 Editor_state
.cursor1
= {line
=1, pos
=4} -- past end of line
95 edit
.draw(Editor_state
)
96 edit
.run_after_keychord(Editor_state
, 'right', 'right')
97 check_eq(Editor_state
.cursor1
.line
, 2, 'line')
98 check_eq(Editor_state
.cursor1
.pos
, 1, 'pos')
101 function test_move_to_start_of_word()
102 App
.screen
.init
{width
=120, height
=60}
103 Editor_state
= edit
.initialize_test_state()
104 Editor_state
.lines
= load_array
{'abc'}
105 Text
.redraw_all(Editor_state
)
106 Editor_state
.cursor1
= {line
=1, pos
=3}
107 edit
.draw(Editor_state
)
108 edit
.run_after_keychord(Editor_state
, 'M-left', 'left')
109 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
112 function test_move_to_start_of_previous_word()
113 App
.screen
.init
{width
=120, height
=60}
114 Editor_state
= edit
.initialize_test_state()
115 Editor_state
.lines
= load_array
{'abc def'}
116 Text
.redraw_all(Editor_state
)
117 Editor_state
.cursor1
= {line
=1, pos
=4} -- at the space between words
118 edit
.draw(Editor_state
)
119 edit
.run_after_keychord(Editor_state
, 'M-left', 'left')
120 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
123 function test_skip_to_previous_word()
124 App
.screen
.init
{width
=120, height
=60}
125 Editor_state
= edit
.initialize_test_state()
126 Editor_state
.lines
= load_array
{'abc def'}
127 Text
.redraw_all(Editor_state
)
128 Editor_state
.cursor1
= {line
=1, pos
=5} -- at the start of second word
129 edit
.draw(Editor_state
)
130 edit
.run_after_keychord(Editor_state
, 'M-left', 'left')
131 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
134 function test_skip_past_tab_to_previous_word()
135 App
.screen
.init
{width
=120, height
=60}
136 Editor_state
= edit
.initialize_test_state()
137 Editor_state
.lines
= load_array
{'abc def\tghi'}
138 Text
.redraw_all(Editor_state
)
139 Editor_state
.cursor1
= {line
=1, pos
=10} -- within third word
140 edit
.draw(Editor_state
)
141 edit
.run_after_keychord(Editor_state
, 'M-left', 'left')
142 check_eq(Editor_state
.cursor1
.pos
, 9, 'check')
145 function test_skip_multiple_spaces_to_previous_word()
146 App
.screen
.init
{width
=120, height
=60}
147 Editor_state
= edit
.initialize_test_state()
148 Editor_state
.lines
= load_array
{'abc def'}
149 Text
.redraw_all(Editor_state
)
150 Editor_state
.cursor1
= {line
=1, pos
=6} -- at the start of second word
151 edit
.draw(Editor_state
)
152 edit
.run_after_keychord(Editor_state
, 'M-left', 'left')
153 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
156 function test_move_to_start_of_word_on_previous_line()
157 App
.screen
.init
{width
=120, height
=60}
158 Editor_state
= edit
.initialize_test_state()
159 Editor_state
.lines
= load_array
{'abc def', 'ghi'}
160 Text
.redraw_all(Editor_state
)
161 Editor_state
.cursor1
= {line
=2, pos
=1}
162 edit
.draw(Editor_state
)
163 edit
.run_after_keychord(Editor_state
, 'M-left', 'left')
164 check_eq(Editor_state
.cursor1
.line
, 1, 'line')
165 check_eq(Editor_state
.cursor1
.pos
, 5, 'pos')
168 function test_move_past_end_of_word()
169 App
.screen
.init
{width
=120, height
=60}
170 Editor_state
= edit
.initialize_test_state()
171 Editor_state
.lines
= load_array
{'abc def'}
172 Text
.redraw_all(Editor_state
)
173 Editor_state
.cursor1
= {line
=1, pos
=1}
174 edit
.draw(Editor_state
)
175 edit
.run_after_keychord(Editor_state
, 'M-right', 'right')
176 check_eq(Editor_state
.cursor1
.pos
, 4, 'check')
179 function test_skip_to_next_word()
180 App
.screen
.init
{width
=120, height
=60}
181 Editor_state
= edit
.initialize_test_state()
182 Editor_state
.lines
= load_array
{'abc def'}
183 Text
.redraw_all(Editor_state
)
184 Editor_state
.cursor1
= {line
=1, pos
=4} -- at the space between words
185 edit
.draw(Editor_state
)
186 edit
.run_after_keychord(Editor_state
, 'M-right', 'right')
187 check_eq(Editor_state
.cursor1
.pos
, 8, 'check')
190 function test_skip_past_tab_to_next_word()
191 App
.screen
.init
{width
=120, height
=60}
192 Editor_state
= edit
.initialize_test_state()
193 Editor_state
.lines
= load_array
{'abc\tdef'}
194 Text
.redraw_all(Editor_state
)
195 Editor_state
.cursor1
= {line
=1, pos
=1} -- at the space between words
196 edit
.draw(Editor_state
)
197 edit
.run_after_keychord(Editor_state
, 'M-right', 'right')
198 check_eq(Editor_state
.cursor1
.pos
, 4, 'check')
201 function test_skip_multiple_spaces_to_next_word()
202 App
.screen
.init
{width
=120, height
=60}
203 Editor_state
= edit
.initialize_test_state()
204 Editor_state
.lines
= load_array
{'abc def'}
205 Text
.redraw_all(Editor_state
)
206 Editor_state
.cursor1
= {line
=1, pos
=4} -- at the start of second word
207 edit
.draw(Editor_state
)
208 edit
.run_after_keychord(Editor_state
, 'M-right', 'right')
209 check_eq(Editor_state
.cursor1
.pos
, 9, 'check')
212 function test_move_past_end_of_word_on_next_line()
213 App
.screen
.init
{width
=120, height
=60}
214 Editor_state
= edit
.initialize_test_state()
215 Editor_state
.lines
= load_array
{'abc def', 'ghi'}
216 Text
.redraw_all(Editor_state
)
217 Editor_state
.cursor1
= {line
=1, pos
=8}
218 edit
.draw(Editor_state
)
219 edit
.run_after_keychord(Editor_state
, 'M-right', 'right')
220 check_eq(Editor_state
.cursor1
.line
, 2, 'line')
221 check_eq(Editor_state
.cursor1
.pos
, 4, 'pos')
224 function test_click_moves_cursor()
225 App
.screen
.init
{width
=50, height
=60}
226 Editor_state
= edit
.initialize_test_state()
227 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
228 Text
.redraw_all(Editor_state
)
229 Editor_state
.cursor1
= {line
=1, pos
=1}
230 Editor_state
.screen_top1
= {line
=1, pos
=1}
231 Editor_state
.selection1
= {}
232 edit
.draw(Editor_state
) -- populate line_cache.startpos for each line
233 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
234 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
235 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
236 -- selection is empty to avoid perturbing future edits
237 check_nil(Editor_state
.selection1
.line
, 'selection:line')
238 check_nil(Editor_state
.selection1
.pos
, 'selection:pos')
241 function test_click_to_left_of_line()
242 -- display a line with the cursor in the middle
243 App
.screen
.init
{width
=50, height
=80}
244 Editor_state
= edit
.initialize_test_state()
245 Editor_state
.lines
= load_array
{'abc'}
246 Text
.redraw_all(Editor_state
)
247 Editor_state
.cursor1
= {line
=1, pos
=3}
248 Editor_state
.screen_top1
= {line
=1, pos
=1}
249 Editor_state
.selection1
= {}
250 -- click to the left of the line
251 edit
.draw(Editor_state
)
252 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
-4,Editor_state
.top
+5, 1)
253 -- cursor moves to start of line
254 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
255 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
256 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
259 function test_click_takes_margins_into_account()
260 -- display two lines with cursor on one of them
261 App
.screen
.init
{width
=100, height
=80}
262 Editor_state
= edit
.initialize_test_state()
263 Editor_state
.left
= 50 -- occupy only right side of screen
264 Editor_state
.lines
= load_array
{'abc', 'def'}
265 Text
.redraw_all(Editor_state
)
266 Editor_state
.cursor1
= {line
=2, pos
=1}
267 Editor_state
.screen_top1
= {line
=1, pos
=1}
268 Editor_state
.selection1
= {}
269 -- click on the other line
270 edit
.draw(Editor_state
)
271 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
273 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
274 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
275 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
278 function test_click_on_empty_line()
279 -- display two lines with the first one empty
280 App
.screen
.init
{width
=50, height
=80}
281 Editor_state
= edit
.initialize_test_state()
282 Editor_state
.lines
= load_array
{'', 'def'}
283 Text
.redraw_all(Editor_state
)
284 Editor_state
.cursor1
= {line
=2, pos
=1}
285 Editor_state
.screen_top1
= {line
=1, pos
=1}
286 Editor_state
.selection1
= {}
287 -- click on the empty line
288 edit
.draw(Editor_state
)
289 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
291 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
292 -- selection remains empty
293 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
296 function test_click_below_all_lines()
298 App
.screen
.init
{width
=50, height
=80}
299 Editor_state
= edit
.initialize_test_state()
300 Editor_state
.lines
= load_array
{'abc'}
301 Text
.redraw_all(Editor_state
)
302 Editor_state
.cursor1
= {line
=1, pos
=1}
303 Editor_state
.screen_top1
= {line
=1, pos
=1}
304 Editor_state
.selection1
= {}
305 -- click below first line
306 edit
.draw(Editor_state
)
307 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+50, 1)
308 -- cursor doesn't move
309 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
310 -- selection remains empty
311 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
314 function test_draw_text()
315 App
.screen
.init
{width
=120, height
=60}
316 Editor_state
= edit
.initialize_test_state()
317 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi'}
318 Text
.redraw_all(Editor_state
)
319 Editor_state
.cursor1
= {line
=1, pos
=1}
320 Editor_state
.screen_top1
= {line
=1, pos
=1}
321 edit
.draw(Editor_state
)
322 local y
= Editor_state
.top
323 App
.screen
.check(y
, 'abc', 'screen:1')
324 y
= y
+ Editor_state
.line_height
325 App
.screen
.check(y
, 'def', 'screen:2')
326 y
= y
+ Editor_state
.line_height
327 App
.screen
.check(y
, 'ghi', 'screen:3')
330 function test_draw_wrapping_text()
331 App
.screen
.init
{width
=50, height
=60}
332 Editor_state
= edit
.initialize_test_state()
333 Editor_state
.lines
= load_array
{'abc', 'defgh', 'xyz'}
334 Text
.redraw_all(Editor_state
)
335 Editor_state
.cursor1
= {line
=1, pos
=1}
336 Editor_state
.screen_top1
= {line
=1, pos
=1}
337 edit
.draw(Editor_state
)
338 local y
= Editor_state
.top
339 App
.screen
.check(y
, 'abc', 'screen:1')
340 y
= y
+ Editor_state
.line_height
341 App
.screen
.check(y
, 'de', 'screen:2')
342 y
= y
+ Editor_state
.line_height
343 App
.screen
.check(y
, 'fgh', 'screen:3')
346 function test_draw_word_wrapping_text()
347 App
.screen
.init
{width
=60, height
=60}
348 Editor_state
= edit
.initialize_test_state()
349 Editor_state
.lines
= load_array
{'abc def ghi', 'jkl'}
350 Text
.redraw_all(Editor_state
)
351 Editor_state
.cursor1
= {line
=1, pos
=1}
352 Editor_state
.screen_top1
= {line
=1, pos
=1}
353 edit
.draw(Editor_state
)
354 local y
= Editor_state
.top
355 App
.screen
.check(y
, 'abc ', 'screen:1')
356 y
= y
+ Editor_state
.line_height
357 App
.screen
.check(y
, 'def ', 'screen:2')
358 y
= y
+ Editor_state
.line_height
359 App
.screen
.check(y
, 'ghi', 'screen:3')
362 function test_click_on_wrapping_line()
363 -- display two screen lines with cursor on one of them
364 App
.screen
.init
{width
=50, height
=80}
365 Editor_state
= edit
.initialize_test_state()
366 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr stu'}
367 Text
.redraw_all(Editor_state
)
368 Editor_state
.cursor1
= {line
=1, pos
=20}
369 Editor_state
.screen_top1
= {line
=1, pos
=1}
370 -- click on the other line
371 edit
.draw(Editor_state
)
372 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
374 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
375 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
376 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
379 function test_click_on_wrapping_line_takes_margins_into_account()
380 -- display two screen lines with cursor on one of them
381 App
.screen
.init
{width
=100, height
=80}
382 Editor_state
= edit
.initialize_test_state()
383 Editor_state
.left
= 50 -- occupy only right side of screen
384 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr stu'}
385 Text
.redraw_all(Editor_state
)
386 Editor_state
.cursor1
= {line
=1, pos
=20}
387 Editor_state
.screen_top1
= {line
=1, pos
=1}
388 -- click on the other line
389 edit
.draw(Editor_state
)
390 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
392 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
393 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
394 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
397 function test_draw_text_wrapping_within_word()
398 -- arrange a screen line that needs to be split within a word
399 App
.screen
.init
{width
=60, height
=60}
400 Editor_state
= edit
.initialize_test_state()
401 Editor_state
.lines
= load_array
{'abcd e fghijk', 'xyz'}
402 Text
.redraw_all(Editor_state
)
403 Editor_state
.cursor1
= {line
=1, pos
=1}
404 Editor_state
.screen_top1
= {line
=1, pos
=1}
405 edit
.draw(Editor_state
)
406 local y
= Editor_state
.top
407 App
.screen
.check(y
, 'abcd ', 'screen:1')
408 y
= y
+ Editor_state
.line_height
409 App
.screen
.check(y
, 'e fgh', 'screen:2')
410 y
= y
+ Editor_state
.line_height
411 App
.screen
.check(y
, 'ijk', 'screen:3')
414 function test_draw_wrapping_text_containing_non_ascii()
415 -- draw a long line containing non-ASCII
416 App
.screen
.init
{width
=60, height
=60}
417 Editor_state
= edit
.initialize_test_state()
418 Editor_state
.lines
= load_array
{'madam I’m adam', 'xyz'} -- notice the non-ASCII apostrophe
419 Text
.redraw_all(Editor_state
)
420 Editor_state
.cursor1
= {line
=1, pos
=1}
421 Editor_state
.screen_top1
= {line
=1, pos
=1}
422 edit
.draw(Editor_state
)
423 local y
= Editor_state
.top
424 App
.screen
.check(y
, 'mad', 'screen:1')
425 y
= y
+ Editor_state
.line_height
426 App
.screen
.check(y
, 'am I', 'screen:2')
427 y
= y
+ Editor_state
.line_height
428 App
.screen
.check(y
, '’m a', 'screen:3')
431 function test_click_past_end_of_screen_line()
432 -- display a wrapping line
433 App
.screen
.init
{width
=75, height
=80}
434 Editor_state
= edit
.initialize_test_state()
436 Editor_state
.lines
= load_array
{"madam I'm adam"}
437 Text
.redraw_all(Editor_state
)
438 Editor_state
.cursor1
= {line
=1, pos
=1}
439 Editor_state
.screen_top1
= {line
=1, pos
=1}
440 edit
.draw(Editor_state
)
441 local y
= Editor_state
.top
442 App
.screen
.check(y
, 'madam ', 'baseline/screen:1')
443 y
= y
+ Editor_state
.line_height
444 App
.screen
.check(y
, "I'm ad", 'baseline/screen:2')
445 y
= y
+ Editor_state
.line_height
446 -- click past end of second screen line
447 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
448 -- cursor moves to end of screen line (one more than final character shown)
449 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
450 check_eq(Editor_state
.cursor1
.pos
, 13, 'cursor:pos')
453 function test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen()
454 -- display a wrapping line from its second screen line
455 App
.screen
.init
{width
=75, height
=80}
456 Editor_state
= edit
.initialize_test_state()
458 Editor_state
.lines
= load_array
{"madam I'm adam"}
459 Text
.redraw_all(Editor_state
)
460 Editor_state
.cursor1
= {line
=1, pos
=8}
461 Editor_state
.screen_top1
= {line
=1, pos
=7}
462 edit
.draw(Editor_state
)
463 local y
= Editor_state
.top
464 App
.screen
.check(y
, "I'm ad", 'baseline/screen:2')
465 y
= y
+ Editor_state
.line_height
466 -- click past end of second screen line
467 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
468 -- cursor moves to end of screen line (one more than final character shown)
469 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
470 check_eq(Editor_state
.cursor1
.pos
, 13, 'cursor:pos')
473 function test_click_past_end_of_wrapping_line()
474 -- display a wrapping line
475 App
.screen
.init
{width
=75, height
=80}
476 Editor_state
= edit
.initialize_test_state()
478 Editor_state
.lines
= load_array
{"madam I'm adam"}
479 Text
.redraw_all(Editor_state
)
480 Editor_state
.cursor1
= {line
=1, pos
=1}
481 Editor_state
.screen_top1
= {line
=1, pos
=1}
482 edit
.draw(Editor_state
)
483 local y
= Editor_state
.top
484 App
.screen
.check(y
, 'madam ', 'baseline/screen:1')
485 y
= y
+ Editor_state
.line_height
486 App
.screen
.check(y
, "I'm ad", 'baseline/screen:2')
487 y
= y
+ Editor_state
.line_height
488 App
.screen
.check(y
, 'am', 'baseline/screen:3')
489 y
= y
+ Editor_state
.line_height
490 -- click past the end of it
491 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
492 -- cursor moves to end of line
493 check_eq(Editor_state
.cursor1
.pos
, 15, 'cursor') -- one more than the number of UTF-8 code-points
496 function test_click_past_end_of_wrapping_line_containing_non_ascii()
497 -- display a wrapping line containing non-ASCII
498 App
.screen
.init
{width
=75, height
=80}
499 Editor_state
= edit
.initialize_test_state()
501 Editor_state
.lines
= load_array
{'madam I’m adam'} -- notice the non-ASCII apostrophe
502 Text
.redraw_all(Editor_state
)
503 Editor_state
.cursor1
= {line
=1, pos
=1}
504 Editor_state
.screen_top1
= {line
=1, pos
=1}
505 edit
.draw(Editor_state
)
506 local y
= Editor_state
.top
507 App
.screen
.check(y
, 'madam ', 'baseline/screen:1')
508 y
= y
+ Editor_state
.line_height
509 App
.screen
.check(y
, 'I’m ad', 'baseline/screen:2')
510 y
= y
+ Editor_state
.line_height
511 App
.screen
.check(y
, 'am', 'baseline/screen:3')
512 y
= y
+ Editor_state
.line_height
513 -- click past the end of it
514 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
515 -- cursor moves to end of line
516 check_eq(Editor_state
.cursor1
.pos
, 15, 'cursor') -- one more than the number of UTF-8 code-points
519 function test_click_past_end_of_word_wrapping_line()
520 -- display a long line wrapping at a word boundary on a screen of more realistic length
521 App
.screen
.init
{width
=160, height
=80}
522 Editor_state
= edit
.initialize_test_state()
524 -- 123456789012345678901
525 Editor_state
.lines
= load_array
{'the quick brown fox jumped over the lazy dog'}
526 Text
.redraw_all(Editor_state
)
527 Editor_state
.cursor1
= {line
=1, pos
=1}
528 Editor_state
.screen_top1
= {line
=1, pos
=1}
529 edit
.draw(Editor_state
)
530 local y
= Editor_state
.top
531 App
.screen
.check(y
, 'the quick brown fox ', 'baseline/screen:1')
532 y
= y
+ Editor_state
.line_height
533 -- click past the end of the screen line
534 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
535 -- cursor moves to end of screen line (one more than final character shown)
536 check_eq(Editor_state
.cursor1
.pos
, 21, 'cursor')
539 function test_select_text()
540 -- display a line of text
541 App
.screen
.init
{width
=75, height
=80}
542 Editor_state
= edit
.initialize_test_state()
543 Editor_state
.lines
= load_array
{'abc def'}
544 Text
.redraw_all(Editor_state
)
545 Editor_state
.cursor1
= {line
=1, pos
=1}
546 Editor_state
.screen_top1
= {line
=1, pos
=1}
547 edit
.draw(Editor_state
)
549 App
.fake_key_press('lshift')
550 edit
.run_after_keychord(Editor_state
, 'S-right', 'right')
551 App
.fake_key_release('lshift')
552 edit
.key_release(Editor_state
, 'lshift')
553 -- selection persists even after shift is released
554 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
555 check_eq(Editor_state
.selection1
.pos
, 1, 'selection:pos')
556 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
557 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
560 function test_cursor_movement_without_shift_resets_selection()
561 -- display a line of text with some part selected
562 App
.screen
.init
{width
=75, height
=80}
563 Editor_state
= edit
.initialize_test_state()
564 Editor_state
.lines
= load_array
{'abc'}
565 Text
.redraw_all(Editor_state
)
566 Editor_state
.cursor1
= {line
=1, pos
=1}
567 Editor_state
.selection1
= {line
=1, pos
=2}
568 Editor_state
.screen_top1
= {line
=1, pos
=1}
569 edit
.draw(Editor_state
)
570 -- press an arrow key without shift
571 edit
.run_after_keychord(Editor_state
, 'right', 'right')
572 -- no change to data, selection is reset
573 check_nil(Editor_state
.selection1
.line
, 'check')
574 check_eq(Editor_state
.lines
[1].data
, 'abc', 'data')
577 function test_edit_deletes_selection()
578 -- display a line of text with some part selected
579 App
.screen
.init
{width
=75, height
=80}
580 Editor_state
= edit
.initialize_test_state()
581 Editor_state
.lines
= load_array
{'abc'}
582 Text
.redraw_all(Editor_state
)
583 Editor_state
.cursor1
= {line
=1, pos
=1}
584 Editor_state
.selection1
= {line
=1, pos
=2}
585 Editor_state
.screen_top1
= {line
=1, pos
=1}
586 edit
.draw(Editor_state
)
588 edit
.run_after_text_input(Editor_state
, 'x')
589 -- selected text is deleted and replaced with the key
590 check_eq(Editor_state
.lines
[1].data
, 'xbc', 'check')
593 function test_edit_with_shift_key_deletes_selection()
594 -- display a line of text with some part selected
595 App
.screen
.init
{width
=75, height
=80}
596 Editor_state
= edit
.initialize_test_state()
597 Editor_state
.lines
= load_array
{'abc'}
598 Text
.redraw_all(Editor_state
)
599 Editor_state
.cursor1
= {line
=1, pos
=1}
600 Editor_state
.selection1
= {line
=1, pos
=2}
601 Editor_state
.screen_top1
= {line
=1, pos
=1}
602 edit
.draw(Editor_state
)
603 -- mimic precise keypresses for a capital letter
604 App
.fake_key_press('lshift')
605 edit
.keychord_press(Editor_state
, 'd', 'd')
606 edit
.text_input(Editor_state
, 'D')
607 edit
.key_release(Editor_state
, 'd')
608 App
.fake_key_release('lshift')
609 -- selected text is deleted and replaced with the key
610 check_nil(Editor_state
.selection1
.line
, 'check')
611 check_eq(Editor_state
.lines
[1].data
, 'Dbc', 'data')
614 function test_copy_does_not_reset_selection()
615 -- display a line of text with a selection
616 App
.screen
.init
{width
=75, height
=80}
617 Editor_state
= edit
.initialize_test_state()
618 Editor_state
.lines
= load_array
{'abc'}
619 Text
.redraw_all(Editor_state
)
620 Editor_state
.cursor1
= {line
=1, pos
=1}
621 Editor_state
.selection1
= {line
=1, pos
=2}
622 Editor_state
.screen_top1
= {line
=1, pos
=1}
623 edit
.draw(Editor_state
)
625 edit
.run_after_keychord(Editor_state
, 'C-c', 'c')
626 check_eq(App
.clipboard
, 'a', 'clipboard')
627 -- selection is reset since shift key is not pressed
628 check(Editor_state
.selection1
.line
, 'check')
632 -- display a line of text with some part selected
633 App
.screen
.init
{width
=75, height
=80}
634 Editor_state
= edit
.initialize_test_state()
635 Editor_state
.lines
= load_array
{'abc'}
636 Text
.redraw_all(Editor_state
)
637 Editor_state
.cursor1
= {line
=1, pos
=1}
638 Editor_state
.selection1
= {line
=1, pos
=2}
639 Editor_state
.screen_top1
= {line
=1, pos
=1}
640 edit
.draw(Editor_state
)
642 edit
.run_after_keychord(Editor_state
, 'C-x', 'x')
643 check_eq(App
.clipboard
, 'a', 'clipboard')
644 -- selected text is deleted
645 check_eq(Editor_state
.lines
[1].data
, 'bc', 'data')
648 function test_paste_replaces_selection()
649 -- display a line of text with a selection
650 App
.screen
.init
{width
=75, height
=80}
651 Editor_state
= edit
.initialize_test_state()
652 Editor_state
.lines
= load_array
{'abc', 'def'}
653 Text
.redraw_all(Editor_state
)
654 Editor_state
.cursor1
= {line
=2, pos
=1}
655 Editor_state
.selection1
= {line
=1, pos
=1}
656 Editor_state
.screen_top1
= {line
=1, pos
=1}
657 edit
.draw(Editor_state
)
659 App
.clipboard
= 'xyz'
661 edit
.run_after_keychord(Editor_state
, 'C-v', 'v')
662 -- selection is reset since shift key is not pressed
663 -- selection includes the newline, so it's also deleted
664 check_eq(Editor_state
.lines
[1].data
, 'xyzdef', 'check')
667 function test_deleting_selection_may_scroll()
668 -- display lines 2/3/4
669 App
.screen
.init
{width
=120, height
=60}
670 Editor_state
= edit
.initialize_test_state()
671 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
672 Text
.redraw_all(Editor_state
)
673 Editor_state
.cursor1
= {line
=3, pos
=2}
674 Editor_state
.screen_top1
= {line
=2, pos
=1}
675 edit
.draw(Editor_state
)
676 local y
= Editor_state
.top
677 App
.screen
.check(y
, 'def', 'baseline/screen:1')
678 y
= y
+ Editor_state
.line_height
679 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
680 y
= y
+ Editor_state
.line_height
681 App
.screen
.check(y
, 'jkl', 'baseline/screen:3')
682 -- set up a selection starting above the currently displayed page
683 Editor_state
.selection1
= {line
=1, pos
=2}
685 edit
.run_after_keychord(Editor_state
, 'backspace', 'backspace')
687 check_eq(Editor_state
.screen_top1
.line
, 1, 'check')
688 check_eq(Editor_state
.lines
[1].data
, 'ahi', 'data')
691 function test_edit_wrapping_text()
692 App
.screen
.init
{width
=50, height
=60}
693 Editor_state
= edit
.initialize_test_state()
694 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
695 Text
.redraw_all(Editor_state
)
696 Editor_state
.cursor1
= {line
=2, pos
=4}
697 Editor_state
.screen_top1
= {line
=1, pos
=1}
698 edit
.draw(Editor_state
)
699 edit
.run_after_text_input(Editor_state
, 'g')
700 local y
= Editor_state
.top
701 App
.screen
.check(y
, 'abc', 'screen:1')
702 y
= y
+ Editor_state
.line_height
703 App
.screen
.check(y
, 'de', 'screen:2')
704 y
= y
+ Editor_state
.line_height
705 App
.screen
.check(y
, 'fg', 'screen:3')
708 function test_insert_newline()
709 -- display a few lines
710 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
711 Editor_state
= edit
.initialize_test_state()
712 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
713 Text
.redraw_all(Editor_state
)
714 Editor_state
.cursor1
= {line
=1, pos
=2}
715 Editor_state
.screen_top1
= {line
=1, pos
=1}
716 edit
.draw(Editor_state
)
717 local y
= Editor_state
.top
718 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
719 y
= y
+ Editor_state
.line_height
720 App
.screen
.check(y
, 'def', 'baseline/screen:2')
721 y
= y
+ Editor_state
.line_height
722 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
723 -- hitting the enter key splits the line
724 edit
.run_after_keychord(Editor_state
, 'return', 'return')
725 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
726 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
727 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
729 App
.screen
.check(y
, 'a', 'screen:1')
730 y
= y
+ Editor_state
.line_height
731 App
.screen
.check(y
, 'bc', 'screen:2')
732 y
= y
+ Editor_state
.line_height
733 App
.screen
.check(y
, 'def', 'screen:3')
736 function test_insert_newline_at_start_of_line()
738 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
739 Editor_state
= edit
.initialize_test_state()
740 Editor_state
.lines
= load_array
{'abc'}
741 Text
.redraw_all(Editor_state
)
742 Editor_state
.cursor1
= {line
=1, pos
=1}
743 Editor_state
.screen_top1
= {line
=1, pos
=1}
744 -- hitting the enter key splits the line
745 edit
.run_after_keychord(Editor_state
, 'return', 'return')
746 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
747 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
748 check_eq(Editor_state
.lines
[1].data
, '', 'data:1')
749 check_eq(Editor_state
.lines
[2].data
, 'abc', 'data:2')
752 function test_insert_from_clipboard()
753 -- display a few lines
754 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
755 Editor_state
= edit
.initialize_test_state()
756 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
757 Text
.redraw_all(Editor_state
)
758 Editor_state
.cursor1
= {line
=1, pos
=2}
759 Editor_state
.screen_top1
= {line
=1, pos
=1}
760 edit
.draw(Editor_state
)
761 local y
= Editor_state
.top
762 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
763 y
= y
+ Editor_state
.line_height
764 App
.screen
.check(y
, 'def', 'baseline/screen:2')
765 y
= y
+ Editor_state
.line_height
766 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
767 -- paste some text including a newline, check that new line is created
768 App
.clipboard
= 'xy\nz'
769 edit
.run_after_keychord(Editor_state
, 'C-v', 'v')
770 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
771 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
772 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
774 App
.screen
.check(y
, 'axy', 'screen:1')
775 y
= y
+ Editor_state
.line_height
776 App
.screen
.check(y
, 'zbc', 'screen:2')
777 y
= y
+ Editor_state
.line_height
778 App
.screen
.check(y
, 'def', 'screen:3')
781 function test_select_text_using_mouse()
782 App
.screen
.init
{width
=50, height
=60}
783 Editor_state
= edit
.initialize_test_state()
784 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
785 Text
.redraw_all(Editor_state
)
786 Editor_state
.cursor1
= {line
=1, pos
=1}
787 Editor_state
.screen_top1
= {line
=1, pos
=1}
788 Editor_state
.selection1
= {}
789 edit
.draw(Editor_state
) -- populate line_cache.startpos for each line
790 -- press and hold on first location
791 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
792 -- drag and release somewhere else
793 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+Editor_state
.line_height
+5, 1)
794 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
795 check_eq(Editor_state
.selection1
.pos
, 2, 'selection:pos')
796 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
797 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
800 function test_select_text_using_mouse_starting_above_text()
801 App
.screen
.init
{width
=50, height
=60}
802 Editor_state
= edit
.initialize_test_state()
803 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
804 Text
.redraw_all(Editor_state
)
805 Editor_state
.cursor1
= {line
=1, pos
=1}
806 Editor_state
.screen_top1
= {line
=1, pos
=1}
807 Editor_state
.selection1
= {}
808 edit
.draw(Editor_state
) -- populate line_cache.startpos for each line
809 -- press mouse above first line of text
810 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+8,5, 1)
811 check(Editor_state
.selection1
.line
~= nil, 'selection:line-not-nil')
812 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
813 check_eq(Editor_state
.selection1
.pos
, 1, 'selection:pos')
816 function test_select_text_using_mouse_starting_above_text_wrapping_line()
817 -- first screen line starts in the middle of a line
818 App
.screen
.init
{width
=50, height
=60}
819 Editor_state
= edit
.initialize_test_state()
820 Editor_state
.lines
= load_array
{'abc', 'defgh', 'xyz'}
821 Text
.redraw_all(Editor_state
)
822 Editor_state
.cursor1
= {line
=2, pos
=5}
823 Editor_state
.screen_top1
= {line
=2, pos
=3}
824 -- press mouse above first line of text
825 edit
.draw(Editor_state
)
826 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+8,5, 1)
827 -- selection is at screen top
828 check(Editor_state
.selection1
.line
~= nil, 'selection:line-not-nil')
829 check_eq(Editor_state
.selection1
.line
, 2, 'selection:line')
830 check_eq(Editor_state
.selection1
.pos
, 3, 'selection:pos')
833 function test_select_text_using_mouse_starting_below_text()
834 -- I'd like to test what happens when a mouse click is below some page of
835 -- text, potentially even in the middle of a line.
836 -- However, it's brittle to set up a text line boundary just right.
837 -- So I'm going to just check things below the bottom of the final line of
838 -- text when it's in the middle of the screen.
839 -- final screen line ends in the middle of screen
840 App
.screen
.init
{width
=50, height
=60}
841 Editor_state
= edit
.initialize_test_state()
842 Editor_state
.lines
= load_array
{'abcde'}
843 Text
.redraw_all(Editor_state
)
844 Editor_state
.cursor1
= {line
=1, pos
=1}
845 Editor_state
.screen_top1
= {line
=1, pos
=1}
846 edit
.draw(Editor_state
)
847 local y
= Editor_state
.top
848 App
.screen
.check(y
, 'ab', 'baseline:screen:1')
849 y
= y
+ Editor_state
.line_height
850 App
.screen
.check(y
, 'cde', 'baseline:screen:2')
851 -- press mouse above first line of text
852 edit
.run_after_mouse_press(Editor_state
, 5,App
.screen
.height
-5, 1)
853 -- selection is past bottom-most text in screen
854 check(Editor_state
.selection1
.line
~= nil, 'selection:line-not-nil')
855 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
856 check_eq(Editor_state
.selection1
.pos
, 6, 'selection:pos')
859 function test_select_text_using_mouse_and_shift()
860 App
.screen
.init
{width
=50, height
=60}
861 Editor_state
= edit
.initialize_test_state()
862 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
863 Text
.redraw_all(Editor_state
)
864 Editor_state
.cursor1
= {line
=1, pos
=1}
865 Editor_state
.screen_top1
= {line
=1, pos
=1}
866 Editor_state
.selection1
= {}
867 edit
.draw(Editor_state
) -- populate line_cache.startpos for each line
868 -- click on first location
869 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
870 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
871 -- hold down shift and click somewhere else
872 App
.fake_key_press('lshift')
873 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+5, 1)
874 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+Editor_state
.line_height
+5, 1)
875 App
.fake_key_release('lshift')
876 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
877 check_eq(Editor_state
.selection1
.pos
, 2, 'selection:pos')
878 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
879 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
882 function test_select_text_repeatedly_using_mouse_and_shift()
883 App
.screen
.init
{width
=50, height
=60}
884 Editor_state
= edit
.initialize_test_state()
885 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
886 Text
.redraw_all(Editor_state
)
887 Text
.redraw_all(Editor_state
)
888 Editor_state
.cursor1
= {line
=1, pos
=1}
889 Editor_state
.screen_top1
= {line
=1, pos
=1}
890 Editor_state
.selection1
= {}
891 edit
.draw(Editor_state
) -- populate line_cache.startpos for each line
892 -- click on first location
893 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
894 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
895 -- hold down shift and click on a second location
896 App
.fake_key_press('lshift')
897 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+5, 1)
898 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+Editor_state
.line_height
+5, 1)
899 -- hold down shift and click at a third location
900 App
.fake_key_press('lshift')
901 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+5, 1)
902 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+Editor_state
.line_height
+5, 1)
903 App
.fake_key_release('lshift')
904 -- selection is between first and third location. forget the second location, not the first.
905 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
906 check_eq(Editor_state
.selection1
.pos
, 2, 'selection:pos')
907 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
908 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
911 function test_select_all_text()
912 -- display a single line of text
913 App
.screen
.init
{width
=75, height
=80}
914 Editor_state
= edit
.initialize_test_state()
915 Editor_state
.lines
= load_array
{'abc def'}
916 Text
.redraw_all(Editor_state
)
917 Editor_state
.cursor1
= {line
=1, pos
=1}
918 Editor_state
.screen_top1
= {line
=1, pos
=1}
919 edit
.draw(Editor_state
)
921 App
.fake_key_press('lctrl')
922 edit
.run_after_keychord(Editor_state
, 'C-a', 'a')
923 App
.fake_key_release('lctrl')
924 edit
.key_release(Editor_state
, 'lctrl')
926 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
927 check_eq(Editor_state
.selection1
.pos
, 1, 'selection:pos')
928 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
929 check_eq(Editor_state
.cursor1
.pos
, 8, 'cursor:pos')
932 function test_cut_without_selection()
933 -- display a few lines
934 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
935 Editor_state
= edit
.initialize_test_state()
936 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
937 Text
.redraw_all(Editor_state
)
938 Editor_state
.cursor1
= {line
=1, pos
=2}
939 Editor_state
.screen_top1
= {line
=1, pos
=1}
940 Editor_state
.selection1
= {}
941 edit
.draw(Editor_state
)
942 -- try to cut without selecting text
943 edit
.run_after_keychord(Editor_state
, 'C-x', 'x')
945 check_nil(Editor_state
.selection1
.line
, 'check')
948 function test_pagedown()
949 App
.screen
.init
{width
=120, height
=45}
950 Editor_state
= edit
.initialize_test_state()
951 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi'}
952 Text
.redraw_all(Editor_state
)
953 Editor_state
.cursor1
= {line
=1, pos
=1}
954 Editor_state
.screen_top1
= {line
=1, pos
=1}
955 -- initially the first two lines are displayed
956 edit
.draw(Editor_state
)
957 local y
= Editor_state
.top
958 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
959 y
= y
+ Editor_state
.line_height
960 App
.screen
.check(y
, 'def', 'baseline/screen:2')
961 -- after pagedown the bottom line becomes the top
962 edit
.run_after_keychord(Editor_state
, 'pagedown', 'pagedown')
963 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
964 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor')
966 App
.screen
.check(y
, 'def', 'screen:1')
967 y
= y
+ Editor_state
.line_height
968 App
.screen
.check(y
, 'ghi', 'screen:2')
971 function test_pagedown_can_start_from_middle_of_long_wrapping_line()
972 -- draw a few lines starting from a very long wrapping line
973 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
974 Editor_state
= edit
.initialize_test_state()
975 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr stu vwx yza bcd efg hij', 'XYZ'}
976 Text
.redraw_all(Editor_state
)
977 Editor_state
.cursor1
= {line
=1, pos
=2}
978 Editor_state
.screen_top1
= {line
=1, pos
=1}
979 edit
.draw(Editor_state
)
980 local y
= Editor_state
.top
981 App
.screen
.check(y
, 'abc ', 'baseline/screen:1')
982 y
= y
+ Editor_state
.line_height
983 App
.screen
.check(y
, 'def ', 'baseline/screen:2')
984 y
= y
+ Editor_state
.line_height
985 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3')
986 -- after pagedown we scroll down the very long wrapping line
987 edit
.run_after_keychord(Editor_state
, 'pagedown', 'pagedown')
988 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
989 check_eq(Editor_state
.screen_top1
.pos
, 9, 'screen_top:pos')
991 App
.screen
.check(y
, 'ghi ', 'screen:1')
992 y
= y
+ Editor_state
.line_height
993 App
.screen
.check(y
, 'jkl ', 'screen:2')
994 y
= y
+ Editor_state
.line_height
995 if Version
== '12.0' then
996 -- HACK: Maybe v12.0 uses a different font? Strange that it only causes
997 -- issues in a couple of places.
998 -- We'll need to rethink our tests if issues like this start to multiply.
999 App
.screen
.check(y
, 'mno ', 'screen:3')
1001 App
.screen
.check(y
, 'mn', 'screen:3')
1005 function test_pagedown_never_moves_up()
1006 -- draw the final screen line of a wrapping line
1007 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1008 Editor_state
= edit
.initialize_test_state()
1009 Editor_state
.lines
= load_array
{'abc def ghi'}
1010 Text
.redraw_all(Editor_state
)
1011 Editor_state
.cursor1
= {line
=1, pos
=9}
1012 Editor_state
.screen_top1
= {line
=1, pos
=9}
1013 edit
.draw(Editor_state
)
1014 -- pagedown makes no change
1015 edit
.run_after_keychord(Editor_state
, 'pagedown', 'pagedown')
1016 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
1017 check_eq(Editor_state
.screen_top1
.pos
, 9, 'screen_top:pos')
1020 function test_down_arrow_moves_cursor()
1021 App
.screen
.init
{width
=120, height
=60}
1022 Editor_state
= edit
.initialize_test_state()
1023 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1024 Text
.redraw_all(Editor_state
)
1025 Editor_state
.cursor1
= {line
=1, pos
=1}
1026 Editor_state
.screen_top1
= {line
=1, pos
=1}
1027 -- initially the first three lines are displayed
1028 edit
.draw(Editor_state
)
1029 local y
= Editor_state
.top
1030 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1031 y
= y
+ Editor_state
.line_height
1032 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1033 y
= y
+ Editor_state
.line_height
1034 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1035 -- after hitting the down arrow, the cursor moves down by 1 line
1036 edit
.run_after_keychord(Editor_state
, 'down', 'down')
1037 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1038 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor')
1039 -- the screen is unchanged
1040 y
= Editor_state
.top
1041 App
.screen
.check(y
, 'abc', 'screen:1')
1042 y
= y
+ Editor_state
.line_height
1043 App
.screen
.check(y
, 'def', 'screen:2')
1044 y
= y
+ Editor_state
.line_height
1045 App
.screen
.check(y
, 'ghi', 'screen:3')
1048 function test_down_arrow_scrolls_down_by_one_line()
1049 -- display the first three lines with the cursor on the bottom line
1050 App
.screen
.init
{width
=120, height
=60}
1051 Editor_state
= edit
.initialize_test_state()
1052 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1053 Text
.redraw_all(Editor_state
)
1054 Editor_state
.cursor1
= {line
=3, pos
=1}
1055 Editor_state
.screen_top1
= {line
=1, pos
=1}
1056 edit
.draw(Editor_state
)
1057 local y
= Editor_state
.top
1058 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1059 y
= y
+ Editor_state
.line_height
1060 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1061 y
= y
+ Editor_state
.line_height
1062 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1063 -- after hitting the down arrow the screen scrolls down by one line
1064 edit
.run_after_keychord(Editor_state
, 'down', 'down')
1065 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1066 check_eq(Editor_state
.cursor1
.line
, 4, 'cursor')
1067 y
= Editor_state
.top
1068 App
.screen
.check(y
, 'def', 'screen:1')
1069 y
= y
+ Editor_state
.line_height
1070 App
.screen
.check(y
, 'ghi', 'screen:2')
1071 y
= y
+ Editor_state
.line_height
1072 App
.screen
.check(y
, 'jkl', 'screen:3')
1075 function test_down_arrow_scrolls_down_by_one_screen_line()
1076 -- display the first three lines with the cursor on the bottom line
1077 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1078 Editor_state
= edit
.initialize_test_state()
1079 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1080 Text
.redraw_all(Editor_state
)
1081 Editor_state
.cursor1
= {line
=3, pos
=1}
1082 Editor_state
.screen_top1
= {line
=1, pos
=1}
1083 edit
.draw(Editor_state
)
1084 local y
= Editor_state
.top
1085 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1086 y
= y
+ Editor_state
.line_height
1087 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1088 y
= y
+ Editor_state
.line_height
1089 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1090 -- after hitting the down arrow the screen scrolls down by one line
1091 edit
.run_after_keychord(Editor_state
, 'down', 'down')
1092 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1093 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1094 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1095 y
= Editor_state
.top
1096 App
.screen
.check(y
, 'def', 'screen:1')
1097 y
= y
+ Editor_state
.line_height
1098 App
.screen
.check(y
, 'ghi ', 'screen:2')
1099 y
= y
+ Editor_state
.line_height
1100 App
.screen
.check(y
, 'jkl', 'screen:3')
1103 function test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word()
1104 -- display the first three lines with the cursor on the bottom line
1105 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1106 Editor_state
= edit
.initialize_test_state()
1107 Editor_state
.lines
= load_array
{'abc', 'def', 'ghijkl', 'mno'}
1108 Text
.redraw_all(Editor_state
)
1109 Editor_state
.cursor1
= {line
=3, pos
=1}
1110 Editor_state
.screen_top1
= {line
=1, pos
=1}
1111 edit
.draw(Editor_state
)
1112 local y
= Editor_state
.top
1113 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1114 y
= y
+ Editor_state
.line_height
1115 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1116 y
= y
+ Editor_state
.line_height
1117 App
.screen
.check(y
, 'ghij', 'baseline/screen:3')
1118 -- after hitting the down arrow the screen scrolls down by one line
1119 edit
.run_after_keychord(Editor_state
, 'down', 'down')
1120 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1121 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1122 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1123 y
= Editor_state
.top
1124 App
.screen
.check(y
, 'def', 'screen:1')
1125 y
= y
+ Editor_state
.line_height
1126 App
.screen
.check(y
, 'ghij', 'screen:2')
1127 y
= y
+ Editor_state
.line_height
1128 App
.screen
.check(y
, 'kl', 'screen:3')
1131 function test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up()
1132 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1133 Editor_state
= edit
.initialize_test_state()
1134 Editor_state
.lines
= load_array
{'abc', 'def', 'ghijkl', 'mno'}
1135 Text
.redraw_all(Editor_state
)
1136 Editor_state
.cursor1
= {line
=3, pos
=1}
1137 Editor_state
.screen_top1
= {line
=1, pos
=1}
1138 edit
.draw(Editor_state
)
1139 local y
= Editor_state
.top
1140 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1141 y
= y
+ Editor_state
.line_height
1142 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1143 y
= y
+ Editor_state
.line_height
1144 App
.screen
.check(y
, 'ghij', 'baseline/screen:3')
1145 -- after hitting pagedown the screen scrolls down to start of a long line
1146 edit
.run_after_keychord(Editor_state
, 'pagedown', 'pagedown')
1147 check_eq(Editor_state
.screen_top1
.line
, 3, 'baseline2/screen_top')
1148 check_eq(Editor_state
.cursor1
.line
, 3, 'baseline2/cursor:line')
1149 check_eq(Editor_state
.cursor1
.pos
, 1, 'baseline2/cursor:pos')
1150 -- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll up
1151 edit
.run_after_keychord(Editor_state
, 'down', 'down')
1152 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top')
1153 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1154 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1155 y
= Editor_state
.top
1156 App
.screen
.check(y
, 'ghij', 'screen:1')
1157 y
= y
+ Editor_state
.line_height
1158 App
.screen
.check(y
, 'kl', 'screen:2')
1159 y
= y
+ Editor_state
.line_height
1160 App
.screen
.check(y
, 'mno', 'screen:3')
1163 function test_up_arrow_moves_cursor()
1164 -- display the first 3 lines with the cursor on the bottom line
1165 App
.screen
.init
{width
=120, height
=60}
1166 Editor_state
= edit
.initialize_test_state()
1167 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1168 Text
.redraw_all(Editor_state
)
1169 Editor_state
.cursor1
= {line
=3, pos
=1}
1170 Editor_state
.screen_top1
= {line
=1, pos
=1}
1171 edit
.draw(Editor_state
)
1172 local y
= Editor_state
.top
1173 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1174 y
= y
+ Editor_state
.line_height
1175 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1176 y
= y
+ Editor_state
.line_height
1177 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1178 -- after hitting the up arrow the cursor moves up by 1 line
1179 edit
.run_after_keychord(Editor_state
, 'up', 'up')
1180 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1181 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor')
1182 -- the screen is unchanged
1183 y
= Editor_state
.top
1184 App
.screen
.check(y
, 'abc', 'screen:1')
1185 y
= y
+ Editor_state
.line_height
1186 App
.screen
.check(y
, 'def', 'screen:2')
1187 y
= y
+ Editor_state
.line_height
1188 App
.screen
.check(y
, 'ghi', 'screen:3')
1191 function test_up_arrow_scrolls_up_by_one_line()
1192 -- display the lines 2/3/4 with the cursor on line 2
1193 App
.screen
.init
{width
=120, height
=60}
1194 Editor_state
= edit
.initialize_test_state()
1195 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1196 Text
.redraw_all(Editor_state
)
1197 Editor_state
.cursor1
= {line
=2, pos
=1}
1198 Editor_state
.screen_top1
= {line
=2, pos
=1}
1199 edit
.draw(Editor_state
)
1200 local y
= Editor_state
.top
1201 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1202 y
= y
+ Editor_state
.line_height
1203 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1204 y
= y
+ Editor_state
.line_height
1205 App
.screen
.check(y
, 'jkl', 'baseline/screen:3')
1206 -- after hitting the up arrow the screen scrolls up by one line
1207 edit
.run_after_keychord(Editor_state
, 'up', 'up')
1208 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1209 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1210 y
= Editor_state
.top
1211 App
.screen
.check(y
, 'abc', 'screen:1')
1212 y
= y
+ Editor_state
.line_height
1213 App
.screen
.check(y
, 'def', 'screen:2')
1214 y
= y
+ Editor_state
.line_height
1215 App
.screen
.check(y
, 'ghi', 'screen:3')
1218 function test_up_arrow_scrolls_up_by_one_screen_line()
1219 -- display lines starting from second screen line of a line
1220 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1221 Editor_state
= edit
.initialize_test_state()
1222 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1223 Text
.redraw_all(Editor_state
)
1224 Editor_state
.cursor1
= {line
=3, pos
=6}
1225 Editor_state
.screen_top1
= {line
=3, pos
=5}
1226 edit
.draw(Editor_state
)
1227 local y
= Editor_state
.top
1228 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1229 y
= y
+ Editor_state
.line_height
1230 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1231 -- after hitting the up arrow the screen scrolls up to first screen line
1232 edit
.run_after_keychord(Editor_state
, 'up', 'up')
1233 y
= Editor_state
.top
1234 App
.screen
.check(y
, 'ghi ', 'screen:1')
1235 y
= y
+ Editor_state
.line_height
1236 App
.screen
.check(y
, 'jkl', 'screen:2')
1237 y
= y
+ Editor_state
.line_height
1238 App
.screen
.check(y
, 'mno', 'screen:3')
1239 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1240 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1241 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1242 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1245 function test_up_arrow_scrolls_up_to_final_screen_line()
1246 -- display lines starting just after a long line
1247 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1248 Editor_state
= edit
.initialize_test_state()
1249 Editor_state
.lines
= load_array
{'abc def', 'ghi', 'jkl', 'mno'}
1250 Text
.redraw_all(Editor_state
)
1251 Editor_state
.cursor1
= {line
=2, pos
=1}
1252 Editor_state
.screen_top1
= {line
=2, pos
=1}
1253 edit
.draw(Editor_state
)
1254 local y
= Editor_state
.top
1255 App
.screen
.check(y
, 'ghi', 'baseline/screen:1')
1256 y
= y
+ Editor_state
.line_height
1257 App
.screen
.check(y
, 'jkl', 'baseline/screen:2')
1258 y
= y
+ Editor_state
.line_height
1259 App
.screen
.check(y
, 'mno', 'baseline/screen:3')
1260 -- after hitting the up arrow the screen scrolls up to final screen line of previous line
1261 edit
.run_after_keychord(Editor_state
, 'up', 'up')
1262 y
= Editor_state
.top
1263 App
.screen
.check(y
, 'def', 'screen:1')
1264 y
= y
+ Editor_state
.line_height
1265 App
.screen
.check(y
, 'ghi', 'screen:2')
1266 y
= y
+ Editor_state
.line_height
1267 App
.screen
.check(y
, 'jkl', 'screen:3')
1268 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
1269 check_eq(Editor_state
.screen_top1
.pos
, 5, 'screen_top:pos')
1270 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1271 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1274 function test_up_arrow_scrolls_up_to_empty_line()
1275 -- display a screenful of text with an empty line just above it outside the screen
1276 App
.screen
.init
{width
=120, height
=60}
1277 Editor_state
= edit
.initialize_test_state()
1278 Editor_state
.lines
= load_array
{'', 'abc', 'def', 'ghi', 'jkl'}
1279 Text
.redraw_all(Editor_state
)
1280 Editor_state
.cursor1
= {line
=2, pos
=1}
1281 Editor_state
.screen_top1
= {line
=2, pos
=1}
1282 edit
.draw(Editor_state
)
1283 local y
= Editor_state
.top
1284 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1285 y
= y
+ Editor_state
.line_height
1286 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1287 y
= y
+ Editor_state
.line_height
1288 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1289 -- after hitting the up arrow the screen scrolls up by one line
1290 edit
.run_after_keychord(Editor_state
, 'up', 'up')
1291 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1292 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1293 y
= Editor_state
.top
1295 y
= y
+ Editor_state
.line_height
1296 App
.screen
.check(y
, 'abc', 'screen:2')
1297 y
= y
+ Editor_state
.line_height
1298 App
.screen
.check(y
, 'def', 'screen:3')
1301 function test_pageup()
1302 App
.screen
.init
{width
=120, height
=45}
1303 Editor_state
= edit
.initialize_test_state()
1304 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi'}
1305 Text
.redraw_all(Editor_state
)
1306 Editor_state
.cursor1
= {line
=2, pos
=1}
1307 Editor_state
.screen_top1
= {line
=2, pos
=1}
1308 -- initially the last two lines are displayed
1309 edit
.draw(Editor_state
)
1310 local y
= Editor_state
.top
1311 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1312 y
= y
+ Editor_state
.line_height
1313 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1314 -- after pageup the cursor goes to first line
1315 edit
.run_after_keychord(Editor_state
, 'pageup', 'pageup')
1316 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1317 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1318 y
= Editor_state
.top
1319 App
.screen
.check(y
, 'abc', 'screen:1')
1320 y
= y
+ Editor_state
.line_height
1321 App
.screen
.check(y
, 'def', 'screen:2')
1324 function test_pageup_scrolls_up_by_screen_line()
1325 -- display the first three lines with the cursor on the bottom line
1326 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1327 Editor_state
= edit
.initialize_test_state()
1328 Editor_state
.lines
= load_array
{'abc def', 'ghi', 'jkl', 'mno'}
1329 Text
.redraw_all(Editor_state
)
1330 Editor_state
.cursor1
= {line
=2, pos
=1}
1331 Editor_state
.screen_top1
= {line
=2, pos
=1}
1332 edit
.draw(Editor_state
)
1333 local y
= Editor_state
.top
1334 App
.screen
.check(y
, 'ghi', 'baseline/screen:1')
1335 y
= y
+ Editor_state
.line_height
1336 App
.screen
.check(y
, 'jkl', 'baseline/screen:2')
1337 y
= y
+ Editor_state
.line_height
1338 App
.screen
.check(y
, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1339 -- after hitting the page-up key the screen scrolls up to top
1340 edit
.run_after_keychord(Editor_state
, 'pageup', 'pageup')
1341 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1342 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1343 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1344 y
= Editor_state
.top
1345 App
.screen
.check(y
, 'abc ', 'screen:1')
1346 y
= y
+ Editor_state
.line_height
1347 App
.screen
.check(y
, 'def', 'screen:2')
1348 y
= y
+ Editor_state
.line_height
1349 App
.screen
.check(y
, 'ghi', 'screen:3')
1352 function test_pageup_scrolls_up_from_middle_screen_line()
1353 -- display a few lines starting from the middle of a line (Editor_state.cursor1.pos > 1)
1354 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1355 Editor_state
= edit
.initialize_test_state()
1356 Editor_state
.lines
= load_array
{'abc def', 'ghi jkl', 'mno'}
1357 Text
.redraw_all(Editor_state
)
1358 Editor_state
.cursor1
= {line
=2, pos
=5}
1359 Editor_state
.screen_top1
= {line
=2, pos
=5}
1360 edit
.draw(Editor_state
)
1361 local y
= Editor_state
.top
1362 App
.screen
.check(y
, 'jkl', 'baseline/screen:2')
1363 y
= y
+ Editor_state
.line_height
1364 App
.screen
.check(y
, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1365 -- after hitting the page-up key the screen scrolls up to top
1366 edit
.run_after_keychord(Editor_state
, 'pageup', 'pageup')
1367 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1368 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1369 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1370 y
= Editor_state
.top
1371 App
.screen
.check(y
, 'abc ', 'screen:1')
1372 y
= y
+ Editor_state
.line_height
1373 App
.screen
.check(y
, 'def', 'screen:2')
1374 y
= y
+ Editor_state
.line_height
1375 App
.screen
.check(y
, 'ghi ', 'screen:3')
1378 function test_enter_on_bottom_line_scrolls_down()
1379 -- display a few lines with cursor on bottom line
1380 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1381 Editor_state
= edit
.initialize_test_state()
1382 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1383 Text
.redraw_all(Editor_state
)
1384 Editor_state
.cursor1
= {line
=3, pos
=2}
1385 Editor_state
.screen_top1
= {line
=1, pos
=1}
1386 edit
.draw(Editor_state
)
1387 local y
= Editor_state
.top
1388 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1389 y
= y
+ Editor_state
.line_height
1390 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1391 y
= y
+ Editor_state
.line_height
1392 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1393 -- after hitting the enter key the screen scrolls down
1394 edit
.run_after_keychord(Editor_state
, 'return', 'return')
1395 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1396 check_eq(Editor_state
.cursor1
.line
, 4, 'cursor:line')
1397 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1398 y
= Editor_state
.top
1399 App
.screen
.check(y
, 'def', 'screen:1')
1400 y
= y
+ Editor_state
.line_height
1401 App
.screen
.check(y
, 'g', 'screen:2')
1402 y
= y
+ Editor_state
.line_height
1403 App
.screen
.check(y
, 'hi', 'screen:3')
1406 function test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1407 -- display just the bottom line on screen
1408 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1409 Editor_state
= edit
.initialize_test_state()
1410 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1411 Text
.redraw_all(Editor_state
)
1412 Editor_state
.cursor1
= {line
=4, pos
=2}
1413 Editor_state
.screen_top1
= {line
=4, pos
=1}
1414 edit
.draw(Editor_state
)
1415 local y
= Editor_state
.top
1416 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1417 -- after hitting the enter key the screen does not scroll down
1418 edit
.run_after_keychord(Editor_state
, 'return', 'return')
1419 check_eq(Editor_state
.screen_top1
.line
, 4, 'screen_top')
1420 check_eq(Editor_state
.cursor1
.line
, 5, 'cursor:line')
1421 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1422 y
= Editor_state
.top
1423 App
.screen
.check(y
, 'j', 'screen:1')
1424 y
= y
+ Editor_state
.line_height
1425 App
.screen
.check(y
, 'kl', 'screen:2')
1428 function test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1429 -- display just an empty bottom line on screen
1430 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1431 Editor_state
= edit
.initialize_test_state()
1432 Editor_state
.lines
= load_array
{'abc', ''}
1433 Text
.redraw_all(Editor_state
)
1434 Editor_state
.cursor1
= {line
=2, pos
=1}
1435 Editor_state
.screen_top1
= {line
=2, pos
=1}
1436 edit
.draw(Editor_state
)
1437 -- after hitting the inserting_text key the screen does not scroll down
1438 edit
.run_after_text_input(Editor_state
, 'a')
1439 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1440 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1441 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
1442 local y
= Editor_state
.top
1443 App
.screen
.check(y
, 'a', 'screen:1')
1446 function test_typing_on_bottom_line_scrolls_down()
1447 -- display a few lines with cursor on bottom line
1448 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1449 Editor_state
= edit
.initialize_test_state()
1450 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1451 Text
.redraw_all(Editor_state
)
1452 Editor_state
.cursor1
= {line
=3, pos
=4}
1453 Editor_state
.screen_top1
= {line
=1, pos
=1}
1454 edit
.draw(Editor_state
)
1455 local y
= Editor_state
.top
1456 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1457 y
= y
+ Editor_state
.line_height
1458 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1459 y
= y
+ Editor_state
.line_height
1460 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1461 -- after typing something the line wraps and the screen scrolls down
1462 edit
.run_after_text_input(Editor_state
, 'j')
1463 edit
.run_after_text_input(Editor_state
, 'k')
1464 edit
.run_after_text_input(Editor_state
, 'l')
1465 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1466 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1467 check_eq(Editor_state
.cursor1
.pos
, 7, 'cursor:pos')
1468 y
= Editor_state
.top
1469 App
.screen
.check(y
, 'def', 'screen:1')
1470 y
= y
+ Editor_state
.line_height
1471 App
.screen
.check(y
, 'ghij', 'screen:2')
1472 y
= y
+ Editor_state
.line_height
1473 App
.screen
.check(y
, 'kl', 'screen:3')
1476 function test_left_arrow_scrolls_up_in_wrapped_line()
1477 -- display lines starting from second screen line of a line
1478 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1479 Editor_state
= edit
.initialize_test_state()
1480 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1481 Text
.redraw_all(Editor_state
)
1482 Editor_state
.screen_top1
= {line
=3, pos
=5}
1483 -- cursor is at top of screen
1484 Editor_state
.cursor1
= {line
=3, pos
=5}
1485 edit
.draw(Editor_state
)
1486 local y
= Editor_state
.top
1487 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1488 y
= y
+ Editor_state
.line_height
1489 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1490 -- after hitting the left arrow the screen scrolls up to first screen line
1491 edit
.run_after_keychord(Editor_state
, 'left', 'left')
1492 y
= Editor_state
.top
1493 App
.screen
.check(y
, 'ghi ', 'screen:1')
1494 y
= y
+ Editor_state
.line_height
1495 App
.screen
.check(y
, 'jkl', 'screen:2')
1496 y
= y
+ Editor_state
.line_height
1497 App
.screen
.check(y
, 'mno', 'screen:3')
1498 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1499 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1500 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1501 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
1504 function test_right_arrow_scrolls_down_in_wrapped_line()
1505 -- display the first three lines with the cursor on the bottom line
1506 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1507 Editor_state
= edit
.initialize_test_state()
1508 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1509 Text
.redraw_all(Editor_state
)
1510 Editor_state
.screen_top1
= {line
=1, pos
=1}
1511 -- cursor is at bottom right of screen
1512 Editor_state
.cursor1
= {line
=3, pos
=5}
1513 edit
.draw(Editor_state
)
1514 local y
= Editor_state
.top
1515 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1516 y
= y
+ Editor_state
.line_height
1517 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1518 y
= y
+ Editor_state
.line_height
1519 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1520 -- after hitting the right arrow the screen scrolls down by one line
1521 edit
.run_after_keychord(Editor_state
, 'right', 'right')
1522 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1523 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1524 check_eq(Editor_state
.cursor1
.pos
, 6, 'cursor:pos')
1525 y
= Editor_state
.top
1526 App
.screen
.check(y
, 'def', 'screen:1')
1527 y
= y
+ Editor_state
.line_height
1528 App
.screen
.check(y
, 'ghi ', 'screen:2')
1529 y
= y
+ Editor_state
.line_height
1530 App
.screen
.check(y
, 'jkl', 'screen:3')
1533 function test_home_scrolls_up_in_wrapped_line()
1534 -- display lines starting from second screen line of a line
1535 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1536 Editor_state
= edit
.initialize_test_state()
1537 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1538 Text
.redraw_all(Editor_state
)
1539 Editor_state
.screen_top1
= {line
=3, pos
=5}
1540 -- cursor is at top of screen
1541 Editor_state
.cursor1
= {line
=3, pos
=5}
1542 edit
.draw(Editor_state
)
1543 local y
= Editor_state
.top
1544 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1545 y
= y
+ Editor_state
.line_height
1546 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1547 -- after hitting home the screen scrolls up to first screen line
1548 edit
.run_after_keychord(Editor_state
, 'home', 'home')
1549 y
= Editor_state
.top
1550 App
.screen
.check(y
, 'ghi ', 'screen:1')
1551 y
= y
+ Editor_state
.line_height
1552 App
.screen
.check(y
, 'jkl', 'screen:2')
1553 y
= y
+ Editor_state
.line_height
1554 App
.screen
.check(y
, 'mno', 'screen:3')
1555 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1556 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1557 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1558 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1561 function test_end_scrolls_down_in_wrapped_line()
1562 -- display the first three lines with the cursor on the bottom line
1563 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1564 Editor_state
= edit
.initialize_test_state()
1565 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1566 Text
.redraw_all(Editor_state
)
1567 Editor_state
.screen_top1
= {line
=1, pos
=1}
1568 -- cursor is at bottom right of screen
1569 Editor_state
.cursor1
= {line
=3, pos
=5}
1570 edit
.draw(Editor_state
)
1571 local y
= Editor_state
.top
1572 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1573 y
= y
+ Editor_state
.line_height
1574 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1575 y
= y
+ Editor_state
.line_height
1576 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1577 -- after hitting end the screen scrolls down by one line
1578 edit
.run_after_keychord(Editor_state
, 'end', 'end')
1579 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1580 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1581 check_eq(Editor_state
.cursor1
.pos
, 8, 'cursor:pos')
1582 y
= Editor_state
.top
1583 App
.screen
.check(y
, 'def', 'screen:1')
1584 y
= y
+ Editor_state
.line_height
1585 App
.screen
.check(y
, 'ghi ', 'screen:2')
1586 y
= y
+ Editor_state
.line_height
1587 App
.screen
.check(y
, 'jkl', 'screen:3')
1590 function test_position_cursor_on_recently_edited_wrapping_line()
1591 -- draw a line wrapping over 2 screen lines
1592 App
.screen
.init
{width
=100, height
=200}
1593 Editor_state
= edit
.initialize_test_state()
1594 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr ', 'xyz'}
1595 Text
.redraw_all(Editor_state
)
1596 Editor_state
.cursor1
= {line
=1, pos
=25}
1597 Editor_state
.screen_top1
= {line
=1, pos
=1}
1598 edit
.draw(Editor_state
)
1599 local y
= Editor_state
.top
1600 App
.screen
.check(y
, 'abc def ghi ', 'baseline1/screen:1')
1601 y
= y
+ Editor_state
.line_height
1602 App
.screen
.check(y
, 'jkl mno pqr ', 'baseline1/screen:2')
1603 y
= y
+ Editor_state
.line_height
1604 App
.screen
.check(y
, 'xyz', 'baseline1/screen:3')
1605 -- add to the line until it's wrapping over 3 screen lines
1606 edit
.run_after_text_input(Editor_state
, 's')
1607 edit
.run_after_text_input(Editor_state
, 't')
1608 edit
.run_after_text_input(Editor_state
, 'u')
1609 check_eq(Editor_state
.cursor1
.pos
, 28, 'cursor:pos')
1610 y
= Editor_state
.top
1611 App
.screen
.check(y
, 'abc def ghi ', 'baseline2/screen:1')
1612 y
= y
+ Editor_state
.line_height
1613 App
.screen
.check(y
, 'jkl mno pqr ', 'baseline2/screen:2')
1614 y
= y
+ Editor_state
.line_height
1615 App
.screen
.check(y
, 'stu', 'baseline2/screen:3')
1616 -- try to move the cursor earlier in the third screen line by clicking the mouse
1617 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+2,Editor_state
.top
+Editor_state
.line_height
*2+5, 1)
1618 -- cursor should move
1619 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1620 check_eq(Editor_state
.cursor1
.pos
, 25, 'cursor:pos')
1623 function test_backspace_can_scroll_up()
1624 -- display the lines 2/3/4 with the cursor on line 2
1625 App
.screen
.init
{width
=120, height
=60}
1626 Editor_state
= edit
.initialize_test_state()
1627 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1628 Text
.redraw_all(Editor_state
)
1629 Editor_state
.cursor1
= {line
=2, pos
=1}
1630 Editor_state
.screen_top1
= {line
=2, pos
=1}
1631 edit
.draw(Editor_state
)
1632 local y
= Editor_state
.top
1633 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1634 y
= y
+ Editor_state
.line_height
1635 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1636 y
= y
+ Editor_state
.line_height
1637 App
.screen
.check(y
, 'jkl', 'baseline/screen:3')
1638 -- after hitting backspace the screen scrolls up by one line
1639 edit
.run_after_keychord(Editor_state
, 'backspace', 'backspace')
1640 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1641 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1642 y
= Editor_state
.top
1643 App
.screen
.check(y
, 'abcdef', 'screen:1')
1644 y
= y
+ Editor_state
.line_height
1645 App
.screen
.check(y
, 'ghi', 'screen:2')
1646 y
= y
+ Editor_state
.line_height
1647 App
.screen
.check(y
, 'jkl', 'screen:3')
1650 function test_backspace_can_scroll_up_screen_line()
1651 -- display lines starting from second screen line of a line
1652 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1653 Editor_state
= edit
.initialize_test_state()
1654 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1655 Text
.redraw_all(Editor_state
)
1656 Editor_state
.cursor1
= {line
=3, pos
=5}
1657 Editor_state
.screen_top1
= {line
=3, pos
=5}
1658 edit
.draw(Editor_state
)
1659 local y
= Editor_state
.top
1660 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1661 y
= y
+ Editor_state
.line_height
1662 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1663 -- after hitting backspace the screen scrolls up by one screen line
1664 edit
.run_after_keychord(Editor_state
, 'backspace', 'backspace')
1665 y
= Editor_state
.top
1666 App
.screen
.check(y
, 'ghij', 'screen:1')
1667 y
= y
+ Editor_state
.line_height
1668 App
.screen
.check(y
, 'kl', 'screen:2')
1669 y
= y
+ Editor_state
.line_height
1670 App
.screen
.check(y
, 'mno', 'screen:3')
1671 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1672 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1673 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1674 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
1677 function test_backspace_past_line_boundary()
1678 -- position cursor at start of a (non-first) line
1679 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1680 Editor_state
= edit
.initialize_test_state()
1681 Editor_state
.lines
= load_array
{'abc', 'def'}
1682 Text
.redraw_all(Editor_state
)
1683 Editor_state
.cursor1
= {line
=2, pos
=1}
1684 -- backspace joins with previous line
1685 edit
.run_after_keychord(Editor_state
, 'backspace', 'backspace')
1686 check_eq(Editor_state
.lines
[1].data
, 'abcdef', 'check')
1689 -- some tests for operating over selections created using Shift- chords
1690 -- we're just testing delete_selection, and it works the same for all keys
1692 function test_backspace_over_selection()
1693 -- select just one character within a line with cursor before selection
1694 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1695 Editor_state
= edit
.initialize_test_state()
1696 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1697 Text
.redraw_all(Editor_state
)
1698 Editor_state
.cursor1
= {line
=1, pos
=1}
1699 Editor_state
.selection1
= {line
=1, pos
=2}
1700 -- backspace deletes the selected character, even though it's after the cursor
1701 edit
.run_after_keychord(Editor_state
, 'backspace', 'backspace')
1702 check_eq(Editor_state
.lines
[1].data
, 'bc', 'data')
1703 -- cursor (remains) at start of selection
1704 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1705 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1706 -- selection is cleared
1707 check_nil(Editor_state
.selection1
.line
, 'selection')
1710 function test_backspace_over_selection_reverse()
1711 -- select just one character within a line with cursor after selection
1712 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1713 Editor_state
= edit
.initialize_test_state()
1714 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1715 Text
.redraw_all(Editor_state
)
1716 Editor_state
.cursor1
= {line
=1, pos
=2}
1717 Editor_state
.selection1
= {line
=1, pos
=1}
1718 -- backspace deletes the selected character
1719 edit
.run_after_keychord(Editor_state
, 'backspace', 'backspace')
1720 check_eq(Editor_state
.lines
[1].data
, 'bc', 'data')
1721 -- cursor moves to start of selection
1722 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1723 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1724 -- selection is cleared
1725 check_nil(Editor_state
.selection1
.line
, 'selection')
1728 function test_backspace_over_multiple_lines()
1729 -- select just one character within a line with cursor after selection
1730 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1731 Editor_state
= edit
.initialize_test_state()
1732 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1733 Text
.redraw_all(Editor_state
)
1734 Editor_state
.cursor1
= {line
=1, pos
=2}
1735 Editor_state
.selection1
= {line
=4, pos
=2}
1736 -- backspace deletes the region and joins the remaining portions of lines on either side
1737 edit
.run_after_keychord(Editor_state
, 'backspace', 'backspace')
1738 check_eq(Editor_state
.lines
[1].data
, 'akl', 'data:1')
1739 check_eq(Editor_state
.lines
[2].data
, 'mno', 'data:2')
1740 -- cursor remains at start of selection
1741 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1742 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
1743 -- selection is cleared
1744 check_nil(Editor_state
.selection1
.line
, 'selection')
1747 function test_backspace_to_end_of_line()
1748 -- select region from cursor to end of line
1749 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1750 Editor_state
= edit
.initialize_test_state()
1751 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1752 Text
.redraw_all(Editor_state
)
1753 Editor_state
.cursor1
= {line
=1, pos
=2}
1754 Editor_state
.selection1
= {line
=1, pos
=4}
1755 -- backspace deletes rest of line without joining to any other line
1756 edit
.run_after_keychord(Editor_state
, 'backspace', 'backspace')
1757 check_eq(Editor_state
.lines
[1].data
, 'a', 'data:1')
1758 check_eq(Editor_state
.lines
[2].data
, 'def', 'data:2')
1759 -- cursor remains at start of selection
1760 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1761 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
1762 -- selection is cleared
1763 check_nil(Editor_state
.selection1
.line
, 'selection')
1766 function test_backspace_to_start_of_line()
1767 -- select region from cursor to start of line
1768 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1769 Editor_state
= edit
.initialize_test_state()
1770 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1771 Text
.redraw_all(Editor_state
)
1772 Editor_state
.cursor1
= {line
=2, pos
=1}
1773 Editor_state
.selection1
= {line
=2, pos
=3}
1774 -- backspace deletes beginning of line without joining to any other line
1775 edit
.run_after_keychord(Editor_state
, 'backspace', 'backspace')
1776 check_eq(Editor_state
.lines
[1].data
, 'abc', 'data:1')
1777 check_eq(Editor_state
.lines
[2].data
, 'f', 'data:2')
1778 -- cursor remains at start of selection
1779 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1780 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1781 -- selection is cleared
1782 check_nil(Editor_state
.selection1
.line
, 'selection')
1785 function test_undo_insert_text()
1786 App
.screen
.init
{width
=120, height
=60}
1787 Editor_state
= edit
.initialize_test_state()
1788 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
1789 Text
.redraw_all(Editor_state
)
1790 Editor_state
.cursor1
= {line
=2, pos
=4}
1791 Editor_state
.screen_top1
= {line
=1, pos
=1}
1792 -- insert a character
1793 edit
.draw(Editor_state
)
1794 edit
.run_after_text_input(Editor_state
, 'g')
1795 check_eq(Editor_state
.cursor1
.line
, 2, 'baseline/cursor:line')
1796 check_eq(Editor_state
.cursor1
.pos
, 5, 'baseline/cursor:pos')
1797 check_nil(Editor_state
.selection1
.line
, 'baseline/selection:line')
1798 check_nil(Editor_state
.selection1
.pos
, 'baseline/selection:pos')
1799 local y
= Editor_state
.top
1800 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1801 y
= y
+ Editor_state
.line_height
1802 App
.screen
.check(y
, 'defg', 'baseline/screen:2')
1803 y
= y
+ Editor_state
.line_height
1804 App
.screen
.check(y
, 'xyz', 'baseline/screen:3')
1806 edit
.run_after_keychord(Editor_state
, 'C-z', 'z')
1807 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1808 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
1809 check_nil(Editor_state
.selection1
.line
, 'selection:line')
1810 check_nil(Editor_state
.selection1
.pos
, 'selection:pos')
1811 y
= Editor_state
.top
1812 App
.screen
.check(y
, 'abc', 'screen:1')
1813 y
= y
+ Editor_state
.line_height
1814 App
.screen
.check(y
, 'def', 'screen:2')
1815 y
= y
+ Editor_state
.line_height
1816 App
.screen
.check(y
, 'xyz', 'screen:3')
1819 function test_undo_delete_text()
1820 App
.screen
.init
{width
=120, height
=60}
1821 Editor_state
= edit
.initialize_test_state()
1822 Editor_state
.lines
= load_array
{'abc', 'defg', 'xyz'}
1823 Text
.redraw_all(Editor_state
)
1824 Editor_state
.cursor1
= {line
=2, pos
=5}
1825 Editor_state
.screen_top1
= {line
=1, pos
=1}
1826 -- delete a character
1827 edit
.run_after_keychord(Editor_state
, 'backspace', 'backspace')
1828 check_eq(Editor_state
.cursor1
.line
, 2, 'baseline/cursor:line')
1829 check_eq(Editor_state
.cursor1
.pos
, 4, 'baseline/cursor:pos')
1830 check_nil(Editor_state
.selection1
.line
, 'baseline/selection:line')
1831 check_nil(Editor_state
.selection1
.pos
, 'baseline/selection:pos')
1832 local y
= Editor_state
.top
1833 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1834 y
= y
+ Editor_state
.line_height
1835 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1836 y
= y
+ Editor_state
.line_height
1837 App
.screen
.check(y
, 'xyz', 'baseline/screen:3')
1839 --? -- after undo, the backspaced key is selected
1840 edit
.run_after_keychord(Editor_state
, 'C-z', 'z')
1841 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1842 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1843 check_nil(Editor_state
.selection1
.line
, 'selection:line')
1844 check_nil(Editor_state
.selection1
.pos
, 'selection:pos')
1845 --? check_eq(Editor_state.selection1.line, 2, 'selection:line')
1846 --? check_eq(Editor_state.selection1.pos, 4, 'selection:pos')
1847 y
= Editor_state
.top
1848 App
.screen
.check(y
, 'abc', 'screen:1')
1849 y
= y
+ Editor_state
.line_height
1850 App
.screen
.check(y
, 'defg', 'screen:2')
1851 y
= y
+ Editor_state
.line_height
1852 App
.screen
.check(y
, 'xyz', 'screen:3')
1855 function test_undo_restores_selection()
1856 -- display a line of text with some part selected
1857 App
.screen
.init
{width
=75, height
=80}
1858 Editor_state
= edit
.initialize_test_state()
1859 Editor_state
.lines
= load_array
{'abc'}
1860 Text
.redraw_all(Editor_state
)
1861 Editor_state
.cursor1
= {line
=1, pos
=1}
1862 Editor_state
.selection1
= {line
=1, pos
=2}
1863 Editor_state
.screen_top1
= {line
=1, pos
=1}
1864 edit
.draw(Editor_state
)
1865 -- delete selected text
1866 edit
.run_after_text_input(Editor_state
, 'x')
1867 check_eq(Editor_state
.lines
[1].data
, 'xbc', 'baseline')
1868 check_nil(Editor_state
.selection1
.line
, 'baseline:selection')
1870 edit
.run_after_keychord(Editor_state
, 'C-z', 'z')
1871 edit
.run_after_keychord(Editor_state
, 'C-z', 'z')
1872 -- selection is restored
1873 check_eq(Editor_state
.selection1
.line
, 1, 'line')
1874 check_eq(Editor_state
.selection1
.pos
, 2, 'pos')
1877 function test_search()
1878 App
.screen
.init
{width
=120, height
=60}
1879 Editor_state
= edit
.initialize_test_state()
1880 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', '’deg'} -- contains unicode quote in final line
1881 Text
.redraw_all(Editor_state
)
1882 Editor_state
.cursor1
= {line
=1, pos
=1}
1883 Editor_state
.screen_top1
= {line
=1, pos
=1}
1884 edit
.draw(Editor_state
)
1885 -- search for a string
1886 edit
.run_after_keychord(Editor_state
, 'C-f', 'f')
1887 edit
.run_after_text_input(Editor_state
, 'd')
1888 edit
.run_after_keychord(Editor_state
, 'return', 'return')
1889 check_eq(Editor_state
.cursor1
.line
, 2, '1/cursor:line')
1890 check_eq(Editor_state
.cursor1
.pos
, 1, '1/cursor:pos')
1892 Editor_state
.cursor1
= {line
=1, pos
=1}
1893 Editor_state
.screen_top1
= {line
=1, pos
=1}
1894 -- search for second occurrence
1895 edit
.run_after_keychord(Editor_state
, 'C-f', 'f')
1896 edit
.run_after_text_input(Editor_state
, 'de')
1897 edit
.run_after_keychord(Editor_state
, 'down', 'down')
1898 edit
.run_after_keychord(Editor_state
, 'return', 'return')
1899 check_eq(Editor_state
.cursor1
.line
, 4, '2/cursor:line')
1900 check_eq(Editor_state
.cursor1
.pos
, 2, '2/cursor:pos')
1903 function test_search_upwards()
1904 App
.screen
.init
{width
=120, height
=60}
1905 Editor_state
= edit
.initialize_test_state()
1906 Editor_state
.lines
= load_array
{'’abc', 'abd'} -- contains unicode quote
1907 Text
.redraw_all(Editor_state
)
1908 Editor_state
.cursor1
= {line
=2, pos
=1}
1909 Editor_state
.screen_top1
= {line
=1, pos
=1}
1910 edit
.draw(Editor_state
)
1911 -- search for a string
1912 edit
.run_after_keychord(Editor_state
, 'C-f', 'f')
1913 edit
.run_after_text_input(Editor_state
, 'a')
1914 -- search for previous occurrence
1915 edit
.run_after_keychord(Editor_state
, 'up', 'up')
1916 check_eq(Editor_state
.cursor1
.line
, 1, '2/cursor:line')
1917 check_eq(Editor_state
.cursor1
.pos
, 2, '2/cursor:pos')
1920 function test_search_wrap()
1921 App
.screen
.init
{width
=120, height
=60}
1922 Editor_state
= edit
.initialize_test_state()
1923 Editor_state
.lines
= load_array
{'’abc', 'def'} -- contains unicode quote in first line
1924 Text
.redraw_all(Editor_state
)
1925 Editor_state
.cursor1
= {line
=2, pos
=1}
1926 Editor_state
.screen_top1
= {line
=1, pos
=1}
1927 edit
.draw(Editor_state
)
1928 -- search for a string
1929 edit
.run_after_keychord(Editor_state
, 'C-f', 'f')
1930 edit
.run_after_text_input(Editor_state
, 'a')
1931 edit
.run_after_keychord(Editor_state
, 'return', 'return')
1933 check_eq(Editor_state
.cursor1
.line
, 1, '1/cursor:line')
1934 check_eq(Editor_state
.cursor1
.pos
, 2, '1/cursor:pos')
1937 function test_search_wrap_upwards()
1938 App
.screen
.init
{width
=120, height
=60}
1939 Editor_state
= edit
.initialize_test_state()
1940 Editor_state
.lines
= load_array
{'abc ’abd'} -- contains unicode quote
1941 Text
.redraw_all(Editor_state
)
1942 Editor_state
.cursor1
= {line
=1, pos
=1}
1943 Editor_state
.screen_top1
= {line
=1, pos
=1}
1944 edit
.draw(Editor_state
)
1945 -- search upwards for a string
1946 edit
.run_after_keychord(Editor_state
, 'C-f', 'f')
1947 edit
.run_after_text_input(Editor_state
, 'a')
1948 edit
.run_after_keychord(Editor_state
, 'up', 'up')
1950 check_eq(Editor_state
.cursor1
.line
, 1, '1/cursor:line')
1951 check_eq(Editor_state
.cursor1
.pos
, 6, '1/cursor:pos')