1 -- major tests for text editing flows
3 function test_initial_state()
4 App
.screen
.init
{width
=120, height
=60}
5 Editor_state
= edit
.initialize_test_state()
6 Editor_state
.lines
= load_array
{}
7 Text
.redraw_all(Editor_state
)
8 edit
.draw(Editor_state
)
9 check_eq(#Editor_state
.lines
, 1, '#lines')
10 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
11 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
12 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
13 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
16 function test_click_to_create_drawing()
17 App
.screen
.init
{width
=120, height
=60}
18 Editor_state
= edit
.initialize_test_state()
19 Editor_state
.lines
= load_array
{}
20 Text
.redraw_all(Editor_state
)
21 edit
.draw(Editor_state
)
22 edit
.run_after_mouse_click(Editor_state
, 8,Editor_state
.top
+8, 1)
23 -- cursor skips drawing to always remain on text
24 check_eq(#Editor_state
.lines
, 2, '#lines')
25 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor')
28 function test_backspace_to_delete_drawing()
29 -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
30 App
.screen
.init
{width
=120, height
=60}
31 Editor_state
= edit
.initialize_test_state()
32 Editor_state
.lines
= load_array
{'```lines', '```', ''}
33 Text
.redraw_all(Editor_state
)
34 -- cursor is on text as always (outside tests this will get initialized correctly)
35 Editor_state
.cursor1
.line
= 2
36 -- backspacing deletes the drawing
37 edit
.run_after_keychord(Editor_state
, 'backspace')
38 check_eq(#Editor_state
.lines
, 1, '#lines')
39 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
42 function test_backspace_from_start_of_final_line()
43 -- display final line of text with cursor at start of it
44 App
.screen
.init
{width
=120, height
=60}
45 Editor_state
= edit
.initialize_test_state()
46 Editor_state
.lines
= load_array
{'abc', 'def'}
47 Editor_state
.screen_top1
= {line
=2, pos
=1}
48 Editor_state
.cursor1
= {line
=2, pos
=1}
49 Text
.redraw_all(Editor_state
)
50 -- backspace scrolls up
51 edit
.run_after_keychord(Editor_state
, 'backspace')
52 check_eq(#Editor_state
.lines
, 1, '#lines')
53 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
54 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
57 function test_insert_first_character()
58 App
.screen
.init
{width
=120, height
=60}
59 Editor_state
= edit
.initialize_test_state()
60 Editor_state
.lines
= load_array
{}
61 Text
.redraw_all(Editor_state
)
62 edit
.draw(Editor_state
)
63 edit
.run_after_text_input(Editor_state
, 'a')
64 local y
= Editor_state
.top
65 App
.screen
.check(y
, 'a', 'screen:1')
68 function test_press_ctrl()
69 -- press ctrl while the cursor is on text
70 App
.screen
.init
{width
=50, height
=80}
71 Editor_state
= edit
.initialize_test_state()
72 Editor_state
.lines
= load_array
{''}
73 Text
.redraw_all(Editor_state
)
74 Editor_state
.cursor1
= {line
=1, pos
=1}
75 Editor_state
.screen_top1
= {line
=1, pos
=1}
76 Editor_state
.screen_bottom1
= {}
77 edit
.run_after_keychord(Editor_state
, 'C-m')
80 function test_move_left()
81 App
.screen
.init
{width
=120, height
=60}
82 Editor_state
= edit
.initialize_test_state()
83 Editor_state
.lines
= load_array
{'a'}
84 Text
.redraw_all(Editor_state
)
85 Editor_state
.cursor1
= {line
=1, pos
=2}
86 edit
.draw(Editor_state
)
87 edit
.run_after_keychord(Editor_state
, 'left')
88 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
91 function test_move_right()
92 App
.screen
.init
{width
=120, height
=60}
93 Editor_state
= edit
.initialize_test_state()
94 Editor_state
.lines
= load_array
{'a'}
95 Text
.redraw_all(Editor_state
)
96 Editor_state
.cursor1
= {line
=1, pos
=1}
97 edit
.draw(Editor_state
)
98 edit
.run_after_keychord(Editor_state
, 'right')
99 check_eq(Editor_state
.cursor1
.pos
, 2, 'check')
102 function test_move_left_to_previous_line()
103 App
.screen
.init
{width
=120, height
=60}
104 Editor_state
= edit
.initialize_test_state()
105 Editor_state
.lines
= load_array
{'abc', 'def'}
106 Text
.redraw_all(Editor_state
)
107 Editor_state
.cursor1
= {line
=2, pos
=1}
108 edit
.draw(Editor_state
)
109 edit
.run_after_keychord(Editor_state
, 'left')
110 check_eq(Editor_state
.cursor1
.line
, 1, 'line')
111 check_eq(Editor_state
.cursor1
.pos
, 4, 'pos') -- past end of line
114 function test_move_right_to_next_line()
115 App
.screen
.init
{width
=120, height
=60}
116 Editor_state
= edit
.initialize_test_state()
117 Editor_state
.lines
= load_array
{'abc', 'def'}
118 Text
.redraw_all(Editor_state
)
119 Editor_state
.cursor1
= {line
=1, pos
=4} -- past end of line
120 edit
.draw(Editor_state
)
121 edit
.run_after_keychord(Editor_state
, 'right')
122 check_eq(Editor_state
.cursor1
.line
, 2, 'line')
123 check_eq(Editor_state
.cursor1
.pos
, 1, 'pos')
126 function test_move_to_start_of_word()
127 App
.screen
.init
{width
=120, height
=60}
128 Editor_state
= edit
.initialize_test_state()
129 Editor_state
.lines
= load_array
{'abc'}
130 Text
.redraw_all(Editor_state
)
131 Editor_state
.cursor1
= {line
=1, pos
=3}
132 edit
.draw(Editor_state
)
133 edit
.run_after_keychord(Editor_state
, 'M-left')
134 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
137 function test_move_to_start_of_previous_word()
138 App
.screen
.init
{width
=120, height
=60}
139 Editor_state
= edit
.initialize_test_state()
140 Editor_state
.lines
= load_array
{'abc def'}
141 Text
.redraw_all(Editor_state
)
142 Editor_state
.cursor1
= {line
=1, pos
=4} -- at the space between words
143 edit
.draw(Editor_state
)
144 edit
.run_after_keychord(Editor_state
, 'M-left')
145 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
148 function test_skip_to_previous_word()
149 App
.screen
.init
{width
=120, height
=60}
150 Editor_state
= edit
.initialize_test_state()
151 Editor_state
.lines
= load_array
{'abc def'}
152 Text
.redraw_all(Editor_state
)
153 Editor_state
.cursor1
= {line
=1, pos
=5} -- at the start of second word
154 edit
.draw(Editor_state
)
155 edit
.run_after_keychord(Editor_state
, 'M-left')
156 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
159 function test_skip_past_tab_to_previous_word()
160 App
.screen
.init
{width
=120, height
=60}
161 Editor_state
= edit
.initialize_test_state()
162 Editor_state
.lines
= load_array
{'abc def\tghi'}
163 Text
.redraw_all(Editor_state
)
164 Editor_state
.cursor1
= {line
=1, pos
=10} -- within third word
165 edit
.draw(Editor_state
)
166 edit
.run_after_keychord(Editor_state
, 'M-left')
167 check_eq(Editor_state
.cursor1
.pos
, 9, 'check')
170 function test_skip_multiple_spaces_to_previous_word()
171 App
.screen
.init
{width
=120, height
=60}
172 Editor_state
= edit
.initialize_test_state()
173 Editor_state
.lines
= load_array
{'abc def'}
174 Text
.redraw_all(Editor_state
)
175 Editor_state
.cursor1
= {line
=1, pos
=6} -- at the start of second word
176 edit
.draw(Editor_state
)
177 edit
.run_after_keychord(Editor_state
, 'M-left')
178 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
181 function test_move_to_start_of_word_on_previous_line()
182 App
.screen
.init
{width
=120, height
=60}
183 Editor_state
= edit
.initialize_test_state()
184 Editor_state
.lines
= load_array
{'abc def', 'ghi'}
185 Text
.redraw_all(Editor_state
)
186 Editor_state
.cursor1
= {line
=2, pos
=1}
187 edit
.draw(Editor_state
)
188 edit
.run_after_keychord(Editor_state
, 'M-left')
189 check_eq(Editor_state
.cursor1
.line
, 1, 'line')
190 check_eq(Editor_state
.cursor1
.pos
, 5, 'pos')
193 function test_move_past_end_of_word()
194 App
.screen
.init
{width
=120, height
=60}
195 Editor_state
= edit
.initialize_test_state()
196 Editor_state
.lines
= load_array
{'abc def'}
197 Text
.redraw_all(Editor_state
)
198 Editor_state
.cursor1
= {line
=1, pos
=1}
199 edit
.draw(Editor_state
)
200 edit
.run_after_keychord(Editor_state
, 'M-right')
201 check_eq(Editor_state
.cursor1
.pos
, 4, 'check')
204 function test_skip_to_next_word()
205 App
.screen
.init
{width
=120, height
=60}
206 Editor_state
= edit
.initialize_test_state()
207 Editor_state
.lines
= load_array
{'abc def'}
208 Text
.redraw_all(Editor_state
)
209 Editor_state
.cursor1
= {line
=1, pos
=4} -- at the space between words
210 edit
.draw(Editor_state
)
211 edit
.run_after_keychord(Editor_state
, 'M-right')
212 check_eq(Editor_state
.cursor1
.pos
, 8, 'check')
215 function test_skip_past_tab_to_next_word()
216 App
.screen
.init
{width
=120, height
=60}
217 Editor_state
= edit
.initialize_test_state()
218 Editor_state
.lines
= load_array
{'abc\tdef'}
219 Text
.redraw_all(Editor_state
)
220 Editor_state
.cursor1
= {line
=1, pos
=1} -- at the space between words
221 edit
.draw(Editor_state
)
222 edit
.run_after_keychord(Editor_state
, 'M-right')
223 check_eq(Editor_state
.cursor1
.pos
, 4, 'check')
226 function test_skip_multiple_spaces_to_next_word()
227 App
.screen
.init
{width
=120, height
=60}
228 Editor_state
= edit
.initialize_test_state()
229 Editor_state
.lines
= load_array
{'abc def'}
230 Text
.redraw_all(Editor_state
)
231 Editor_state
.cursor1
= {line
=1, pos
=4} -- at the start of second word
232 edit
.draw(Editor_state
)
233 edit
.run_after_keychord(Editor_state
, 'M-right')
234 check_eq(Editor_state
.cursor1
.pos
, 9, 'check')
237 function test_move_past_end_of_word_on_next_line()
238 App
.screen
.init
{width
=120, height
=60}
239 Editor_state
= edit
.initialize_test_state()
240 Editor_state
.lines
= load_array
{'abc def', 'ghi'}
241 Text
.redraw_all(Editor_state
)
242 Editor_state
.cursor1
= {line
=1, pos
=8}
243 edit
.draw(Editor_state
)
244 edit
.run_after_keychord(Editor_state
, 'M-right')
245 check_eq(Editor_state
.cursor1
.line
, 2, 'line')
246 check_eq(Editor_state
.cursor1
.pos
, 4, 'pos')
249 function test_click_moves_cursor()
250 App
.screen
.init
{width
=50, height
=60}
251 Editor_state
= edit
.initialize_test_state()
252 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
253 Text
.redraw_all(Editor_state
)
254 Editor_state
.cursor1
= {line
=1, pos
=1}
255 Editor_state
.screen_top1
= {line
=1, pos
=1}
256 Editor_state
.screen_bottom1
= {}
257 Editor_state
.selection1
= {}
258 edit
.draw(Editor_state
) -- populate line_cache.starty for each line Editor_state.line_cache
259 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
260 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
261 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
262 -- selection is empty to avoid perturbing future edits
263 check_nil(Editor_state
.selection1
.line
, 'selection:line')
264 check_nil(Editor_state
.selection1
.pos
, 'selection:pos')
267 function test_click_to_left_of_line()
268 -- display a line with the cursor in the middle
269 App
.screen
.init
{width
=50, height
=80}
270 Editor_state
= edit
.initialize_test_state()
271 Editor_state
.lines
= load_array
{'abc'}
272 Text
.redraw_all(Editor_state
)
273 Editor_state
.cursor1
= {line
=1, pos
=3}
274 Editor_state
.screen_top1
= {line
=1, pos
=1}
275 Editor_state
.screen_bottom1
= {}
276 -- click to the left of the line
277 edit
.draw(Editor_state
)
278 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
-4,Editor_state
.top
+5, 1)
279 -- cursor moves to start of line
280 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
281 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
282 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
285 function test_click_takes_margins_into_account()
286 -- display two lines with cursor on one of them
287 App
.screen
.init
{width
=100, height
=80}
288 Editor_state
= edit
.initialize_test_state()
289 Editor_state
.left
= 50 -- occupy only right side of screen
290 Editor_state
.lines
= load_array
{'abc', 'def'}
291 Text
.redraw_all(Editor_state
)
292 Editor_state
.cursor1
= {line
=2, pos
=1}
293 Editor_state
.screen_top1
= {line
=1, pos
=1}
294 Editor_state
.screen_bottom1
= {}
295 -- click on the other line
296 edit
.draw(Editor_state
)
297 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
299 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
300 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
301 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
304 function test_click_on_empty_line()
305 -- display two lines with the first one empty
306 App
.screen
.init
{width
=50, height
=80}
307 Editor_state
= edit
.initialize_test_state()
308 Editor_state
.lines
= load_array
{'', 'def'}
309 Text
.redraw_all(Editor_state
)
310 Editor_state
.cursor1
= {line
=2, pos
=1}
311 Editor_state
.screen_top1
= {line
=1, pos
=1}
312 Editor_state
.screen_bottom1
= {}
313 -- click on the empty line
314 edit
.draw(Editor_state
)
315 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
317 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
320 function test_draw_text()
321 App
.screen
.init
{width
=120, height
=60}
322 Editor_state
= edit
.initialize_test_state()
323 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi'}
324 Text
.redraw_all(Editor_state
)
325 Editor_state
.cursor1
= {line
=1, pos
=1}
326 Editor_state
.screen_top1
= {line
=1, pos
=1}
327 Editor_state
.screen_bottom1
= {}
328 edit
.draw(Editor_state
)
329 local y
= Editor_state
.top
330 App
.screen
.check(y
, 'abc', 'screen:1')
331 y
= y
+ Editor_state
.line_height
332 App
.screen
.check(y
, 'def', 'screen:2')
333 y
= y
+ Editor_state
.line_height
334 App
.screen
.check(y
, 'ghi', 'screen:3')
337 function test_draw_wrapping_text()
338 App
.screen
.init
{width
=50, height
=60}
339 Editor_state
= edit
.initialize_test_state()
340 Editor_state
.lines
= load_array
{'abc', 'defgh', 'xyz'}
341 Text
.redraw_all(Editor_state
)
342 Editor_state
.cursor1
= {line
=1, pos
=1}
343 Editor_state
.screen_top1
= {line
=1, pos
=1}
344 Editor_state
.screen_bottom1
= {}
345 edit
.draw(Editor_state
)
346 local y
= Editor_state
.top
347 App
.screen
.check(y
, 'abc', 'screen:1')
348 y
= y
+ Editor_state
.line_height
349 App
.screen
.check(y
, 'de', 'screen:2')
350 y
= y
+ Editor_state
.line_height
351 App
.screen
.check(y
, 'fgh', 'screen:3')
354 function test_draw_word_wrapping_text()
355 App
.screen
.init
{width
=60, height
=60}
356 Editor_state
= edit
.initialize_test_state()
357 Editor_state
.lines
= load_array
{'abc def ghi', 'jkl'}
358 Text
.redraw_all(Editor_state
)
359 Editor_state
.cursor1
= {line
=1, pos
=1}
360 Editor_state
.screen_top1
= {line
=1, pos
=1}
361 Editor_state
.screen_bottom1
= {}
362 edit
.draw(Editor_state
)
363 local y
= Editor_state
.top
364 App
.screen
.check(y
, 'abc ', 'screen:1')
365 y
= y
+ Editor_state
.line_height
366 App
.screen
.check(y
, 'def ', 'screen:2')
367 y
= y
+ Editor_state
.line_height
368 App
.screen
.check(y
, 'ghi', 'screen:3')
371 function test_click_on_wrapping_line()
372 -- display two screen lines with cursor on one of them
373 App
.screen
.init
{width
=50, height
=80}
374 Editor_state
= edit
.initialize_test_state()
375 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr stu'}
376 Text
.redraw_all(Editor_state
)
377 Editor_state
.cursor1
= {line
=1, pos
=20}
378 Editor_state
.screen_top1
= {line
=1, pos
=1}
379 Editor_state
.screen_bottom1
= {}
380 -- click on the other line
381 edit
.draw(Editor_state
)
382 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
384 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
385 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
386 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
389 function test_click_on_wrapping_line_takes_margins_into_account()
390 -- display two screen lines with cursor on one of them
391 App
.screen
.init
{width
=100, height
=80}
392 Editor_state
= edit
.initialize_test_state()
393 Editor_state
.left
= 50 -- occupy only right side of screen
394 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr stu'}
395 Text
.redraw_all(Editor_state
)
396 Editor_state
.cursor1
= {line
=1, pos
=20}
397 Editor_state
.screen_top1
= {line
=1, pos
=1}
398 Editor_state
.screen_bottom1
= {}
399 -- click on the other line
400 edit
.draw(Editor_state
)
401 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
403 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
404 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
405 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
408 function test_draw_text_wrapping_within_word()
409 -- arrange a screen line that needs to be split within a word
410 App
.screen
.init
{width
=60, height
=60}
411 Editor_state
= edit
.initialize_test_state()
412 Editor_state
.lines
= load_array
{'abcd e fghijk', 'xyz'}
413 Text
.redraw_all(Editor_state
)
414 Editor_state
.cursor1
= {line
=1, pos
=1}
415 Editor_state
.screen_top1
= {line
=1, pos
=1}
416 Editor_state
.screen_bottom1
= {}
417 edit
.draw(Editor_state
)
418 local y
= Editor_state
.top
419 App
.screen
.check(y
, 'abcd ', 'screen:1')
420 y
= y
+ Editor_state
.line_height
421 App
.screen
.check(y
, 'e fgh', 'screen:2')
422 y
= y
+ Editor_state
.line_height
423 App
.screen
.check(y
, 'ijk', 'screen:3')
426 function test_draw_wrapping_text_containing_non_ascii()
427 -- draw a long line containing non-ASCII
428 App
.screen
.init
{width
=60, height
=60}
429 Editor_state
= edit
.initialize_test_state()
430 Editor_state
.lines
= load_array
{'madam I’m adam', 'xyz'} -- notice the non-ASCII apostrophe
431 Text
.redraw_all(Editor_state
)
432 Editor_state
.cursor1
= {line
=1, pos
=1}
433 Editor_state
.screen_top1
= {line
=1, pos
=1}
434 Editor_state
.screen_bottom1
= {}
435 edit
.draw(Editor_state
)
436 local y
= Editor_state
.top
437 App
.screen
.check(y
, 'mad', 'screen:1')
438 y
= y
+ Editor_state
.line_height
439 App
.screen
.check(y
, 'am I', 'screen:2')
440 y
= y
+ Editor_state
.line_height
441 App
.screen
.check(y
, '’m a', 'screen:3')
444 function test_click_past_end_of_screen_line()
445 -- display a wrapping line
446 App
.screen
.init
{width
=75, height
=80}
447 Editor_state
= edit
.initialize_test_state()
449 Editor_state
.lines
= load_array
{"madam I'm adam"}
450 Text
.redraw_all(Editor_state
)
451 Editor_state
.cursor1
= {line
=1, pos
=1}
452 Editor_state
.screen_top1
= {line
=1, pos
=1}
453 Editor_state
.screen_bottom1
= {}
454 edit
.draw(Editor_state
)
455 local y
= Editor_state
.top
456 App
.screen
.check(y
, 'madam ', 'baseline/screen:1')
457 y
= y
+ Editor_state
.line_height
458 App
.screen
.check(y
, "I'm ad", 'baseline/screen:2')
459 y
= y
+ Editor_state
.line_height
460 -- click past end of second screen line
461 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
462 -- cursor moves to end of screen line
463 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
464 check_eq(Editor_state
.cursor1
.pos
, 12, 'cursor:pos')
467 function test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen()
468 -- display a wrapping line from its second screen line
469 App
.screen
.init
{width
=75, height
=80}
470 Editor_state
= edit
.initialize_test_state()
472 Editor_state
.lines
= load_array
{"madam I'm adam"}
473 Text
.redraw_all(Editor_state
)
474 Editor_state
.cursor1
= {line
=1, pos
=8}
475 Editor_state
.screen_top1
= {line
=1, pos
=7}
476 Editor_state
.screen_bottom1
= {}
477 edit
.draw(Editor_state
)
478 local y
= Editor_state
.top
479 App
.screen
.check(y
, "I'm ad", 'baseline/screen:2')
480 y
= y
+ Editor_state
.line_height
481 -- click past end of second screen line
482 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
483 -- cursor moves to end of screen line
484 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
485 check_eq(Editor_state
.cursor1
.pos
, 12, 'cursor:pos')
488 function test_click_past_end_of_wrapping_line()
489 -- display a wrapping line
490 App
.screen
.init
{width
=75, height
=80}
491 Editor_state
= edit
.initialize_test_state()
493 Editor_state
.lines
= load_array
{"madam I'm adam"}
494 Text
.redraw_all(Editor_state
)
495 Editor_state
.cursor1
= {line
=1, pos
=1}
496 Editor_state
.screen_top1
= {line
=1, pos
=1}
497 Editor_state
.screen_bottom1
= {}
498 edit
.draw(Editor_state
)
499 local y
= Editor_state
.top
500 App
.screen
.check(y
, 'madam ', 'baseline/screen:1')
501 y
= y
+ Editor_state
.line_height
502 App
.screen
.check(y
, "I'm ad", 'baseline/screen:2')
503 y
= y
+ Editor_state
.line_height
504 App
.screen
.check(y
, 'am', 'baseline/screen:3')
505 y
= y
+ Editor_state
.line_height
506 -- click past the end of it
507 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
508 -- cursor moves to end of line
509 check_eq(Editor_state
.cursor1
.pos
, 15, 'cursor') -- one more than the number of UTF-8 code-points
512 function test_click_past_end_of_wrapping_line_containing_non_ascii()
513 -- display a wrapping line containing non-ASCII
514 App
.screen
.init
{width
=75, height
=80}
515 Editor_state
= edit
.initialize_test_state()
517 Editor_state
.lines
= load_array
{'madam I’m adam'} -- notice the non-ASCII apostrophe
518 Text
.redraw_all(Editor_state
)
519 Editor_state
.cursor1
= {line
=1, pos
=1}
520 Editor_state
.screen_top1
= {line
=1, pos
=1}
521 Editor_state
.screen_bottom1
= {}
522 edit
.draw(Editor_state
)
523 local y
= Editor_state
.top
524 App
.screen
.check(y
, 'madam ', 'baseline/screen:1')
525 y
= y
+ Editor_state
.line_height
526 App
.screen
.check(y
, 'I’m ad', 'baseline/screen:2')
527 y
= y
+ Editor_state
.line_height
528 App
.screen
.check(y
, 'am', 'baseline/screen:3')
529 y
= y
+ Editor_state
.line_height
530 -- click past the end of it
531 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
532 -- cursor moves to end of line
533 check_eq(Editor_state
.cursor1
.pos
, 15, 'cursor') -- one more than the number of UTF-8 code-points
536 function test_click_past_end_of_word_wrapping_line()
537 -- display a long line wrapping at a word boundary on a screen of more realistic length
538 App
.screen
.init
{width
=160, height
=80}
539 Editor_state
= edit
.initialize_test_state()
541 -- 123456789012345678901
542 Editor_state
.lines
= load_array
{'the quick brown fox jumped over the lazy dog'}
543 Text
.redraw_all(Editor_state
)
544 Editor_state
.cursor1
= {line
=1, pos
=1}
545 Editor_state
.screen_top1
= {line
=1, pos
=1}
546 Editor_state
.screen_bottom1
= {}
547 edit
.draw(Editor_state
)
548 local y
= Editor_state
.top
549 App
.screen
.check(y
, 'the quick brown fox ', 'baseline/screen:1')
550 y
= y
+ Editor_state
.line_height
551 -- click past the end of the screen line
552 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
553 -- cursor moves to end of screen line
554 check_eq(Editor_state
.cursor1
.pos
, 20, 'cursor')
557 function test_select_text()
558 -- display a line of text
559 App
.screen
.init
{width
=75, height
=80}
560 Editor_state
= edit
.initialize_test_state()
561 Editor_state
.lines
= load_array
{'abc def'}
562 Text
.redraw_all(Editor_state
)
563 Editor_state
.cursor1
= {line
=1, pos
=1}
564 Editor_state
.screen_top1
= {line
=1, pos
=1}
565 Editor_state
.screen_bottom1
= {}
566 edit
.draw(Editor_state
)
568 App
.fake_key_press('lshift')
569 edit
.run_after_keychord(Editor_state
, 'S-right')
570 App
.fake_key_release('lshift')
571 edit
.key_release(Editor_state
, 'lshift')
572 -- selection persists even after shift is released
573 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
574 check_eq(Editor_state
.selection1
.pos
, 1, 'selection:pos')
575 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
576 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
579 function test_cursor_movement_without_shift_resets_selection()
580 -- display a line of text with some part selected
581 App
.screen
.init
{width
=75, height
=80}
582 Editor_state
= edit
.initialize_test_state()
583 Editor_state
.lines
= load_array
{'abc'}
584 Text
.redraw_all(Editor_state
)
585 Editor_state
.cursor1
= {line
=1, pos
=1}
586 Editor_state
.selection1
= {line
=1, pos
=2}
587 Editor_state
.screen_top1
= {line
=1, pos
=1}
588 Editor_state
.screen_bottom1
= {}
589 edit
.draw(Editor_state
)
590 -- press an arrow key without shift
591 edit
.run_after_keychord(Editor_state
, 'right')
592 -- no change to data, selection is reset
593 check_nil(Editor_state
.selection1
.line
, 'check')
594 check_eq(Editor_state
.lines
[1].data
, 'abc', 'data')
597 function test_edit_deletes_selection()
598 -- display a line of text with some part selected
599 App
.screen
.init
{width
=75, height
=80}
600 Editor_state
= edit
.initialize_test_state()
601 Editor_state
.lines
= load_array
{'abc'}
602 Text
.redraw_all(Editor_state
)
603 Editor_state
.cursor1
= {line
=1, pos
=1}
604 Editor_state
.selection1
= {line
=1, pos
=2}
605 Editor_state
.screen_top1
= {line
=1, pos
=1}
606 Editor_state
.screen_bottom1
= {}
607 edit
.draw(Editor_state
)
609 edit
.run_after_text_input(Editor_state
, 'x')
610 -- selected text is deleted and replaced with the key
611 check_eq(Editor_state
.lines
[1].data
, 'xbc', 'check')
614 function test_edit_with_shift_key_deletes_selection()
615 -- display a line of text with some part selected
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 Editor_state
.screen_bottom1
= {}
624 edit
.draw(Editor_state
)
625 -- mimic precise keypresses for a capital letter
626 App
.fake_key_press('lshift')
627 edit
.keychord_press(Editor_state
, 'd', 'd')
628 edit
.text_input(Editor_state
, 'D')
629 edit
.key_release(Editor_state
, 'd')
630 App
.fake_key_release('lshift')
631 -- selected text is deleted and replaced with the key
632 check_nil(Editor_state
.selection1
.line
, 'check')
633 check_eq(Editor_state
.lines
[1].data
, 'Dbc', 'data')
636 function test_copy_does_not_reset_selection()
637 -- display a line of text with a selection
638 App
.screen
.init
{width
=75, height
=80}
639 Editor_state
= edit
.initialize_test_state()
640 Editor_state
.lines
= load_array
{'abc'}
641 Text
.redraw_all(Editor_state
)
642 Editor_state
.cursor1
= {line
=1, pos
=1}
643 Editor_state
.selection1
= {line
=1, pos
=2}
644 Editor_state
.screen_top1
= {line
=1, pos
=1}
645 Editor_state
.screen_bottom1
= {}
646 edit
.draw(Editor_state
)
648 edit
.run_after_keychord(Editor_state
, 'C-c')
649 check_eq(App
.clipboard
, 'a', 'clipboard')
650 -- selection is reset since shift key is not pressed
651 check(Editor_state
.selection1
.line
, 'check')
655 -- display a line of text with some part selected
656 App
.screen
.init
{width
=75, height
=80}
657 Editor_state
= edit
.initialize_test_state()
658 Editor_state
.lines
= load_array
{'abc'}
659 Text
.redraw_all(Editor_state
)
660 Editor_state
.cursor1
= {line
=1, pos
=1}
661 Editor_state
.selection1
= {line
=1, pos
=2}
662 Editor_state
.screen_top1
= {line
=1, pos
=1}
663 Editor_state
.screen_bottom1
= {}
664 edit
.draw(Editor_state
)
666 edit
.run_after_keychord(Editor_state
, 'C-x')
667 check_eq(App
.clipboard
, 'a', 'clipboard')
668 -- selected text is deleted
669 check_eq(Editor_state
.lines
[1].data
, 'bc', 'data')
672 function test_paste_replaces_selection()
673 -- display a line of text with a selection
674 App
.screen
.init
{width
=75, height
=80}
675 Editor_state
= edit
.initialize_test_state()
676 Editor_state
.lines
= load_array
{'abc', 'def'}
677 Text
.redraw_all(Editor_state
)
678 Editor_state
.cursor1
= {line
=2, pos
=1}
679 Editor_state
.selection1
= {line
=1, pos
=1}
680 Editor_state
.screen_top1
= {line
=1, pos
=1}
681 Editor_state
.screen_bottom1
= {}
682 edit
.draw(Editor_state
)
684 App
.clipboard
= 'xyz'
686 edit
.run_after_keychord(Editor_state
, 'C-v')
687 -- selection is reset since shift key is not pressed
688 -- selection includes the newline, so it's also deleted
689 check_eq(Editor_state
.lines
[1].data
, 'xyzdef', 'check')
692 function test_deleting_selection_may_scroll()
693 -- display lines 2/3/4
694 App
.screen
.init
{width
=120, height
=60}
695 Editor_state
= edit
.initialize_test_state()
696 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
697 Text
.redraw_all(Editor_state
)
698 Editor_state
.cursor1
= {line
=3, pos
=2}
699 Editor_state
.screen_top1
= {line
=2, pos
=1}
700 Editor_state
.screen_bottom1
= {}
701 edit
.draw(Editor_state
)
702 local y
= Editor_state
.top
703 App
.screen
.check(y
, 'def', 'baseline/screen:1')
704 y
= y
+ Editor_state
.line_height
705 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
706 y
= y
+ Editor_state
.line_height
707 App
.screen
.check(y
, 'jkl', 'baseline/screen:3')
708 -- set up a selection starting above the currently displayed page
709 Editor_state
.selection1
= {line
=1, pos
=2}
711 edit
.run_after_keychord(Editor_state
, 'backspace')
713 check_eq(Editor_state
.screen_top1
.line
, 1, 'check')
714 check_eq(Editor_state
.lines
[1].data
, 'ahi', 'data')
717 function test_edit_wrapping_text()
718 App
.screen
.init
{width
=50, height
=60}
719 Editor_state
= edit
.initialize_test_state()
720 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
721 Text
.redraw_all(Editor_state
)
722 Editor_state
.cursor1
= {line
=2, pos
=4}
723 Editor_state
.screen_top1
= {line
=1, pos
=1}
724 Editor_state
.screen_bottom1
= {}
725 edit
.draw(Editor_state
)
726 edit
.run_after_text_input(Editor_state
, 'g')
727 local y
= Editor_state
.top
728 App
.screen
.check(y
, 'abc', 'screen:1')
729 y
= y
+ Editor_state
.line_height
730 App
.screen
.check(y
, 'de', 'screen:2')
731 y
= y
+ Editor_state
.line_height
732 App
.screen
.check(y
, 'fg', 'screen:3')
735 function test_insert_newline()
736 -- display a few lines
737 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
738 Editor_state
= edit
.initialize_test_state()
739 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
740 Text
.redraw_all(Editor_state
)
741 Editor_state
.cursor1
= {line
=1, pos
=2}
742 Editor_state
.screen_top1
= {line
=1, pos
=1}
743 Editor_state
.screen_bottom1
= {}
744 edit
.draw(Editor_state
)
745 local y
= Editor_state
.top
746 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
747 y
= y
+ Editor_state
.line_height
748 App
.screen
.check(y
, 'def', 'baseline/screen:2')
749 y
= y
+ Editor_state
.line_height
750 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
751 -- hitting the enter key splits the line
752 edit
.run_after_keychord(Editor_state
, 'return')
753 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
754 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
755 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
757 App
.screen
.check(y
, 'a', 'screen:1')
758 y
= y
+ Editor_state
.line_height
759 App
.screen
.check(y
, 'bc', 'screen:2')
760 y
= y
+ Editor_state
.line_height
761 App
.screen
.check(y
, 'def', 'screen:3')
764 function test_insert_newline_at_start_of_line()
766 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
767 Editor_state
= edit
.initialize_test_state()
768 Editor_state
.lines
= load_array
{'abc'}
769 Text
.redraw_all(Editor_state
)
770 Editor_state
.cursor1
= {line
=1, pos
=1}
771 Editor_state
.screen_top1
= {line
=1, pos
=1}
772 Editor_state
.screen_bottom1
= {}
773 -- hitting the enter key splits the line
774 edit
.run_after_keychord(Editor_state
, 'return')
775 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
776 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
777 check_eq(Editor_state
.lines
[1].data
, '', 'data:1')
778 check_eq(Editor_state
.lines
[2].data
, 'abc', 'data:2')
781 function test_insert_from_clipboard()
782 -- display a few lines
783 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
784 Editor_state
= edit
.initialize_test_state()
785 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
786 Text
.redraw_all(Editor_state
)
787 Editor_state
.cursor1
= {line
=1, pos
=2}
788 Editor_state
.screen_top1
= {line
=1, pos
=1}
789 Editor_state
.screen_bottom1
= {}
790 edit
.draw(Editor_state
)
791 local y
= Editor_state
.top
792 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
793 y
= y
+ Editor_state
.line_height
794 App
.screen
.check(y
, 'def', 'baseline/screen:2')
795 y
= y
+ Editor_state
.line_height
796 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
797 -- paste some text including a newline, check that new line is created
798 App
.clipboard
= 'xy\nz'
799 edit
.run_after_keychord(Editor_state
, 'C-v')
800 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
801 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
802 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
804 App
.screen
.check(y
, 'axy', 'screen:1')
805 y
= y
+ Editor_state
.line_height
806 App
.screen
.check(y
, 'zbc', 'screen:2')
807 y
= y
+ Editor_state
.line_height
808 App
.screen
.check(y
, 'def', 'screen:3')
811 function test_select_text_using_mouse()
812 App
.screen
.init
{width
=50, height
=60}
813 Editor_state
= edit
.initialize_test_state()
814 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
815 Text
.redraw_all(Editor_state
)
816 Editor_state
.cursor1
= {line
=1, pos
=1}
817 Editor_state
.screen_top1
= {line
=1, pos
=1}
818 Editor_state
.screen_bottom1
= {}
819 Editor_state
.selection1
= {}
820 edit
.draw(Editor_state
) -- populate line_cache.starty for each line Editor_state.line_cache
821 -- press and hold on first location
822 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
823 -- drag and release somewhere else
824 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+Editor_state
.line_height
+5, 1)
825 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
826 check_eq(Editor_state
.selection1
.pos
, 2, 'selection:pos')
827 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
828 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
831 function test_select_text_using_mouse_and_shift()
832 App
.screen
.init
{width
=50, height
=60}
833 Editor_state
= edit
.initialize_test_state()
834 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
835 Text
.redraw_all(Editor_state
)
836 Editor_state
.cursor1
= {line
=1, pos
=1}
837 Editor_state
.screen_top1
= {line
=1, pos
=1}
838 Editor_state
.screen_bottom1
= {}
839 Editor_state
.selection1
= {}
840 edit
.draw(Editor_state
) -- populate line_cache.starty for each line Editor_state.line_cache
841 -- click on first location
842 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
843 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
844 -- hold down shift and click somewhere else
845 App
.fake_key_press('lshift')
846 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+5, 1)
847 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+Editor_state
.line_height
+5, 1)
848 App
.fake_key_release('lshift')
849 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
850 check_eq(Editor_state
.selection1
.pos
, 2, 'selection:pos')
851 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
852 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
855 function test_select_text_repeatedly_using_mouse_and_shift()
856 App
.screen
.init
{width
=50, height
=60}
857 Editor_state
= edit
.initialize_test_state()
858 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
859 Text
.redraw_all(Editor_state
)
860 Text
.redraw_all(Editor_state
)
861 Editor_state
.cursor1
= {line
=1, pos
=1}
862 Editor_state
.screen_top1
= {line
=1, pos
=1}
863 Editor_state
.screen_bottom1
= {}
864 Editor_state
.selection1
= {}
865 edit
.draw(Editor_state
) -- populate line_cache.starty for each line Editor_state.line_cache
866 -- click on first location
867 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
868 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
869 -- hold down shift and click on a second location
870 App
.fake_key_press('lshift')
871 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+5, 1)
872 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+Editor_state
.line_height
+5, 1)
873 -- hold down shift and click at a third location
874 App
.fake_key_press('lshift')
875 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+5, 1)
876 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+Editor_state
.line_height
+5, 1)
877 App
.fake_key_release('lshift')
878 -- selection is between first and third location. forget the second location, not the first.
879 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
880 check_eq(Editor_state
.selection1
.pos
, 2, 'selection:pos')
881 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
882 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
885 function test_cut_without_selection()
886 -- display a few lines
887 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
888 Editor_state
= edit
.initialize_test_state()
889 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
890 Text
.redraw_all(Editor_state
)
891 Editor_state
.cursor1
= {line
=1, pos
=2}
892 Editor_state
.screen_top1
= {line
=1, pos
=1}
893 Editor_state
.screen_bottom1
= {}
894 Editor_state
.selection1
= {}
895 edit
.draw(Editor_state
)
896 -- try to cut without selecting text
897 edit
.run_after_keychord(Editor_state
, 'C-x')
899 check_nil(Editor_state
.selection1
.line
, 'check')
902 function test_pagedown()
903 App
.screen
.init
{width
=120, height
=45}
904 Editor_state
= edit
.initialize_test_state()
905 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi'}
906 Text
.redraw_all(Editor_state
)
907 Editor_state
.cursor1
= {line
=1, pos
=1}
908 Editor_state
.screen_top1
= {line
=1, pos
=1}
909 Editor_state
.screen_bottom1
= {}
910 -- initially the first two lines are displayed
911 edit
.draw(Editor_state
)
912 local y
= Editor_state
.top
913 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
914 y
= y
+ Editor_state
.line_height
915 App
.screen
.check(y
, 'def', 'baseline/screen:2')
916 -- after pagedown the bottom line becomes the top
917 edit
.run_after_keychord(Editor_state
, 'pagedown')
918 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
919 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor')
921 App
.screen
.check(y
, 'def', 'screen:1')
922 y
= y
+ Editor_state
.line_height
923 App
.screen
.check(y
, 'ghi', 'screen:2')
926 function test_pagedown_skips_drawings()
927 -- some lines of text with a drawing intermixed
928 local drawing_width
= 50
929 App
.screen
.init
{width
=Editor_state
.left
+drawing_width
, height
=80}
930 Editor_state
= edit
.initialize_test_state()
931 Editor_state
.lines
= load_array
{'abc', -- height 15
932 '```lines', '```', -- height 25
935 Text
.redraw_all(Editor_state
)
936 check_eq(Editor_state
.lines
[2].mode
, 'drawing', 'baseline/lines')
937 Editor_state
.cursor1
= {line
=1, pos
=1}
938 Editor_state
.screen_top1
= {line
=1, pos
=1}
939 Editor_state
.screen_bottom1
= {}
940 local drawing_height
= Drawing_padding_height
+ drawing_width
/2 -- default
941 -- initially the screen displays the first line and the drawing
942 -- 15px margin + 15px line1 + 10px margin + 25px drawing + 10px margin = 75px < screen height 80px
943 edit
.draw(Editor_state
)
944 local y
= Editor_state
.top
945 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
946 -- after pagedown the screen draws the drawing up top
947 -- 15px margin + 10px margin + 25px drawing + 10px margin + 15px line3 = 75px < screen height 80px
948 edit
.run_after_keychord(Editor_state
, 'pagedown')
949 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
950 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor')
951 y
= Editor_state
.top
+ drawing_height
952 App
.screen
.check(y
, 'def', 'screen:1')
955 function test_pagedown_can_start_from_middle_of_long_wrapping_line()
956 -- draw a few lines starting from a very long wrapping line
957 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
958 Editor_state
= edit
.initialize_test_state()
959 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr stu vwx yza bcd efg hij', 'XYZ'}
960 Text
.redraw_all(Editor_state
)
961 Editor_state
.cursor1
= {line
=1, pos
=2}
962 Editor_state
.screen_top1
= {line
=1, pos
=1}
963 Editor_state
.screen_bottom1
= {}
964 edit
.draw(Editor_state
)
965 local y
= Editor_state
.top
966 App
.screen
.check(y
, 'abc ', 'baseline/screen:1')
967 y
= y
+ Editor_state
.line_height
968 App
.screen
.check(y
, 'def ', 'baseline/screen:2')
969 y
= y
+ Editor_state
.line_height
970 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3')
971 -- after pagedown we scroll down the very long wrapping line
972 edit
.run_after_keychord(Editor_state
, 'pagedown')
973 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
974 check_eq(Editor_state
.screen_top1
.pos
, 9, 'screen_top:pos')
976 App
.screen
.check(y
, 'ghi ', 'screen:1')
977 y
= y
+ Editor_state
.line_height
978 App
.screen
.check(y
, 'jkl ', 'screen:2')
979 y
= y
+ Editor_state
.line_height
980 App
.screen
.check(y
, 'mn', 'screen:3')
983 function test_pagedown_never_moves_up()
984 -- draw the final screen line of a wrapping line
985 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
986 Editor_state
= edit
.initialize_test_state()
987 Editor_state
.lines
= load_array
{'abc def ghi'}
988 Text
.redraw_all(Editor_state
)
989 Editor_state
.cursor1
= {line
=1, pos
=9}
990 Editor_state
.screen_top1
= {line
=1, pos
=9}
991 Editor_state
.screen_bottom1
= {}
992 edit
.draw(Editor_state
)
993 -- pagedown makes no change
994 edit
.run_after_keychord(Editor_state
, 'pagedown')
995 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
996 check_eq(Editor_state
.screen_top1
.pos
, 9, 'screen_top:pos')
999 function test_down_arrow_moves_cursor()
1000 App
.screen
.init
{width
=120, height
=60}
1001 Editor_state
= edit
.initialize_test_state()
1002 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1003 Text
.redraw_all(Editor_state
)
1004 Editor_state
.cursor1
= {line
=1, pos
=1}
1005 Editor_state
.screen_top1
= {line
=1, pos
=1}
1006 Editor_state
.screen_bottom1
= {}
1007 -- initially the first three lines are displayed
1008 edit
.draw(Editor_state
)
1009 local y
= Editor_state
.top
1010 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1011 y
= y
+ Editor_state
.line_height
1012 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1013 y
= y
+ Editor_state
.line_height
1014 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1015 -- after hitting the down arrow, the cursor moves down by 1 line
1016 edit
.run_after_keychord(Editor_state
, 'down')
1017 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1018 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor')
1019 -- the screen is unchanged
1020 y
= Editor_state
.top
1021 App
.screen
.check(y
, 'abc', 'screen:1')
1022 y
= y
+ Editor_state
.line_height
1023 App
.screen
.check(y
, 'def', 'screen:2')
1024 y
= y
+ Editor_state
.line_height
1025 App
.screen
.check(y
, 'ghi', 'screen:3')
1028 function test_down_arrow_skips_drawing()
1029 -- some lines of text with a drawing intermixed
1030 local drawing_width
= 50
1031 App
.screen
.init
{width
=Editor_state
.left
+drawing_width
, height
=100}
1032 Editor_state
= edit
.initialize_test_state()
1033 Editor_state
.lines
= load_array
{'abc', -- height 15
1034 '```lines', '```', -- height 25
1036 Text
.redraw_all(Editor_state
)
1037 Editor_state
.cursor1
= {line
=1, pos
=1}
1038 Editor_state
.screen_top1
= {line
=1, pos
=1}
1039 Editor_state
.screen_bottom1
= {}
1040 edit
.draw(Editor_state
)
1041 local y
= Editor_state
.top
1042 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1043 y
= y
+ Editor_state
.line_height
1044 local drawing_height
= Drawing_padding_height
+ drawing_width
/2 -- default
1045 y
= y
+ drawing_height
1046 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1047 check(Editor_state
.cursor_x
, 'baseline/cursor_x')
1048 -- after hitting the down arrow the cursor moves down by 2 lines, skipping the drawing
1049 edit
.run_after_keychord(Editor_state
, 'down')
1050 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor')
1053 function test_down_arrow_scrolls_down_by_one_line()
1054 -- display the first three lines with the cursor on the bottom line
1055 App
.screen
.init
{width
=120, height
=60}
1056 Editor_state
= edit
.initialize_test_state()
1057 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1058 Text
.redraw_all(Editor_state
)
1059 Editor_state
.cursor1
= {line
=3, pos
=1}
1060 Editor_state
.screen_top1
= {line
=1, pos
=1}
1061 Editor_state
.screen_bottom1
= {}
1062 edit
.draw(Editor_state
)
1063 local y
= Editor_state
.top
1064 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1065 y
= y
+ Editor_state
.line_height
1066 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1067 y
= y
+ Editor_state
.line_height
1068 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1069 -- after hitting the down arrow the screen scrolls down by one line
1070 edit
.run_after_keychord(Editor_state
, 'down')
1071 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1072 check_eq(Editor_state
.cursor1
.line
, 4, 'cursor')
1073 y
= Editor_state
.top
1074 App
.screen
.check(y
, 'def', 'screen:1')
1075 y
= y
+ Editor_state
.line_height
1076 App
.screen
.check(y
, 'ghi', 'screen:2')
1077 y
= y
+ Editor_state
.line_height
1078 App
.screen
.check(y
, 'jkl', 'screen:3')
1081 function test_down_arrow_scrolls_down_by_one_screen_line()
1082 -- display the first three lines with the cursor on the bottom line
1083 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1084 Editor_state
= edit
.initialize_test_state()
1085 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1086 Text
.redraw_all(Editor_state
)
1087 Editor_state
.cursor1
= {line
=3, pos
=1}
1088 Editor_state
.screen_top1
= {line
=1, pos
=1}
1089 Editor_state
.screen_bottom1
= {}
1090 edit
.draw(Editor_state
)
1091 local y
= Editor_state
.top
1092 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1093 y
= y
+ Editor_state
.line_height
1094 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1095 y
= y
+ Editor_state
.line_height
1096 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1097 -- after hitting the down arrow the screen scrolls down by one line
1098 edit
.run_after_keychord(Editor_state
, 'down')
1099 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1100 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1101 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1102 y
= Editor_state
.top
1103 App
.screen
.check(y
, 'def', 'screen:1')
1104 y
= y
+ Editor_state
.line_height
1105 App
.screen
.check(y
, 'ghi ', 'screen:2')
1106 y
= y
+ Editor_state
.line_height
1107 App
.screen
.check(y
, 'jkl', 'screen:3')
1110 function test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word()
1111 -- display the first three lines with the cursor on the bottom line
1112 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1113 Editor_state
= edit
.initialize_test_state()
1114 Editor_state
.lines
= load_array
{'abc', 'def', 'ghijkl', 'mno'}
1115 Text
.redraw_all(Editor_state
)
1116 Editor_state
.cursor1
= {line
=3, pos
=1}
1117 Editor_state
.screen_top1
= {line
=1, pos
=1}
1118 Editor_state
.screen_bottom1
= {}
1119 edit
.draw(Editor_state
)
1120 local y
= Editor_state
.top
1121 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1122 y
= y
+ Editor_state
.line_height
1123 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1124 y
= y
+ Editor_state
.line_height
1125 App
.screen
.check(y
, 'ghij', 'baseline/screen:3')
1126 -- after hitting the down arrow the screen scrolls down by one line
1127 edit
.run_after_keychord(Editor_state
, 'down')
1128 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1129 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1130 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1131 y
= Editor_state
.top
1132 App
.screen
.check(y
, 'def', 'screen:1')
1133 y
= y
+ Editor_state
.line_height
1134 App
.screen
.check(y
, 'ghij', 'screen:2')
1135 y
= y
+ Editor_state
.line_height
1136 App
.screen
.check(y
, 'kl', 'screen:3')
1139 function test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up()
1140 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1141 Editor_state
= edit
.initialize_test_state()
1142 Editor_state
.lines
= load_array
{'abc', 'def', 'ghijkl', 'mno'}
1143 Text
.redraw_all(Editor_state
)
1144 Editor_state
.cursor1
= {line
=3, pos
=1}
1145 Editor_state
.screen_top1
= {line
=1, pos
=1}
1146 Editor_state
.screen_bottom1
= {}
1147 edit
.draw(Editor_state
)
1148 local y
= Editor_state
.top
1149 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1150 y
= y
+ Editor_state
.line_height
1151 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1152 y
= y
+ Editor_state
.line_height
1153 App
.screen
.check(y
, 'ghij', 'baseline/screen:3')
1154 -- after hitting pagedown the screen scrolls down to start of a long line
1155 edit
.run_after_keychord(Editor_state
, 'pagedown')
1156 check_eq(Editor_state
.screen_top1
.line
, 3, 'baseline2/screen_top')
1157 check_eq(Editor_state
.cursor1
.line
, 3, 'baseline2/cursor:line')
1158 check_eq(Editor_state
.cursor1
.pos
, 1, 'baseline2/cursor:pos')
1159 -- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll up
1160 edit
.run_after_keychord(Editor_state
, 'down')
1161 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top')
1162 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1163 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1164 y
= Editor_state
.top
1165 App
.screen
.check(y
, 'ghij', 'screen:1')
1166 y
= y
+ Editor_state
.line_height
1167 App
.screen
.check(y
, 'kl', 'screen:2')
1168 y
= y
+ Editor_state
.line_height
1169 App
.screen
.check(y
, 'mno', 'screen:3')
1172 function test_up_arrow_moves_cursor()
1173 -- display the first 3 lines with the cursor on the bottom line
1174 App
.screen
.init
{width
=120, height
=60}
1175 Editor_state
= edit
.initialize_test_state()
1176 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1177 Text
.redraw_all(Editor_state
)
1178 Editor_state
.cursor1
= {line
=3, pos
=1}
1179 Editor_state
.screen_top1
= {line
=1, pos
=1}
1180 Editor_state
.screen_bottom1
= {}
1181 edit
.draw(Editor_state
)
1182 local y
= Editor_state
.top
1183 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1184 y
= y
+ Editor_state
.line_height
1185 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1186 y
= y
+ Editor_state
.line_height
1187 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1188 -- after hitting the up arrow the cursor moves up by 1 line
1189 edit
.run_after_keychord(Editor_state
, 'up')
1190 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1191 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor')
1192 -- the screen is unchanged
1193 y
= Editor_state
.top
1194 App
.screen
.check(y
, 'abc', 'screen:1')
1195 y
= y
+ Editor_state
.line_height
1196 App
.screen
.check(y
, 'def', 'screen:2')
1197 y
= y
+ Editor_state
.line_height
1198 App
.screen
.check(y
, 'ghi', 'screen:3')
1201 function test_up_arrow_skips_drawing()
1202 -- some lines of text with a drawing intermixed
1203 local drawing_width
= 50
1204 App
.screen
.init
{width
=Editor_state
.left
+drawing_width
, height
=100}
1205 Editor_state
= edit
.initialize_test_state()
1206 Editor_state
.lines
= load_array
{'abc', -- height 15
1207 '```lines', '```', -- height 25
1209 Text
.redraw_all(Editor_state
)
1210 Editor_state
.cursor1
= {line
=3, pos
=1}
1211 Editor_state
.screen_top1
= {line
=1, pos
=1}
1212 Editor_state
.screen_bottom1
= {}
1213 edit
.draw(Editor_state
)
1214 local y
= Editor_state
.top
1215 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1216 y
= y
+ Editor_state
.line_height
1217 local drawing_height
= Drawing_padding_height
+ drawing_width
/2 -- default
1218 y
= y
+ drawing_height
1219 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1220 check(Editor_state
.cursor_x
, 'baseline/cursor_x')
1221 -- after hitting the up arrow the cursor moves up by 2 lines, skipping the drawing
1222 edit
.run_after_keychord(Editor_state
, 'up')
1223 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1226 function test_up_arrow_scrolls_up_by_one_line()
1227 -- display the lines 2/3/4 with the cursor on line 2
1228 App
.screen
.init
{width
=120, height
=60}
1229 Editor_state
= edit
.initialize_test_state()
1230 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1231 Text
.redraw_all(Editor_state
)
1232 Editor_state
.cursor1
= {line
=2, pos
=1}
1233 Editor_state
.screen_top1
= {line
=2, pos
=1}
1234 Editor_state
.screen_bottom1
= {}
1235 edit
.draw(Editor_state
)
1236 local y
= Editor_state
.top
1237 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1238 y
= y
+ Editor_state
.line_height
1239 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1240 y
= y
+ Editor_state
.line_height
1241 App
.screen
.check(y
, 'jkl', 'baseline/screen:3')
1242 -- after hitting the up arrow the screen scrolls up by one line
1243 edit
.run_after_keychord(Editor_state
, 'up')
1244 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1245 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1246 y
= Editor_state
.top
1247 App
.screen
.check(y
, 'abc', 'screen:1')
1248 y
= y
+ Editor_state
.line_height
1249 App
.screen
.check(y
, 'def', 'screen:2')
1250 y
= y
+ Editor_state
.line_height
1251 App
.screen
.check(y
, 'ghi', 'screen:3')
1254 function test_up_arrow_scrolls_up_by_one_line_skipping_drawing()
1255 -- display lines 3/4/5 with a drawing just off screen at line 2
1256 App
.screen
.init
{width
=120, height
=60}
1257 Editor_state
= edit
.initialize_test_state()
1258 Editor_state
.lines
= load_array
{'abc', '```lines', '```', 'def', 'ghi', 'jkl'}
1259 Text
.redraw_all(Editor_state
)
1260 Editor_state
.cursor1
= {line
=3, pos
=1}
1261 Editor_state
.screen_top1
= {line
=3, pos
=1}
1262 Editor_state
.screen_bottom1
= {}
1263 edit
.draw(Editor_state
)
1264 local y
= Editor_state
.top
1265 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1266 y
= y
+ Editor_state
.line_height
1267 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1268 y
= y
+ Editor_state
.line_height
1269 App
.screen
.check(y
, 'jkl', 'baseline/screen:3')
1270 -- after hitting the up arrow the screen scrolls up to previous text line
1271 edit
.run_after_keychord(Editor_state
, 'up')
1272 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1273 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1276 function test_up_arrow_scrolls_up_by_one_screen_line()
1277 -- display lines starting from second screen line of a line
1278 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1279 Editor_state
= edit
.initialize_test_state()
1280 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1281 Text
.redraw_all(Editor_state
)
1282 Editor_state
.cursor1
= {line
=3, pos
=6}
1283 Editor_state
.screen_top1
= {line
=3, pos
=5}
1284 Editor_state
.screen_bottom1
= {}
1285 edit
.draw(Editor_state
)
1286 local y
= Editor_state
.top
1287 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1288 y
= y
+ Editor_state
.line_height
1289 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1290 -- after hitting the up arrow the screen scrolls up to first screen line
1291 edit
.run_after_keychord(Editor_state
, 'up')
1292 y
= Editor_state
.top
1293 App
.screen
.check(y
, 'ghi ', 'screen:1')
1294 y
= y
+ Editor_state
.line_height
1295 App
.screen
.check(y
, 'jkl', 'screen:2')
1296 y
= y
+ Editor_state
.line_height
1297 App
.screen
.check(y
, 'mno', 'screen:3')
1298 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1299 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1300 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1301 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1304 function test_up_arrow_scrolls_up_to_final_screen_line()
1305 -- display lines starting just after a long line
1306 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1307 Editor_state
= edit
.initialize_test_state()
1308 Editor_state
.lines
= load_array
{'abc def', 'ghi', 'jkl', 'mno'}
1309 Text
.redraw_all(Editor_state
)
1310 Editor_state
.cursor1
= {line
=2, pos
=1}
1311 Editor_state
.screen_top1
= {line
=2, pos
=1}
1312 Editor_state
.screen_bottom1
= {}
1313 edit
.draw(Editor_state
)
1314 local y
= Editor_state
.top
1315 App
.screen
.check(y
, 'ghi', 'baseline/screen:1')
1316 y
= y
+ Editor_state
.line_height
1317 App
.screen
.check(y
, 'jkl', 'baseline/screen:2')
1318 y
= y
+ Editor_state
.line_height
1319 App
.screen
.check(y
, 'mno', 'baseline/screen:3')
1320 -- after hitting the up arrow the screen scrolls up to final screen line of previous line
1321 edit
.run_after_keychord(Editor_state
, 'up')
1322 y
= Editor_state
.top
1323 App
.screen
.check(y
, 'def', 'screen:1')
1324 y
= y
+ Editor_state
.line_height
1325 App
.screen
.check(y
, 'ghi', 'screen:2')
1326 y
= y
+ Editor_state
.line_height
1327 App
.screen
.check(y
, 'jkl', 'screen:3')
1328 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
1329 check_eq(Editor_state
.screen_top1
.pos
, 5, 'screen_top:pos')
1330 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1331 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1334 function test_up_arrow_scrolls_up_to_empty_line()
1335 -- display a screenful of text with an empty line just above it outside the screen
1336 App
.screen
.init
{width
=120, height
=60}
1337 Editor_state
= edit
.initialize_test_state()
1338 Editor_state
.lines
= load_array
{'', 'abc', 'def', 'ghi', 'jkl'}
1339 Text
.redraw_all(Editor_state
)
1340 Editor_state
.cursor1
= {line
=2, pos
=1}
1341 Editor_state
.screen_top1
= {line
=2, pos
=1}
1342 Editor_state
.screen_bottom1
= {}
1343 edit
.draw(Editor_state
)
1344 local y
= Editor_state
.top
1345 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1346 y
= y
+ Editor_state
.line_height
1347 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1348 y
= y
+ Editor_state
.line_height
1349 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1350 -- after hitting the up arrow the screen scrolls up by one line
1351 edit
.run_after_keychord(Editor_state
, 'up')
1352 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1353 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1354 y
= Editor_state
.top
1356 y
= y
+ Editor_state
.line_height
1357 App
.screen
.check(y
, 'abc', 'screen:2')
1358 y
= y
+ Editor_state
.line_height
1359 App
.screen
.check(y
, 'def', 'screen:3')
1362 function test_pageup()
1363 App
.screen
.init
{width
=120, height
=45}
1364 Editor_state
= edit
.initialize_test_state()
1365 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi'}
1366 Text
.redraw_all(Editor_state
)
1367 Editor_state
.cursor1
= {line
=2, pos
=1}
1368 Editor_state
.screen_top1
= {line
=2, pos
=1}
1369 Editor_state
.screen_bottom1
= {}
1370 -- initially the last two lines are displayed
1371 edit
.draw(Editor_state
)
1372 local y
= Editor_state
.top
1373 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1374 y
= y
+ Editor_state
.line_height
1375 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1376 -- after pageup the cursor goes to first line
1377 edit
.run_after_keychord(Editor_state
, 'pageup')
1378 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1379 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1380 y
= Editor_state
.top
1381 App
.screen
.check(y
, 'abc', 'screen:1')
1382 y
= y
+ Editor_state
.line_height
1383 App
.screen
.check(y
, 'def', 'screen:2')
1386 function test_pageup_scrolls_up_by_screen_line()
1387 -- display the first three lines with the cursor on the bottom line
1388 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1389 Editor_state
= edit
.initialize_test_state()
1390 Editor_state
.lines
= load_array
{'abc def', 'ghi', 'jkl', 'mno'}
1391 Text
.redraw_all(Editor_state
)
1392 Editor_state
.cursor1
= {line
=2, pos
=1}
1393 Editor_state
.screen_top1
= {line
=2, pos
=1}
1394 Editor_state
.screen_bottom1
= {}
1395 edit
.draw(Editor_state
)
1396 local y
= Editor_state
.top
1397 App
.screen
.check(y
, 'ghi', 'baseline/screen:1')
1398 y
= y
+ Editor_state
.line_height
1399 App
.screen
.check(y
, 'jkl', 'baseline/screen:2')
1400 y
= y
+ Editor_state
.line_height
1401 App
.screen
.check(y
, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1402 -- after hitting the page-up key the screen scrolls up to top
1403 edit
.run_after_keychord(Editor_state
, 'pageup')
1404 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1405 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1406 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1407 y
= Editor_state
.top
1408 App
.screen
.check(y
, 'abc ', 'screen:1')
1409 y
= y
+ Editor_state
.line_height
1410 App
.screen
.check(y
, 'def', 'screen:2')
1411 y
= y
+ Editor_state
.line_height
1412 App
.screen
.check(y
, 'ghi', 'screen:3')
1415 function test_pageup_scrolls_up_from_middle_screen_line()
1416 -- display a few lines starting from the middle of a line (Editor_state.cursor1.pos > 1)
1417 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1418 Editor_state
= edit
.initialize_test_state()
1419 Editor_state
.lines
= load_array
{'abc def', 'ghi jkl', 'mno'}
1420 Text
.redraw_all(Editor_state
)
1421 Editor_state
.cursor1
= {line
=2, pos
=5}
1422 Editor_state
.screen_top1
= {line
=2, pos
=5}
1423 Editor_state
.screen_bottom1
= {}
1424 edit
.draw(Editor_state
)
1425 local y
= Editor_state
.top
1426 App
.screen
.check(y
, 'jkl', 'baseline/screen:2')
1427 y
= y
+ Editor_state
.line_height
1428 App
.screen
.check(y
, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1429 -- after hitting the page-up key the screen scrolls up to top
1430 edit
.run_after_keychord(Editor_state
, 'pageup')
1431 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1432 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1433 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1434 y
= Editor_state
.top
1435 App
.screen
.check(y
, 'abc ', 'screen:1')
1436 y
= y
+ Editor_state
.line_height
1437 App
.screen
.check(y
, 'def', 'screen:2')
1438 y
= y
+ Editor_state
.line_height
1439 App
.screen
.check(y
, 'ghi ', 'screen:3')
1442 function test_enter_on_bottom_line_scrolls_down()
1443 -- display a few lines with cursor on bottom line
1444 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1445 Editor_state
= edit
.initialize_test_state()
1446 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1447 Text
.redraw_all(Editor_state
)
1448 Editor_state
.cursor1
= {line
=3, pos
=2}
1449 Editor_state
.screen_top1
= {line
=1, pos
=1}
1450 Editor_state
.screen_bottom1
= {}
1451 edit
.draw(Editor_state
)
1452 local y
= Editor_state
.top
1453 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1454 y
= y
+ Editor_state
.line_height
1455 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1456 y
= y
+ Editor_state
.line_height
1457 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1458 -- after hitting the enter key the screen scrolls down
1459 edit
.run_after_keychord(Editor_state
, 'return')
1460 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1461 check_eq(Editor_state
.cursor1
.line
, 4, 'cursor:line')
1462 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1463 y
= Editor_state
.top
1464 App
.screen
.check(y
, 'def', 'screen:1')
1465 y
= y
+ Editor_state
.line_height
1466 App
.screen
.check(y
, 'g', 'screen:2')
1467 y
= y
+ Editor_state
.line_height
1468 App
.screen
.check(y
, 'hi', 'screen:3')
1471 function test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1472 -- display just the bottom line on screen
1473 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1474 Editor_state
= edit
.initialize_test_state()
1475 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1476 Text
.redraw_all(Editor_state
)
1477 Editor_state
.cursor1
= {line
=4, pos
=2}
1478 Editor_state
.screen_top1
= {line
=4, pos
=1}
1479 Editor_state
.screen_bottom1
= {}
1480 edit
.draw(Editor_state
)
1481 local y
= Editor_state
.top
1482 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1483 -- after hitting the enter key the screen does not scroll down
1484 edit
.run_after_keychord(Editor_state
, 'return')
1485 check_eq(Editor_state
.screen_top1
.line
, 4, 'screen_top')
1486 check_eq(Editor_state
.cursor1
.line
, 5, 'cursor:line')
1487 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1488 y
= Editor_state
.top
1489 App
.screen
.check(y
, 'j', 'screen:1')
1490 y
= y
+ Editor_state
.line_height
1491 App
.screen
.check(y
, 'kl', 'screen:2')
1494 function test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1495 -- display just an empty bottom line on screen
1496 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1497 Editor_state
= edit
.initialize_test_state()
1498 Editor_state
.lines
= load_array
{'abc', ''}
1499 Text
.redraw_all(Editor_state
)
1500 Editor_state
.cursor1
= {line
=2, pos
=1}
1501 Editor_state
.screen_top1
= {line
=2, pos
=1}
1502 Editor_state
.screen_bottom1
= {}
1503 edit
.draw(Editor_state
)
1504 -- after hitting the inserting_text key the screen does not scroll down
1505 edit
.run_after_text_input(Editor_state
, 'a')
1506 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1507 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1508 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
1509 local y
= Editor_state
.top
1510 App
.screen
.check(y
, 'a', 'screen:1')
1513 function test_typing_on_bottom_line_scrolls_down()
1514 -- display a few lines with cursor on bottom line
1515 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1516 Editor_state
= edit
.initialize_test_state()
1517 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1518 Text
.redraw_all(Editor_state
)
1519 Editor_state
.cursor1
= {line
=3, pos
=4}
1520 Editor_state
.screen_top1
= {line
=1, pos
=1}
1521 Editor_state
.screen_bottom1
= {}
1522 edit
.draw(Editor_state
)
1523 local y
= Editor_state
.top
1524 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1525 y
= y
+ Editor_state
.line_height
1526 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1527 y
= y
+ Editor_state
.line_height
1528 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1529 -- after typing something the line wraps and the screen scrolls down
1530 edit
.run_after_text_input(Editor_state
, 'j')
1531 edit
.run_after_text_input(Editor_state
, 'k')
1532 edit
.run_after_text_input(Editor_state
, 'l')
1533 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1534 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1535 check_eq(Editor_state
.cursor1
.pos
, 7, 'cursor:pos')
1536 y
= Editor_state
.top
1537 App
.screen
.check(y
, 'def', 'screen:1')
1538 y
= y
+ Editor_state
.line_height
1539 App
.screen
.check(y
, 'ghij', 'screen:2')
1540 y
= y
+ Editor_state
.line_height
1541 App
.screen
.check(y
, 'kl', 'screen:3')
1544 function test_left_arrow_scrolls_up_in_wrapped_line()
1545 -- display lines starting from second screen line of a line
1546 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1547 Editor_state
= edit
.initialize_test_state()
1548 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1549 Text
.redraw_all(Editor_state
)
1550 Editor_state
.screen_top1
= {line
=3, pos
=5}
1551 Editor_state
.screen_bottom1
= {}
1552 -- cursor is at top of screen
1553 Editor_state
.cursor1
= {line
=3, pos
=5}
1554 edit
.draw(Editor_state
)
1555 local y
= Editor_state
.top
1556 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1557 y
= y
+ Editor_state
.line_height
1558 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1559 -- after hitting the left arrow the screen scrolls up to first screen line
1560 edit
.run_after_keychord(Editor_state
, 'left')
1561 y
= Editor_state
.top
1562 App
.screen
.check(y
, 'ghi ', 'screen:1')
1563 y
= y
+ Editor_state
.line_height
1564 App
.screen
.check(y
, 'jkl', 'screen:2')
1565 y
= y
+ Editor_state
.line_height
1566 App
.screen
.check(y
, 'mno', 'screen:3')
1567 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1568 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1569 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1570 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
1573 function test_right_arrow_scrolls_down_in_wrapped_line()
1574 -- display the first three lines with the cursor on the bottom line
1575 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1576 Editor_state
= edit
.initialize_test_state()
1577 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1578 Text
.redraw_all(Editor_state
)
1579 Editor_state
.screen_top1
= {line
=1, pos
=1}
1580 Editor_state
.screen_bottom1
= {}
1581 -- cursor is at bottom right of screen
1582 Editor_state
.cursor1
= {line
=3, pos
=5}
1583 edit
.draw(Editor_state
)
1584 local y
= Editor_state
.top
1585 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1586 y
= y
+ Editor_state
.line_height
1587 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1588 y
= y
+ Editor_state
.line_height
1589 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1590 -- after hitting the right arrow the screen scrolls down by one line
1591 edit
.run_after_keychord(Editor_state
, 'right')
1592 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1593 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1594 check_eq(Editor_state
.cursor1
.pos
, 6, 'cursor:pos')
1595 y
= Editor_state
.top
1596 App
.screen
.check(y
, 'def', 'screen:1')
1597 y
= y
+ Editor_state
.line_height
1598 App
.screen
.check(y
, 'ghi ', 'screen:2')
1599 y
= y
+ Editor_state
.line_height
1600 App
.screen
.check(y
, 'jkl', 'screen:3')
1603 function test_home_scrolls_up_in_wrapped_line()
1604 -- display lines starting from second screen line of a line
1605 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1606 Editor_state
= edit
.initialize_test_state()
1607 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1608 Text
.redraw_all(Editor_state
)
1609 Editor_state
.screen_top1
= {line
=3, pos
=5}
1610 Editor_state
.screen_bottom1
= {}
1611 -- cursor is at top of screen
1612 Editor_state
.cursor1
= {line
=3, pos
=5}
1613 edit
.draw(Editor_state
)
1614 local y
= Editor_state
.top
1615 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1616 y
= y
+ Editor_state
.line_height
1617 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1618 -- after hitting home the screen scrolls up to first screen line
1619 edit
.run_after_keychord(Editor_state
, 'home')
1620 y
= Editor_state
.top
1621 App
.screen
.check(y
, 'ghi ', 'screen:1')
1622 y
= y
+ Editor_state
.line_height
1623 App
.screen
.check(y
, 'jkl', 'screen:2')
1624 y
= y
+ Editor_state
.line_height
1625 App
.screen
.check(y
, 'mno', 'screen:3')
1626 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1627 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1628 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1629 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1632 function test_end_scrolls_down_in_wrapped_line()
1633 -- display the first three lines with the cursor on the bottom line
1634 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1635 Editor_state
= edit
.initialize_test_state()
1636 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1637 Text
.redraw_all(Editor_state
)
1638 Editor_state
.screen_top1
= {line
=1, pos
=1}
1639 Editor_state
.screen_bottom1
= {}
1640 -- cursor is at bottom right of screen
1641 Editor_state
.cursor1
= {line
=3, pos
=5}
1642 edit
.draw(Editor_state
)
1643 local y
= Editor_state
.top
1644 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1645 y
= y
+ Editor_state
.line_height
1646 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1647 y
= y
+ Editor_state
.line_height
1648 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1649 -- after hitting end the screen scrolls down by one line
1650 edit
.run_after_keychord(Editor_state
, 'end')
1651 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1652 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1653 check_eq(Editor_state
.cursor1
.pos
, 8, 'cursor:pos')
1654 y
= Editor_state
.top
1655 App
.screen
.check(y
, 'def', 'screen:1')
1656 y
= y
+ Editor_state
.line_height
1657 App
.screen
.check(y
, 'ghi ', 'screen:2')
1658 y
= y
+ Editor_state
.line_height
1659 App
.screen
.check(y
, 'jkl', 'screen:3')
1662 function test_position_cursor_on_recently_edited_wrapping_line()
1663 -- draw a line wrapping over 2 screen lines
1664 App
.screen
.init
{width
=100, height
=200}
1665 Editor_state
= edit
.initialize_test_state()
1666 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr ', 'xyz'}
1667 Text
.redraw_all(Editor_state
)
1668 Editor_state
.cursor1
= {line
=1, pos
=25}
1669 Editor_state
.screen_top1
= {line
=1, pos
=1}
1670 Editor_state
.screen_bottom1
= {}
1671 edit
.draw(Editor_state
)
1672 local y
= Editor_state
.top
1673 App
.screen
.check(y
, 'abc def ghi ', 'baseline1/screen:1')
1674 y
= y
+ Editor_state
.line_height
1675 App
.screen
.check(y
, 'jkl mno pqr ', 'baseline1/screen:2')
1676 y
= y
+ Editor_state
.line_height
1677 App
.screen
.check(y
, 'xyz', 'baseline1/screen:3')
1678 -- add to the line until it's wrapping over 3 screen lines
1679 edit
.run_after_text_input(Editor_state
, 's')
1680 edit
.run_after_text_input(Editor_state
, 't')
1681 edit
.run_after_text_input(Editor_state
, 'u')
1682 check_eq(Editor_state
.cursor1
.pos
, 28, 'cursor:pos')
1683 y
= Editor_state
.top
1684 App
.screen
.check(y
, 'abc def ghi ', 'baseline2/screen:1')
1685 y
= y
+ Editor_state
.line_height
1686 App
.screen
.check(y
, 'jkl mno pqr ', 'baseline2/screen:2')
1687 y
= y
+ Editor_state
.line_height
1688 App
.screen
.check(y
, 'stu', 'baseline2/screen:3')
1689 -- try to move the cursor earlier in the third screen line by clicking the mouse
1690 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+Editor_state
.line_height
*2+5, 1)
1691 -- cursor should move
1692 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1693 check_eq(Editor_state
.cursor1
.pos
, 26, 'cursor:pos')
1696 function test_backspace_can_scroll_up()
1697 -- display the lines 2/3/4 with the cursor on line 2
1698 App
.screen
.init
{width
=120, height
=60}
1699 Editor_state
= edit
.initialize_test_state()
1700 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1701 Text
.redraw_all(Editor_state
)
1702 Editor_state
.cursor1
= {line
=2, pos
=1}
1703 Editor_state
.screen_top1
= {line
=2, pos
=1}
1704 Editor_state
.screen_bottom1
= {}
1705 edit
.draw(Editor_state
)
1706 local y
= Editor_state
.top
1707 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1708 y
= y
+ Editor_state
.line_height
1709 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1710 y
= y
+ Editor_state
.line_height
1711 App
.screen
.check(y
, 'jkl', 'baseline/screen:3')
1712 -- after hitting backspace the screen scrolls up by one line
1713 edit
.run_after_keychord(Editor_state
, 'backspace')
1714 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1715 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1716 y
= Editor_state
.top
1717 App
.screen
.check(y
, 'abcdef', 'screen:1')
1718 y
= y
+ Editor_state
.line_height
1719 App
.screen
.check(y
, 'ghi', 'screen:2')
1720 y
= y
+ Editor_state
.line_height
1721 App
.screen
.check(y
, 'jkl', 'screen:3')
1724 function test_backspace_can_scroll_up_screen_line()
1725 -- display lines starting from second screen line of a line
1726 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1727 Editor_state
= edit
.initialize_test_state()
1728 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1729 Text
.redraw_all(Editor_state
)
1730 Editor_state
.cursor1
= {line
=3, pos
=5}
1731 Editor_state
.screen_top1
= {line
=3, pos
=5}
1732 Editor_state
.screen_bottom1
= {}
1733 edit
.draw(Editor_state
)
1734 local y
= Editor_state
.top
1735 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1736 y
= y
+ Editor_state
.line_height
1737 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1738 -- after hitting backspace the screen scrolls up by one screen line
1739 edit
.run_after_keychord(Editor_state
, 'backspace')
1740 y
= Editor_state
.top
1741 App
.screen
.check(y
, 'ghij', 'screen:1')
1742 y
= y
+ Editor_state
.line_height
1743 App
.screen
.check(y
, 'kl', 'screen:2')
1744 y
= y
+ Editor_state
.line_height
1745 App
.screen
.check(y
, 'mno', 'screen:3')
1746 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1747 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1748 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1749 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
1752 function test_backspace_past_line_boundary()
1753 -- position cursor at start of a (non-first) line
1754 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1755 Editor_state
= edit
.initialize_test_state()
1756 Editor_state
.lines
= load_array
{'abc', 'def'}
1757 Text
.redraw_all(Editor_state
)
1758 Editor_state
.cursor1
= {line
=2, pos
=1}
1759 -- backspace joins with previous line
1760 edit
.run_after_keychord(Editor_state
, 'backspace')
1761 check_eq(Editor_state
.lines
[1].data
, 'abcdef', 'check')
1764 -- some tests for operating over selections created using Shift- chords
1765 -- we're just testing delete_selection, and it works the same for all keys
1767 function test_backspace_over_selection()
1768 -- select just one character within a line with cursor before selection
1769 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1770 Editor_state
= edit
.initialize_test_state()
1771 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1772 Text
.redraw_all(Editor_state
)
1773 Editor_state
.cursor1
= {line
=1, pos
=1}
1774 Editor_state
.selection1
= {line
=1, pos
=2}
1775 -- backspace deletes the selected character, even though it's after the cursor
1776 edit
.run_after_keychord(Editor_state
, 'backspace')
1777 check_eq(Editor_state
.lines
[1].data
, 'bc', 'data')
1778 -- cursor (remains) at start of selection
1779 check_eq(Editor_state
.cursor1
.line
, 1, '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_backspace_over_selection_reverse()
1786 -- select just one character within a line with cursor after selection
1787 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1788 Editor_state
= edit
.initialize_test_state()
1789 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1790 Text
.redraw_all(Editor_state
)
1791 Editor_state
.cursor1
= {line
=1, pos
=2}
1792 Editor_state
.selection1
= {line
=1, pos
=1}
1793 -- backspace deletes the selected character
1794 edit
.run_after_keychord(Editor_state
, 'backspace')
1795 check_eq(Editor_state
.lines
[1].data
, 'bc', 'data')
1796 -- cursor moves to start of selection
1797 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1798 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1799 -- selection is cleared
1800 check_nil(Editor_state
.selection1
.line
, 'selection')
1803 function test_backspace_over_multiple_lines()
1804 -- select just one character within a line with cursor after selection
1805 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1806 Editor_state
= edit
.initialize_test_state()
1807 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1808 Text
.redraw_all(Editor_state
)
1809 Editor_state
.cursor1
= {line
=1, pos
=2}
1810 Editor_state
.selection1
= {line
=4, pos
=2}
1811 -- backspace deletes the region and joins the remaining portions of lines on either side
1812 edit
.run_after_keychord(Editor_state
, 'backspace')
1813 check_eq(Editor_state
.lines
[1].data
, 'akl', 'data:1')
1814 check_eq(Editor_state
.lines
[2].data
, 'mno', 'data:2')
1815 -- cursor remains at start of selection
1816 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1817 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
1818 -- selection is cleared
1819 check_nil(Editor_state
.selection1
.line
, 'selection')
1822 function test_backspace_to_end_of_line()
1823 -- select region from cursor to end of line
1824 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1825 Editor_state
= edit
.initialize_test_state()
1826 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1827 Text
.redraw_all(Editor_state
)
1828 Editor_state
.cursor1
= {line
=1, pos
=2}
1829 Editor_state
.selection1
= {line
=1, pos
=4}
1830 -- backspace deletes rest of line without joining to any other line
1831 edit
.run_after_keychord(Editor_state
, 'backspace')
1832 check_eq(Editor_state
.lines
[1].data
, 'a', 'data:1')
1833 check_eq(Editor_state
.lines
[2].data
, 'def', 'data:2')
1834 -- cursor remains at start of selection
1835 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1836 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
1837 -- selection is cleared
1838 check_nil(Editor_state
.selection1
.line
, 'selection')
1841 function test_backspace_to_start_of_line()
1842 -- select region from cursor to start of line
1843 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1844 Editor_state
= edit
.initialize_test_state()
1845 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1846 Text
.redraw_all(Editor_state
)
1847 Editor_state
.cursor1
= {line
=2, pos
=1}
1848 Editor_state
.selection1
= {line
=2, pos
=3}
1849 -- backspace deletes beginning of line without joining to any other line
1850 edit
.run_after_keychord(Editor_state
, 'backspace')
1851 check_eq(Editor_state
.lines
[1].data
, 'abc', 'data:1')
1852 check_eq(Editor_state
.lines
[2].data
, 'f', 'data:2')
1853 -- cursor remains at start of selection
1854 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1855 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1856 -- selection is cleared
1857 check_nil(Editor_state
.selection1
.line
, 'selection')
1860 function test_undo_insert_text()
1861 App
.screen
.init
{width
=120, height
=60}
1862 Editor_state
= edit
.initialize_test_state()
1863 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
1864 Text
.redraw_all(Editor_state
)
1865 Editor_state
.cursor1
= {line
=2, pos
=4}
1866 Editor_state
.screen_top1
= {line
=1, pos
=1}
1867 Editor_state
.screen_bottom1
= {}
1868 -- insert a character
1869 edit
.draw(Editor_state
)
1870 edit
.run_after_text_input(Editor_state
, 'g')
1871 check_eq(Editor_state
.cursor1
.line
, 2, 'baseline/cursor:line')
1872 check_eq(Editor_state
.cursor1
.pos
, 5, 'baseline/cursor:pos')
1873 check_nil(Editor_state
.selection1
.line
, 'baseline/selection:line')
1874 check_nil(Editor_state
.selection1
.pos
, 'baseline/selection:pos')
1875 local y
= Editor_state
.top
1876 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1877 y
= y
+ Editor_state
.line_height
1878 App
.screen
.check(y
, 'defg', 'baseline/screen:2')
1879 y
= y
+ Editor_state
.line_height
1880 App
.screen
.check(y
, 'xyz', 'baseline/screen:3')
1882 edit
.run_after_keychord(Editor_state
, 'C-z')
1883 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1884 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
1885 check_nil(Editor_state
.selection1
.line
, 'selection:line')
1886 check_nil(Editor_state
.selection1
.pos
, 'selection:pos')
1887 y
= Editor_state
.top
1888 App
.screen
.check(y
, 'abc', 'screen:1')
1889 y
= y
+ Editor_state
.line_height
1890 App
.screen
.check(y
, 'def', 'screen:2')
1891 y
= y
+ Editor_state
.line_height
1892 App
.screen
.check(y
, 'xyz', 'screen:3')
1895 function test_undo_delete_text()
1896 App
.screen
.init
{width
=120, height
=60}
1897 Editor_state
= edit
.initialize_test_state()
1898 Editor_state
.lines
= load_array
{'abc', 'defg', 'xyz'}
1899 Text
.redraw_all(Editor_state
)
1900 Editor_state
.cursor1
= {line
=2, pos
=5}
1901 Editor_state
.screen_top1
= {line
=1, pos
=1}
1902 Editor_state
.screen_bottom1
= {}
1903 -- delete a character
1904 edit
.run_after_keychord(Editor_state
, 'backspace')
1905 check_eq(Editor_state
.cursor1
.line
, 2, 'baseline/cursor:line')
1906 check_eq(Editor_state
.cursor1
.pos
, 4, 'baseline/cursor:pos')
1907 check_nil(Editor_state
.selection1
.line
, 'baseline/selection:line')
1908 check_nil(Editor_state
.selection1
.pos
, 'baseline/selection:pos')
1909 local y
= Editor_state
.top
1910 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1911 y
= y
+ Editor_state
.line_height
1912 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1913 y
= y
+ Editor_state
.line_height
1914 App
.screen
.check(y
, 'xyz', 'baseline/screen:3')
1916 --? -- after undo, the backspaced key is selected
1917 edit
.run_after_keychord(Editor_state
, 'C-z')
1918 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1919 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1920 check_nil(Editor_state
.selection1
.line
, 'selection:line')
1921 check_nil(Editor_state
.selection1
.pos
, 'selection:pos')
1922 --? check_eq(Editor_state.selection1.line, 2, 'selection:line')
1923 --? check_eq(Editor_state.selection1.pos, 4, 'selection:pos')
1924 y
= Editor_state
.top
1925 App
.screen
.check(y
, 'abc', 'screen:1')
1926 y
= y
+ Editor_state
.line_height
1927 App
.screen
.check(y
, 'defg', 'screen:2')
1928 y
= y
+ Editor_state
.line_height
1929 App
.screen
.check(y
, 'xyz', 'screen:3')
1932 function test_undo_restores_selection()
1933 -- display a line of text with some part selected
1934 App
.screen
.init
{width
=75, height
=80}
1935 Editor_state
= edit
.initialize_test_state()
1936 Editor_state
.lines
= load_array
{'abc'}
1937 Text
.redraw_all(Editor_state
)
1938 Editor_state
.cursor1
= {line
=1, pos
=1}
1939 Editor_state
.selection1
= {line
=1, pos
=2}
1940 Editor_state
.screen_top1
= {line
=1, pos
=1}
1941 Editor_state
.screen_bottom1
= {}
1942 edit
.draw(Editor_state
)
1943 -- delete selected text
1944 edit
.run_after_text_input(Editor_state
, 'x')
1945 check_eq(Editor_state
.lines
[1].data
, 'xbc', 'baseline')
1946 check_nil(Editor_state
.selection1
.line
, 'baseline:selection')
1948 edit
.run_after_keychord(Editor_state
, 'C-z')
1949 edit
.run_after_keychord(Editor_state
, 'C-z')
1950 -- selection is restored
1951 check_eq(Editor_state
.selection1
.line
, 1, 'line')
1952 check_eq(Editor_state
.selection1
.pos
, 2, 'pos')
1955 function test_search()
1956 App
.screen
.init
{width
=120, height
=60}
1957 Editor_state
= edit
.initialize_test_state()
1958 Editor_state
.lines
= load_array
{'```lines', '```', 'def', 'ghi', '’deg'} -- contains unicode quote in final line
1959 Text
.redraw_all(Editor_state
)
1960 Editor_state
.cursor1
= {line
=1, pos
=1}
1961 Editor_state
.screen_top1
= {line
=1, pos
=1}
1962 Editor_state
.screen_bottom1
= {}
1963 edit
.draw(Editor_state
)
1964 -- search for a string
1965 edit
.run_after_keychord(Editor_state
, 'C-f')
1966 edit
.run_after_text_input(Editor_state
, 'd')
1967 edit
.run_after_keychord(Editor_state
, 'return')
1968 check_eq(Editor_state
.cursor1
.line
, 2, '1/cursor:line')
1969 check_eq(Editor_state
.cursor1
.pos
, 1, '1/cursor:pos')
1971 Editor_state
.cursor1
= {line
=1, pos
=1}
1972 Editor_state
.screen_top1
= {line
=1, pos
=1}
1973 -- search for second occurrence
1974 edit
.run_after_keychord(Editor_state
, 'C-f')
1975 edit
.run_after_text_input(Editor_state
, 'de')
1976 edit
.run_after_keychord(Editor_state
, 'down')
1977 edit
.run_after_keychord(Editor_state
, 'return')
1978 check_eq(Editor_state
.cursor1
.line
, 4, '2/cursor:line')
1979 check_eq(Editor_state
.cursor1
.pos
, 2, '2/cursor:pos')
1982 function test_search_upwards()
1983 App
.screen
.init
{width
=120, height
=60}
1984 Editor_state
= edit
.initialize_test_state()
1985 Editor_state
.lines
= load_array
{'’abc', 'abd'} -- contains unicode quote
1986 Text
.redraw_all(Editor_state
)
1987 Editor_state
.cursor1
= {line
=2, pos
=1}
1988 Editor_state
.screen_top1
= {line
=1, pos
=1}
1989 Editor_state
.screen_bottom1
= {}
1990 edit
.draw(Editor_state
)
1991 -- search for a string
1992 edit
.run_after_keychord(Editor_state
, 'C-f')
1993 edit
.run_after_text_input(Editor_state
, 'a')
1994 -- search for previous occurrence
1995 edit
.run_after_keychord(Editor_state
, 'up')
1996 check_eq(Editor_state
.cursor1
.line
, 1, '2/cursor:line')
1997 check_eq(Editor_state
.cursor1
.pos
, 2, '2/cursor:pos')
2000 function test_search_wrap()
2001 App
.screen
.init
{width
=120, height
=60}
2002 Editor_state
= edit
.initialize_test_state()
2003 Editor_state
.lines
= load_array
{'’abc', 'def'} -- contains unicode quote in first line
2004 Text
.redraw_all(Editor_state
)
2005 Editor_state
.cursor1
= {line
=2, pos
=1}
2006 Editor_state
.screen_top1
= {line
=1, pos
=1}
2007 Editor_state
.screen_bottom1
= {}
2008 edit
.draw(Editor_state
)
2009 -- search for a string
2010 edit
.run_after_keychord(Editor_state
, 'C-f')
2011 edit
.run_after_text_input(Editor_state
, 'a')
2012 edit
.run_after_keychord(Editor_state
, 'return')
2014 check_eq(Editor_state
.cursor1
.line
, 1, '1/cursor:line')
2015 check_eq(Editor_state
.cursor1
.pos
, 2, '1/cursor:pos')
2018 function test_search_wrap_upwards()
2019 App
.screen
.init
{width
=120, height
=60}
2020 Editor_state
= edit
.initialize_test_state()
2021 Editor_state
.lines
= load_array
{'abc ’abd'} -- contains unicode quote
2022 Text
.redraw_all(Editor_state
)
2023 Editor_state
.cursor1
= {line
=1, pos
=1}
2024 Editor_state
.screen_top1
= {line
=1, pos
=1}
2025 Editor_state
.screen_bottom1
= {}
2026 edit
.draw(Editor_state
)
2027 -- search upwards for a string
2028 edit
.run_after_keychord(Editor_state
, 'C-f')
2029 edit
.run_after_text_input(Editor_state
, 'a')
2030 edit
.run_after_keychord(Editor_state
, 'up')
2032 check_eq(Editor_state
.cursor1
.line
, 1, '1/cursor:line')
2033 check_eq(Editor_state
.cursor1
.pos
, 6, '1/cursor:pos')