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_often_shows_start_of_wrapping_line()
956 -- draw a few lines ending in part of a wrapping line
957 App
.screen
.init
{width
=50, height
=60}
958 Editor_state
= edit
.initialize_test_state()
959 Editor_state
.lines
= load_array
{'abc', 'def ghi jkl', 'mno'}
960 Text
.redraw_all(Editor_state
)
961 Editor_state
.cursor1
= {line
=1, pos
=1}
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 start drawing from the bottom _line_ (multiple screen lines)
972 edit
.run_after_keychord(Editor_state
, 'pagedown')
973 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top:line')
974 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
975 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
976 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
978 App
.screen
.check(y
, 'def ', 'screen:1')
979 y
= y
+ Editor_state
.line_height
980 App
.screen
.check(y
, 'ghi ', 'screen:2')
981 y
= y
+ Editor_state
.line_height
982 App
.screen
.check(y
, 'jkl', 'screen:3')
985 function test_pagedown_can_start_from_middle_of_long_wrapping_line()
986 -- draw a few lines starting from a very long wrapping line
987 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
988 Editor_state
= edit
.initialize_test_state()
989 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr stu vwx yza bcd efg hij', 'XYZ'}
990 Text
.redraw_all(Editor_state
)
991 Editor_state
.cursor1
= {line
=1, pos
=2}
992 Editor_state
.screen_top1
= {line
=1, pos
=1}
993 Editor_state
.screen_bottom1
= {}
994 edit
.draw(Editor_state
)
995 local y
= Editor_state
.top
996 App
.screen
.check(y
, 'abc ', 'baseline/screen:1')
997 y
= y
+ Editor_state
.line_height
998 App
.screen
.check(y
, 'def ', 'baseline/screen:2')
999 y
= y
+ Editor_state
.line_height
1000 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3')
1001 -- after pagedown we scroll down the very long wrapping line
1002 edit
.run_after_keychord(Editor_state
, 'pagedown')
1003 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
1004 check_eq(Editor_state
.screen_top1
.pos
, 9, 'screen_top:pos')
1005 y
= Editor_state
.top
1006 App
.screen
.check(y
, 'ghi ', 'screen:1')
1007 y
= y
+ Editor_state
.line_height
1008 App
.screen
.check(y
, 'jkl ', 'screen:2')
1009 y
= y
+ Editor_state
.line_height
1010 App
.screen
.check(y
, 'mno ', 'screen:3')
1013 function test_pagedown_never_moves_up()
1014 -- draw the final screen line of a wrapping line
1015 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1016 Editor_state
= edit
.initialize_test_state()
1017 Editor_state
.lines
= load_array
{'abc def ghi'}
1018 Text
.redraw_all(Editor_state
)
1019 Editor_state
.cursor1
= {line
=1, pos
=9}
1020 Editor_state
.screen_top1
= {line
=1, pos
=9}
1021 Editor_state
.screen_bottom1
= {}
1022 edit
.draw(Editor_state
)
1023 -- pagedown makes no change
1024 edit
.run_after_keychord(Editor_state
, 'pagedown')
1025 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
1026 check_eq(Editor_state
.screen_top1
.pos
, 9, 'screen_top:pos')
1029 function test_down_arrow_moves_cursor()
1030 App
.screen
.init
{width
=120, height
=60}
1031 Editor_state
= edit
.initialize_test_state()
1032 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1033 Text
.redraw_all(Editor_state
)
1034 Editor_state
.cursor1
= {line
=1, pos
=1}
1035 Editor_state
.screen_top1
= {line
=1, pos
=1}
1036 Editor_state
.screen_bottom1
= {}
1037 -- initially the first three lines are displayed
1038 edit
.draw(Editor_state
)
1039 local y
= Editor_state
.top
1040 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1041 y
= y
+ Editor_state
.line_height
1042 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1043 y
= y
+ Editor_state
.line_height
1044 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1045 -- after hitting the down arrow, the cursor moves down by 1 line
1046 edit
.run_after_keychord(Editor_state
, 'down')
1047 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1048 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor')
1049 -- the screen is unchanged
1050 y
= Editor_state
.top
1051 App
.screen
.check(y
, 'abc', 'screen:1')
1052 y
= y
+ Editor_state
.line_height
1053 App
.screen
.check(y
, 'def', 'screen:2')
1054 y
= y
+ Editor_state
.line_height
1055 App
.screen
.check(y
, 'ghi', 'screen:3')
1058 function test_down_arrow_skips_drawing()
1059 -- some lines of text with a drawing intermixed
1060 local drawing_width
= 50
1061 App
.screen
.init
{width
=Editor_state
.left
+drawing_width
, height
=100}
1062 Editor_state
= edit
.initialize_test_state()
1063 Editor_state
.lines
= load_array
{'abc', -- height 15
1064 '```lines', '```', -- height 25
1066 Text
.redraw_all(Editor_state
)
1067 Editor_state
.cursor1
= {line
=1, pos
=1}
1068 Editor_state
.screen_top1
= {line
=1, pos
=1}
1069 Editor_state
.screen_bottom1
= {}
1070 edit
.draw(Editor_state
)
1071 local y
= Editor_state
.top
1072 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1073 y
= y
+ Editor_state
.line_height
1074 local drawing_height
= Drawing_padding_height
+ drawing_width
/2 -- default
1075 y
= y
+ drawing_height
1076 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1077 check(Editor_state
.cursor_x
, 'baseline/cursor_x')
1078 -- after hitting the down arrow the cursor moves down by 2 lines, skipping the drawing
1079 edit
.run_after_keychord(Editor_state
, 'down')
1080 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor')
1083 function test_down_arrow_scrolls_down_by_one_line()
1084 -- display the first three lines with the cursor on the bottom line
1085 App
.screen
.init
{width
=120, height
=60}
1086 Editor_state
= edit
.initialize_test_state()
1087 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1088 Text
.redraw_all(Editor_state
)
1089 Editor_state
.cursor1
= {line
=3, pos
=1}
1090 Editor_state
.screen_top1
= {line
=1, pos
=1}
1091 Editor_state
.screen_bottom1
= {}
1092 edit
.draw(Editor_state
)
1093 local y
= Editor_state
.top
1094 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1095 y
= y
+ Editor_state
.line_height
1096 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1097 y
= y
+ Editor_state
.line_height
1098 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1099 -- after hitting the down arrow the screen scrolls down by one line
1100 edit
.run_after_keychord(Editor_state
, 'down')
1101 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1102 check_eq(Editor_state
.cursor1
.line
, 4, 'cursor')
1103 y
= Editor_state
.top
1104 App
.screen
.check(y
, 'def', 'screen:1')
1105 y
= y
+ Editor_state
.line_height
1106 App
.screen
.check(y
, 'ghi', 'screen:2')
1107 y
= y
+ Editor_state
.line_height
1108 App
.screen
.check(y
, 'jkl', 'screen:3')
1111 function test_down_arrow_scrolls_down_by_one_screen_line()
1112 -- display the first three lines with the cursor on the bottom line
1113 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1114 Editor_state
= edit
.initialize_test_state()
1115 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1116 Text
.redraw_all(Editor_state
)
1117 Editor_state
.cursor1
= {line
=3, pos
=1}
1118 Editor_state
.screen_top1
= {line
=1, pos
=1}
1119 Editor_state
.screen_bottom1
= {}
1120 edit
.draw(Editor_state
)
1121 local y
= Editor_state
.top
1122 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1123 y
= y
+ Editor_state
.line_height
1124 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1125 y
= y
+ Editor_state
.line_height
1126 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1127 -- after hitting the down arrow the screen scrolls down by one line
1128 edit
.run_after_keychord(Editor_state
, 'down')
1129 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1130 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1131 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1132 y
= Editor_state
.top
1133 App
.screen
.check(y
, 'def', 'screen:1')
1134 y
= y
+ Editor_state
.line_height
1135 App
.screen
.check(y
, 'ghi ', 'screen:2')
1136 y
= y
+ Editor_state
.line_height
1137 App
.screen
.check(y
, 'jkl', 'screen:3')
1140 function test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word()
1141 -- display the first three lines with the cursor on the bottom line
1142 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1143 Editor_state
= edit
.initialize_test_state()
1144 Editor_state
.lines
= load_array
{'abc', 'def', 'ghijkl', 'mno'}
1145 Text
.redraw_all(Editor_state
)
1146 Editor_state
.cursor1
= {line
=3, pos
=1}
1147 Editor_state
.screen_top1
= {line
=1, pos
=1}
1148 Editor_state
.screen_bottom1
= {}
1149 edit
.draw(Editor_state
)
1150 local y
= Editor_state
.top
1151 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1152 y
= y
+ Editor_state
.line_height
1153 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1154 y
= y
+ Editor_state
.line_height
1155 App
.screen
.check(y
, 'ghij', 'baseline/screen:3')
1156 -- after hitting the down arrow the screen scrolls down by one line
1157 edit
.run_after_keychord(Editor_state
, 'down')
1158 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1159 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1160 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1161 y
= Editor_state
.top
1162 App
.screen
.check(y
, 'def', 'screen:1')
1163 y
= y
+ Editor_state
.line_height
1164 App
.screen
.check(y
, 'ghij', 'screen:2')
1165 y
= y
+ Editor_state
.line_height
1166 App
.screen
.check(y
, 'kl', 'screen:3')
1169 function test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up()
1170 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1171 Editor_state
= edit
.initialize_test_state()
1172 Editor_state
.lines
= load_array
{'abc', 'def', 'ghijkl', 'mno'}
1173 Text
.redraw_all(Editor_state
)
1174 Editor_state
.cursor1
= {line
=3, pos
=1}
1175 Editor_state
.screen_top1
= {line
=1, pos
=1}
1176 Editor_state
.screen_bottom1
= {}
1177 edit
.draw(Editor_state
)
1178 local y
= Editor_state
.top
1179 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1180 y
= y
+ Editor_state
.line_height
1181 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1182 y
= y
+ Editor_state
.line_height
1183 App
.screen
.check(y
, 'ghij', 'baseline/screen:3')
1184 -- after hitting pagedown the screen scrolls down to start of a long line
1185 edit
.run_after_keychord(Editor_state
, 'pagedown')
1186 check_eq(Editor_state
.screen_top1
.line
, 3, 'baseline2/screen_top')
1187 check_eq(Editor_state
.cursor1
.line
, 3, 'baseline2/cursor:line')
1188 check_eq(Editor_state
.cursor1
.pos
, 1, 'baseline2/cursor:pos')
1189 -- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll up
1190 edit
.run_after_keychord(Editor_state
, 'down')
1191 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top')
1192 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1193 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1194 y
= Editor_state
.top
1195 App
.screen
.check(y
, 'ghij', 'screen:1')
1196 y
= y
+ Editor_state
.line_height
1197 App
.screen
.check(y
, 'kl', 'screen:2')
1198 y
= y
+ Editor_state
.line_height
1199 App
.screen
.check(y
, 'mno', 'screen:3')
1202 function test_up_arrow_moves_cursor()
1203 -- display the first 3 lines with the cursor on the bottom line
1204 App
.screen
.init
{width
=120, height
=60}
1205 Editor_state
= edit
.initialize_test_state()
1206 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1207 Text
.redraw_all(Editor_state
)
1208 Editor_state
.cursor1
= {line
=3, pos
=1}
1209 Editor_state
.screen_top1
= {line
=1, pos
=1}
1210 Editor_state
.screen_bottom1
= {}
1211 edit
.draw(Editor_state
)
1212 local y
= Editor_state
.top
1213 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1214 y
= y
+ Editor_state
.line_height
1215 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1216 y
= y
+ Editor_state
.line_height
1217 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1218 -- after hitting the up arrow the cursor moves up by 1 line
1219 edit
.run_after_keychord(Editor_state
, 'up')
1220 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1221 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor')
1222 -- the screen is unchanged
1223 y
= Editor_state
.top
1224 App
.screen
.check(y
, 'abc', 'screen:1')
1225 y
= y
+ Editor_state
.line_height
1226 App
.screen
.check(y
, 'def', 'screen:2')
1227 y
= y
+ Editor_state
.line_height
1228 App
.screen
.check(y
, 'ghi', 'screen:3')
1231 function test_up_arrow_skips_drawing()
1232 -- some lines of text with a drawing intermixed
1233 local drawing_width
= 50
1234 App
.screen
.init
{width
=Editor_state
.left
+drawing_width
, height
=100}
1235 Editor_state
= edit
.initialize_test_state()
1236 Editor_state
.lines
= load_array
{'abc', -- height 15
1237 '```lines', '```', -- height 25
1239 Text
.redraw_all(Editor_state
)
1240 Editor_state
.cursor1
= {line
=3, pos
=1}
1241 Editor_state
.screen_top1
= {line
=1, pos
=1}
1242 Editor_state
.screen_bottom1
= {}
1243 edit
.draw(Editor_state
)
1244 local y
= Editor_state
.top
1245 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1246 y
= y
+ Editor_state
.line_height
1247 local drawing_height
= Drawing_padding_height
+ drawing_width
/2 -- default
1248 y
= y
+ drawing_height
1249 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1250 check(Editor_state
.cursor_x
, 'baseline/cursor_x')
1251 -- after hitting the up arrow the cursor moves up by 2 lines, skipping the drawing
1252 edit
.run_after_keychord(Editor_state
, 'up')
1253 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1256 function test_up_arrow_scrolls_up_by_one_line()
1257 -- display the lines 2/3/4 with the cursor on line 2
1258 App
.screen
.init
{width
=120, height
=60}
1259 Editor_state
= edit
.initialize_test_state()
1260 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1261 Text
.redraw_all(Editor_state
)
1262 Editor_state
.cursor1
= {line
=2, pos
=1}
1263 Editor_state
.screen_top1
= {line
=2, pos
=1}
1264 Editor_state
.screen_bottom1
= {}
1265 edit
.draw(Editor_state
)
1266 local y
= Editor_state
.top
1267 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1268 y
= y
+ Editor_state
.line_height
1269 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1270 y
= y
+ Editor_state
.line_height
1271 App
.screen
.check(y
, 'jkl', 'baseline/screen:3')
1272 -- after hitting the up arrow the screen scrolls up by one line
1273 edit
.run_after_keychord(Editor_state
, 'up')
1274 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1275 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1276 y
= Editor_state
.top
1277 App
.screen
.check(y
, 'abc', 'screen:1')
1278 y
= y
+ Editor_state
.line_height
1279 App
.screen
.check(y
, 'def', 'screen:2')
1280 y
= y
+ Editor_state
.line_height
1281 App
.screen
.check(y
, 'ghi', 'screen:3')
1284 function test_up_arrow_scrolls_up_by_one_line_skipping_drawing()
1285 -- display lines 3/4/5 with a drawing just off screen at line 2
1286 App
.screen
.init
{width
=120, height
=60}
1287 Editor_state
= edit
.initialize_test_state()
1288 Editor_state
.lines
= load_array
{'abc', '```lines', '```', 'def', 'ghi', 'jkl'}
1289 Text
.redraw_all(Editor_state
)
1290 Editor_state
.cursor1
= {line
=3, pos
=1}
1291 Editor_state
.screen_top1
= {line
=3, pos
=1}
1292 Editor_state
.screen_bottom1
= {}
1293 edit
.draw(Editor_state
)
1294 local y
= Editor_state
.top
1295 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1296 y
= y
+ Editor_state
.line_height
1297 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1298 y
= y
+ Editor_state
.line_height
1299 App
.screen
.check(y
, 'jkl', 'baseline/screen:3')
1300 -- after hitting the up arrow the screen scrolls up to previous text line
1301 edit
.run_after_keychord(Editor_state
, 'up')
1302 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1303 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1306 function test_up_arrow_scrolls_up_by_one_screen_line()
1307 -- display lines starting from second screen line of a line
1308 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1309 Editor_state
= edit
.initialize_test_state()
1310 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1311 Text
.redraw_all(Editor_state
)
1312 Editor_state
.cursor1
= {line
=3, pos
=6}
1313 Editor_state
.screen_top1
= {line
=3, pos
=5}
1314 Editor_state
.screen_bottom1
= {}
1315 edit
.draw(Editor_state
)
1316 local y
= Editor_state
.top
1317 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1318 y
= y
+ Editor_state
.line_height
1319 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1320 -- after hitting the up arrow the screen scrolls up to first screen line
1321 edit
.run_after_keychord(Editor_state
, 'up')
1322 y
= Editor_state
.top
1323 App
.screen
.check(y
, 'ghi ', 'screen:1')
1324 y
= y
+ Editor_state
.line_height
1325 App
.screen
.check(y
, 'jkl', 'screen:2')
1326 y
= y
+ Editor_state
.line_height
1327 App
.screen
.check(y
, 'mno', 'screen:3')
1328 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1329 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1330 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1331 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1334 function test_up_arrow_scrolls_up_to_final_screen_line()
1335 -- display lines starting just after a long line
1336 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1337 Editor_state
= edit
.initialize_test_state()
1338 Editor_state
.lines
= load_array
{'abc def', 'ghi', 'jkl', 'mno'}
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
, 'ghi', 'baseline/screen:1')
1346 y
= y
+ Editor_state
.line_height
1347 App
.screen
.check(y
, 'jkl', 'baseline/screen:2')
1348 y
= y
+ Editor_state
.line_height
1349 App
.screen
.check(y
, 'mno', 'baseline/screen:3')
1350 -- after hitting the up arrow the screen scrolls up to final screen line of previous line
1351 edit
.run_after_keychord(Editor_state
, 'up')
1352 y
= Editor_state
.top
1353 App
.screen
.check(y
, 'def', 'screen:1')
1354 y
= y
+ Editor_state
.line_height
1355 App
.screen
.check(y
, 'ghi', 'screen:2')
1356 y
= y
+ Editor_state
.line_height
1357 App
.screen
.check(y
, 'jkl', 'screen:3')
1358 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
1359 check_eq(Editor_state
.screen_top1
.pos
, 5, 'screen_top:pos')
1360 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1361 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1364 function test_up_arrow_scrolls_up_to_empty_line()
1365 -- display a screenful of text with an empty line just above it outside the screen
1366 App
.screen
.init
{width
=120, height
=60}
1367 Editor_state
= edit
.initialize_test_state()
1368 Editor_state
.lines
= load_array
{'', 'abc', 'def', 'ghi', 'jkl'}
1369 Text
.redraw_all(Editor_state
)
1370 Editor_state
.cursor1
= {line
=2, pos
=1}
1371 Editor_state
.screen_top1
= {line
=2, pos
=1}
1372 Editor_state
.screen_bottom1
= {}
1373 edit
.draw(Editor_state
)
1374 local y
= Editor_state
.top
1375 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1376 y
= y
+ Editor_state
.line_height
1377 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1378 y
= y
+ Editor_state
.line_height
1379 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1380 -- after hitting the up arrow the screen scrolls up by one line
1381 edit
.run_after_keychord(Editor_state
, 'up')
1382 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1383 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1384 y
= Editor_state
.top
1386 y
= y
+ Editor_state
.line_height
1387 App
.screen
.check(y
, 'abc', 'screen:2')
1388 y
= y
+ Editor_state
.line_height
1389 App
.screen
.check(y
, 'def', 'screen:3')
1392 function test_pageup()
1393 App
.screen
.init
{width
=120, height
=45}
1394 Editor_state
= edit
.initialize_test_state()
1395 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi'}
1396 Text
.redraw_all(Editor_state
)
1397 Editor_state
.cursor1
= {line
=2, pos
=1}
1398 Editor_state
.screen_top1
= {line
=2, pos
=1}
1399 Editor_state
.screen_bottom1
= {}
1400 -- initially the last two lines are displayed
1401 edit
.draw(Editor_state
)
1402 local y
= Editor_state
.top
1403 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1404 y
= y
+ Editor_state
.line_height
1405 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1406 -- after pageup the cursor goes to first line
1407 edit
.run_after_keychord(Editor_state
, 'pageup')
1408 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1409 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1410 y
= Editor_state
.top
1411 App
.screen
.check(y
, 'abc', 'screen:1')
1412 y
= y
+ Editor_state
.line_height
1413 App
.screen
.check(y
, 'def', 'screen:2')
1416 function test_pageup_scrolls_up_by_screen_line()
1417 -- display the first three lines with the cursor on the bottom line
1418 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1419 Editor_state
= edit
.initialize_test_state()
1420 Editor_state
.lines
= load_array
{'abc def', 'ghi', 'jkl', 'mno'}
1421 Text
.redraw_all(Editor_state
)
1422 Editor_state
.cursor1
= {line
=2, pos
=1}
1423 Editor_state
.screen_top1
= {line
=2, pos
=1}
1424 Editor_state
.screen_bottom1
= {}
1425 edit
.draw(Editor_state
)
1426 local y
= Editor_state
.top
1427 App
.screen
.check(y
, 'ghi', 'baseline/screen:1')
1428 y
= y
+ Editor_state
.line_height
1429 App
.screen
.check(y
, 'jkl', 'baseline/screen:2')
1430 y
= y
+ Editor_state
.line_height
1431 App
.screen
.check(y
, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1432 -- after hitting the page-up key the screen scrolls up to top
1433 edit
.run_after_keychord(Editor_state
, 'pageup')
1434 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1435 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1436 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1437 y
= Editor_state
.top
1438 App
.screen
.check(y
, 'abc ', 'screen:1')
1439 y
= y
+ Editor_state
.line_height
1440 App
.screen
.check(y
, 'def', 'screen:2')
1441 y
= y
+ Editor_state
.line_height
1442 App
.screen
.check(y
, 'ghi', 'screen:3')
1445 function test_pageup_scrolls_up_from_middle_screen_line()
1446 -- display a few lines starting from the middle of a line (Editor_state.cursor1.pos > 1)
1447 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1448 Editor_state
= edit
.initialize_test_state()
1449 Editor_state
.lines
= load_array
{'abc def', 'ghi jkl', 'mno'}
1450 Text
.redraw_all(Editor_state
)
1451 Editor_state
.cursor1
= {line
=2, pos
=5}
1452 Editor_state
.screen_top1
= {line
=2, pos
=5}
1453 Editor_state
.screen_bottom1
= {}
1454 edit
.draw(Editor_state
)
1455 local y
= Editor_state
.top
1456 App
.screen
.check(y
, 'jkl', 'baseline/screen:2')
1457 y
= y
+ Editor_state
.line_height
1458 App
.screen
.check(y
, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1459 -- after hitting the page-up key the screen scrolls up to top
1460 edit
.run_after_keychord(Editor_state
, 'pageup')
1461 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1462 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1463 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1464 y
= Editor_state
.top
1465 App
.screen
.check(y
, 'abc ', 'screen:1')
1466 y
= y
+ Editor_state
.line_height
1467 App
.screen
.check(y
, 'def', 'screen:2')
1468 y
= y
+ Editor_state
.line_height
1469 App
.screen
.check(y
, 'ghi ', 'screen:3')
1472 function test_enter_on_bottom_line_scrolls_down()
1473 -- display a few lines with cursor on bottom line
1474 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1475 Editor_state
= edit
.initialize_test_state()
1476 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1477 Text
.redraw_all(Editor_state
)
1478 Editor_state
.cursor1
= {line
=3, pos
=2}
1479 Editor_state
.screen_top1
= {line
=1, pos
=1}
1480 Editor_state
.screen_bottom1
= {}
1481 edit
.draw(Editor_state
)
1482 local y
= Editor_state
.top
1483 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1484 y
= y
+ Editor_state
.line_height
1485 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1486 y
= y
+ Editor_state
.line_height
1487 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1488 -- after hitting the enter key the screen scrolls down
1489 edit
.run_after_keychord(Editor_state
, 'return')
1490 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1491 check_eq(Editor_state
.cursor1
.line
, 4, 'cursor:line')
1492 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1493 y
= Editor_state
.top
1494 App
.screen
.check(y
, 'def', 'screen:1')
1495 y
= y
+ Editor_state
.line_height
1496 App
.screen
.check(y
, 'g', 'screen:2')
1497 y
= y
+ Editor_state
.line_height
1498 App
.screen
.check(y
, 'hi', 'screen:3')
1501 function test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1502 -- display just the bottom line on screen
1503 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1504 Editor_state
= edit
.initialize_test_state()
1505 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1506 Text
.redraw_all(Editor_state
)
1507 Editor_state
.cursor1
= {line
=4, pos
=2}
1508 Editor_state
.screen_top1
= {line
=4, pos
=1}
1509 Editor_state
.screen_bottom1
= {}
1510 edit
.draw(Editor_state
)
1511 local y
= Editor_state
.top
1512 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1513 -- after hitting the enter key the screen does not scroll down
1514 edit
.run_after_keychord(Editor_state
, 'return')
1515 check_eq(Editor_state
.screen_top1
.line
, 4, 'screen_top')
1516 check_eq(Editor_state
.cursor1
.line
, 5, 'cursor:line')
1517 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1518 y
= Editor_state
.top
1519 App
.screen
.check(y
, 'j', 'screen:1')
1520 y
= y
+ Editor_state
.line_height
1521 App
.screen
.check(y
, 'kl', 'screen:2')
1524 function test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1525 -- display just an empty bottom line on screen
1526 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1527 Editor_state
= edit
.initialize_test_state()
1528 Editor_state
.lines
= load_array
{'abc', ''}
1529 Text
.redraw_all(Editor_state
)
1530 Editor_state
.cursor1
= {line
=2, pos
=1}
1531 Editor_state
.screen_top1
= {line
=2, pos
=1}
1532 Editor_state
.screen_bottom1
= {}
1533 edit
.draw(Editor_state
)
1534 -- after hitting the inserting_text key the screen does not scroll down
1535 edit
.run_after_text_input(Editor_state
, 'a')
1536 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1537 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1538 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
1539 local y
= Editor_state
.top
1540 App
.screen
.check(y
, 'a', 'screen:1')
1543 function test_typing_on_bottom_line_scrolls_down()
1544 -- display a few lines with cursor on bottom line
1545 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1546 Editor_state
= edit
.initialize_test_state()
1547 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1548 Text
.redraw_all(Editor_state
)
1549 Editor_state
.cursor1
= {line
=3, pos
=4}
1550 Editor_state
.screen_top1
= {line
=1, pos
=1}
1551 Editor_state
.screen_bottom1
= {}
1552 edit
.draw(Editor_state
)
1553 local y
= Editor_state
.top
1554 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1555 y
= y
+ Editor_state
.line_height
1556 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1557 y
= y
+ Editor_state
.line_height
1558 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1559 -- after typing something the line wraps and the screen scrolls down
1560 edit
.run_after_text_input(Editor_state
, 'j')
1561 edit
.run_after_text_input(Editor_state
, 'k')
1562 edit
.run_after_text_input(Editor_state
, 'l')
1563 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1564 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1565 check_eq(Editor_state
.cursor1
.pos
, 7, 'cursor:pos')
1566 y
= Editor_state
.top
1567 App
.screen
.check(y
, 'def', 'screen:1')
1568 y
= y
+ Editor_state
.line_height
1569 App
.screen
.check(y
, 'ghij', 'screen:2')
1570 y
= y
+ Editor_state
.line_height
1571 App
.screen
.check(y
, 'kl', 'screen:3')
1574 function test_left_arrow_scrolls_up_in_wrapped_line()
1575 -- display lines starting from second screen line of a line
1576 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1577 Editor_state
= edit
.initialize_test_state()
1578 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1579 Text
.redraw_all(Editor_state
)
1580 Editor_state
.screen_top1
= {line
=3, pos
=5}
1581 Editor_state
.screen_bottom1
= {}
1582 -- cursor is at top of screen
1583 Editor_state
.cursor1
= {line
=3, pos
=5}
1584 edit
.draw(Editor_state
)
1585 local y
= Editor_state
.top
1586 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1587 y
= y
+ Editor_state
.line_height
1588 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1589 -- after hitting the left arrow the screen scrolls up to first screen line
1590 edit
.run_after_keychord(Editor_state
, 'left')
1591 y
= Editor_state
.top
1592 App
.screen
.check(y
, 'ghi ', 'screen:1')
1593 y
= y
+ Editor_state
.line_height
1594 App
.screen
.check(y
, 'jkl', 'screen:2')
1595 y
= y
+ Editor_state
.line_height
1596 App
.screen
.check(y
, 'mno', 'screen:3')
1597 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1598 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1599 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1600 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
1603 function test_right_arrow_scrolls_down_in_wrapped_line()
1604 -- display the first three lines with the cursor on the bottom 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
=1, pos
=1}
1610 Editor_state
.screen_bottom1
= {}
1611 -- cursor is at bottom right 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
, 'abc', 'baseline/screen:1')
1616 y
= y
+ Editor_state
.line_height
1617 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1618 y
= y
+ Editor_state
.line_height
1619 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1620 -- after hitting the right arrow the screen scrolls down by one line
1621 edit
.run_after_keychord(Editor_state
, 'right')
1622 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1623 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1624 check_eq(Editor_state
.cursor1
.pos
, 6, 'cursor:pos')
1625 y
= Editor_state
.top
1626 App
.screen
.check(y
, 'def', 'screen:1')
1627 y
= y
+ Editor_state
.line_height
1628 App
.screen
.check(y
, 'ghi ', 'screen:2')
1629 y
= y
+ Editor_state
.line_height
1630 App
.screen
.check(y
, 'jkl', 'screen:3')
1633 function test_home_scrolls_up_in_wrapped_line()
1634 -- display lines starting from second screen line of a line
1635 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1636 Editor_state
= edit
.initialize_test_state()
1637 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1638 Text
.redraw_all(Editor_state
)
1639 Editor_state
.screen_top1
= {line
=3, pos
=5}
1640 Editor_state
.screen_bottom1
= {}
1641 -- cursor is at top of screen
1642 Editor_state
.cursor1
= {line
=3, pos
=5}
1643 edit
.draw(Editor_state
)
1644 local y
= Editor_state
.top
1645 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1646 y
= y
+ Editor_state
.line_height
1647 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1648 -- after hitting home the screen scrolls up to first screen line
1649 edit
.run_after_keychord(Editor_state
, 'home')
1650 y
= Editor_state
.top
1651 App
.screen
.check(y
, 'ghi ', 'screen:1')
1652 y
= y
+ Editor_state
.line_height
1653 App
.screen
.check(y
, 'jkl', 'screen:2')
1654 y
= y
+ Editor_state
.line_height
1655 App
.screen
.check(y
, 'mno', 'screen:3')
1656 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1657 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1658 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1659 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1662 function test_end_scrolls_down_in_wrapped_line()
1663 -- display the first three lines with the cursor on the bottom line
1664 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1665 Editor_state
= edit
.initialize_test_state()
1666 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1667 Text
.redraw_all(Editor_state
)
1668 Editor_state
.screen_top1
= {line
=1, pos
=1}
1669 Editor_state
.screen_bottom1
= {}
1670 -- cursor is at bottom right of screen
1671 Editor_state
.cursor1
= {line
=3, pos
=5}
1672 edit
.draw(Editor_state
)
1673 local y
= Editor_state
.top
1674 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1675 y
= y
+ Editor_state
.line_height
1676 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1677 y
= y
+ Editor_state
.line_height
1678 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1679 -- after hitting end the screen scrolls down by one line
1680 edit
.run_after_keychord(Editor_state
, 'end')
1681 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1682 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1683 check_eq(Editor_state
.cursor1
.pos
, 8, 'cursor:pos')
1684 y
= Editor_state
.top
1685 App
.screen
.check(y
, 'def', 'screen:1')
1686 y
= y
+ Editor_state
.line_height
1687 App
.screen
.check(y
, 'ghi ', 'screen:2')
1688 y
= y
+ Editor_state
.line_height
1689 App
.screen
.check(y
, 'jkl', 'screen:3')
1692 function test_position_cursor_on_recently_edited_wrapping_line()
1693 -- draw a line wrapping over 2 screen lines
1694 App
.screen
.init
{width
=100, height
=200}
1695 Editor_state
= edit
.initialize_test_state()
1696 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr ', 'xyz'}
1697 Text
.redraw_all(Editor_state
)
1698 Editor_state
.cursor1
= {line
=1, pos
=25}
1699 Editor_state
.screen_top1
= {line
=1, pos
=1}
1700 Editor_state
.screen_bottom1
= {}
1701 edit
.draw(Editor_state
)
1702 local y
= Editor_state
.top
1703 App
.screen
.check(y
, 'abc def ghi ', 'baseline1/screen:1')
1704 y
= y
+ Editor_state
.line_height
1705 App
.screen
.check(y
, 'jkl mno pqr ', 'baseline1/screen:2')
1706 y
= y
+ Editor_state
.line_height
1707 App
.screen
.check(y
, 'xyz', 'baseline1/screen:3')
1708 -- add to the line until it's wrapping over 3 screen lines
1709 edit
.run_after_text_input(Editor_state
, 's')
1710 edit
.run_after_text_input(Editor_state
, 't')
1711 edit
.run_after_text_input(Editor_state
, 'u')
1712 check_eq(Editor_state
.cursor1
.pos
, 28, 'cursor:pos')
1713 y
= Editor_state
.top
1714 App
.screen
.check(y
, 'abc def ghi ', 'baseline2/screen:1')
1715 y
= y
+ Editor_state
.line_height
1716 App
.screen
.check(y
, 'jkl mno pqr ', 'baseline2/screen:2')
1717 y
= y
+ Editor_state
.line_height
1718 App
.screen
.check(y
, 'stu', 'baseline2/screen:3')
1719 -- try to move the cursor earlier in the third screen line by clicking the mouse
1720 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+Editor_state
.line_height
*2+5, 1)
1721 -- cursor should move
1722 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1723 check_eq(Editor_state
.cursor1
.pos
, 26, 'cursor:pos')
1726 function test_backspace_can_scroll_up()
1727 -- display the lines 2/3/4 with the cursor on line 2
1728 App
.screen
.init
{width
=120, height
=60}
1729 Editor_state
= edit
.initialize_test_state()
1730 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1731 Text
.redraw_all(Editor_state
)
1732 Editor_state
.cursor1
= {line
=2, pos
=1}
1733 Editor_state
.screen_top1
= {line
=2, pos
=1}
1734 Editor_state
.screen_bottom1
= {}
1735 edit
.draw(Editor_state
)
1736 local y
= Editor_state
.top
1737 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1738 y
= y
+ Editor_state
.line_height
1739 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1740 y
= y
+ Editor_state
.line_height
1741 App
.screen
.check(y
, 'jkl', 'baseline/screen:3')
1742 -- after hitting backspace the screen scrolls up by one line
1743 edit
.run_after_keychord(Editor_state
, 'backspace')
1744 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1745 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1746 y
= Editor_state
.top
1747 App
.screen
.check(y
, 'abcdef', 'screen:1')
1748 y
= y
+ Editor_state
.line_height
1749 App
.screen
.check(y
, 'ghi', 'screen:2')
1750 y
= y
+ Editor_state
.line_height
1751 App
.screen
.check(y
, 'jkl', 'screen:3')
1754 function test_backspace_can_scroll_up_screen_line()
1755 -- display lines starting from second screen line of a line
1756 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1757 Editor_state
= edit
.initialize_test_state()
1758 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1759 Text
.redraw_all(Editor_state
)
1760 Editor_state
.cursor1
= {line
=3, pos
=5}
1761 Editor_state
.screen_top1
= {line
=3, pos
=5}
1762 Editor_state
.screen_bottom1
= {}
1763 edit
.draw(Editor_state
)
1764 local y
= Editor_state
.top
1765 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1766 y
= y
+ Editor_state
.line_height
1767 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1768 -- after hitting backspace the screen scrolls up by one screen line
1769 edit
.run_after_keychord(Editor_state
, 'backspace')
1770 y
= Editor_state
.top
1771 App
.screen
.check(y
, 'ghij', 'screen:1')
1772 y
= y
+ Editor_state
.line_height
1773 App
.screen
.check(y
, 'kl', 'screen:2')
1774 y
= y
+ Editor_state
.line_height
1775 App
.screen
.check(y
, 'mno', 'screen:3')
1776 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1777 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1778 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1779 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
1782 function test_backspace_past_line_boundary()
1783 -- position cursor at start of a (non-first) line
1784 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1785 Editor_state
= edit
.initialize_test_state()
1786 Editor_state
.lines
= load_array
{'abc', 'def'}
1787 Text
.redraw_all(Editor_state
)
1788 Editor_state
.cursor1
= {line
=2, pos
=1}
1789 -- backspace joins with previous line
1790 edit
.run_after_keychord(Editor_state
, 'backspace')
1791 check_eq(Editor_state
.lines
[1].data
, 'abcdef', 'check')
1794 -- some tests for operating over selections created using Shift- chords
1795 -- we're just testing delete_selection, and it works the same for all keys
1797 function test_backspace_over_selection()
1798 -- select just one character within a line with cursor before selection
1799 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1800 Editor_state
= edit
.initialize_test_state()
1801 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1802 Text
.redraw_all(Editor_state
)
1803 Editor_state
.cursor1
= {line
=1, pos
=1}
1804 Editor_state
.selection1
= {line
=1, pos
=2}
1805 -- backspace deletes the selected character, even though it's after the cursor
1806 edit
.run_after_keychord(Editor_state
, 'backspace')
1807 check_eq(Editor_state
.lines
[1].data
, 'bc', 'data')
1808 -- cursor (remains) at start of selection
1809 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1810 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1811 -- selection is cleared
1812 check_nil(Editor_state
.selection1
.line
, 'selection')
1815 function test_backspace_over_selection_reverse()
1816 -- select just one character within a line with cursor after selection
1817 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1818 Editor_state
= edit
.initialize_test_state()
1819 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1820 Text
.redraw_all(Editor_state
)
1821 Editor_state
.cursor1
= {line
=1, pos
=2}
1822 Editor_state
.selection1
= {line
=1, pos
=1}
1823 -- backspace deletes the selected character
1824 edit
.run_after_keychord(Editor_state
, 'backspace')
1825 check_eq(Editor_state
.lines
[1].data
, 'bc', 'data')
1826 -- cursor moves to start of selection
1827 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1828 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1829 -- selection is cleared
1830 check_nil(Editor_state
.selection1
.line
, 'selection')
1833 function test_backspace_over_multiple_lines()
1834 -- select just one character within a line with cursor after selection
1835 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1836 Editor_state
= edit
.initialize_test_state()
1837 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1838 Text
.redraw_all(Editor_state
)
1839 Editor_state
.cursor1
= {line
=1, pos
=2}
1840 Editor_state
.selection1
= {line
=4, pos
=2}
1841 -- backspace deletes the region and joins the remaining portions of lines on either side
1842 edit
.run_after_keychord(Editor_state
, 'backspace')
1843 check_eq(Editor_state
.lines
[1].data
, 'akl', 'data:1')
1844 check_eq(Editor_state
.lines
[2].data
, 'mno', 'data:2')
1845 -- cursor remains at start of selection
1846 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1847 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
1848 -- selection is cleared
1849 check_nil(Editor_state
.selection1
.line
, 'selection')
1852 function test_backspace_to_end_of_line()
1853 -- select region from cursor to end of line
1854 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1855 Editor_state
= edit
.initialize_test_state()
1856 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1857 Text
.redraw_all(Editor_state
)
1858 Editor_state
.cursor1
= {line
=1, pos
=2}
1859 Editor_state
.selection1
= {line
=1, pos
=4}
1860 -- backspace deletes rest of line without joining to any other line
1861 edit
.run_after_keychord(Editor_state
, 'backspace')
1862 check_eq(Editor_state
.lines
[1].data
, 'a', 'data:1')
1863 check_eq(Editor_state
.lines
[2].data
, 'def', 'data:2')
1864 -- cursor remains at start of selection
1865 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1866 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
1867 -- selection is cleared
1868 check_nil(Editor_state
.selection1
.line
, 'selection')
1871 function test_backspace_to_start_of_line()
1872 -- select region from cursor to start of line
1873 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1874 Editor_state
= edit
.initialize_test_state()
1875 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1876 Text
.redraw_all(Editor_state
)
1877 Editor_state
.cursor1
= {line
=2, pos
=1}
1878 Editor_state
.selection1
= {line
=2, pos
=3}
1879 -- backspace deletes beginning of line without joining to any other line
1880 edit
.run_after_keychord(Editor_state
, 'backspace')
1881 check_eq(Editor_state
.lines
[1].data
, 'abc', 'data:1')
1882 check_eq(Editor_state
.lines
[2].data
, 'f', 'data:2')
1883 -- cursor remains at start of selection
1884 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1885 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1886 -- selection is cleared
1887 check_nil(Editor_state
.selection1
.line
, 'selection')
1890 function test_undo_insert_text()
1891 App
.screen
.init
{width
=120, height
=60}
1892 Editor_state
= edit
.initialize_test_state()
1893 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
1894 Text
.redraw_all(Editor_state
)
1895 Editor_state
.cursor1
= {line
=2, pos
=4}
1896 Editor_state
.screen_top1
= {line
=1, pos
=1}
1897 Editor_state
.screen_bottom1
= {}
1898 -- insert a character
1899 edit
.draw(Editor_state
)
1900 edit
.run_after_text_input(Editor_state
, 'g')
1901 check_eq(Editor_state
.cursor1
.line
, 2, 'baseline/cursor:line')
1902 check_eq(Editor_state
.cursor1
.pos
, 5, 'baseline/cursor:pos')
1903 check_nil(Editor_state
.selection1
.line
, 'baseline/selection:line')
1904 check_nil(Editor_state
.selection1
.pos
, 'baseline/selection:pos')
1905 local y
= Editor_state
.top
1906 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1907 y
= y
+ Editor_state
.line_height
1908 App
.screen
.check(y
, 'defg', 'baseline/screen:2')
1909 y
= y
+ Editor_state
.line_height
1910 App
.screen
.check(y
, 'xyz', 'baseline/screen:3')
1912 edit
.run_after_keychord(Editor_state
, 'C-z')
1913 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1914 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
1915 check_nil(Editor_state
.selection1
.line
, 'selection:line')
1916 check_nil(Editor_state
.selection1
.pos
, 'selection:pos')
1917 y
= Editor_state
.top
1918 App
.screen
.check(y
, 'abc', 'screen:1')
1919 y
= y
+ Editor_state
.line_height
1920 App
.screen
.check(y
, 'def', 'screen:2')
1921 y
= y
+ Editor_state
.line_height
1922 App
.screen
.check(y
, 'xyz', 'screen:3')
1925 function test_undo_delete_text()
1926 App
.screen
.init
{width
=120, height
=60}
1927 Editor_state
= edit
.initialize_test_state()
1928 Editor_state
.lines
= load_array
{'abc', 'defg', 'xyz'}
1929 Text
.redraw_all(Editor_state
)
1930 Editor_state
.cursor1
= {line
=2, pos
=5}
1931 Editor_state
.screen_top1
= {line
=1, pos
=1}
1932 Editor_state
.screen_bottom1
= {}
1933 -- delete a character
1934 edit
.run_after_keychord(Editor_state
, 'backspace')
1935 check_eq(Editor_state
.cursor1
.line
, 2, 'baseline/cursor:line')
1936 check_eq(Editor_state
.cursor1
.pos
, 4, 'baseline/cursor:pos')
1937 check_nil(Editor_state
.selection1
.line
, 'baseline/selection:line')
1938 check_nil(Editor_state
.selection1
.pos
, 'baseline/selection:pos')
1939 local y
= Editor_state
.top
1940 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1941 y
= y
+ Editor_state
.line_height
1942 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1943 y
= y
+ Editor_state
.line_height
1944 App
.screen
.check(y
, 'xyz', 'baseline/screen:3')
1946 --? -- after undo, the backspaced key is selected
1947 edit
.run_after_keychord(Editor_state
, 'C-z')
1948 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1949 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1950 check_nil(Editor_state
.selection1
.line
, 'selection:line')
1951 check_nil(Editor_state
.selection1
.pos
, 'selection:pos')
1952 --? check_eq(Editor_state.selection1.line, 2, 'selection:line')
1953 --? check_eq(Editor_state.selection1.pos, 4, 'selection:pos')
1954 y
= Editor_state
.top
1955 App
.screen
.check(y
, 'abc', 'screen:1')
1956 y
= y
+ Editor_state
.line_height
1957 App
.screen
.check(y
, 'defg', 'screen:2')
1958 y
= y
+ Editor_state
.line_height
1959 App
.screen
.check(y
, 'xyz', 'screen:3')
1962 function test_undo_restores_selection()
1963 -- display a line of text with some part selected
1964 App
.screen
.init
{width
=75, height
=80}
1965 Editor_state
= edit
.initialize_test_state()
1966 Editor_state
.lines
= load_array
{'abc'}
1967 Text
.redraw_all(Editor_state
)
1968 Editor_state
.cursor1
= {line
=1, pos
=1}
1969 Editor_state
.selection1
= {line
=1, pos
=2}
1970 Editor_state
.screen_top1
= {line
=1, pos
=1}
1971 Editor_state
.screen_bottom1
= {}
1972 edit
.draw(Editor_state
)
1973 -- delete selected text
1974 edit
.run_after_text_input(Editor_state
, 'x')
1975 check_eq(Editor_state
.lines
[1].data
, 'xbc', 'baseline')
1976 check_nil(Editor_state
.selection1
.line
, 'baseline:selection')
1978 edit
.run_after_keychord(Editor_state
, 'C-z')
1979 edit
.run_after_keychord(Editor_state
, 'C-z')
1980 -- selection is restored
1981 check_eq(Editor_state
.selection1
.line
, 1, 'line')
1982 check_eq(Editor_state
.selection1
.pos
, 2, 'pos')
1985 function test_search()
1986 App
.screen
.init
{width
=120, height
=60}
1987 Editor_state
= edit
.initialize_test_state()
1988 Editor_state
.lines
= load_array
{'```lines', '```', 'def', 'ghi', 'deg'}
1989 Text
.redraw_all(Editor_state
)
1990 Editor_state
.cursor1
= {line
=1, pos
=1}
1991 Editor_state
.screen_top1
= {line
=1, pos
=1}
1992 Editor_state
.screen_bottom1
= {}
1993 edit
.draw(Editor_state
)
1994 -- search for a string
1995 edit
.run_after_keychord(Editor_state
, 'C-f')
1996 edit
.run_after_text_input(Editor_state
, 'd')
1997 edit
.run_after_keychord(Editor_state
, 'return')
1998 check_eq(Editor_state
.cursor1
.line
, 2, '1/cursor:line')
1999 check_eq(Editor_state
.cursor1
.pos
, 1, '1/cursor:pos')
2001 Editor_state
.cursor1
= {line
=1, pos
=1}
2002 Editor_state
.screen_top1
= {line
=1, pos
=1}
2003 -- search for second occurrence
2004 edit
.run_after_keychord(Editor_state
, 'C-f')
2005 edit
.run_after_text_input(Editor_state
, 'de')
2006 edit
.run_after_keychord(Editor_state
, 'down')
2007 edit
.run_after_keychord(Editor_state
, 'return')
2008 check_eq(Editor_state
.cursor1
.line
, 4, '2/cursor:line')
2009 check_eq(Editor_state
.cursor1
.pos
, 1, '2/cursor:pos')
2012 function test_search_upwards()
2013 App
.screen
.init
{width
=120, height
=60}
2014 Editor_state
= edit
.initialize_test_state()
2015 Editor_state
.lines
= load_array
{'abc abd'}
2016 Text
.redraw_all(Editor_state
)
2017 Editor_state
.cursor1
= {line
=1, pos
=2}
2018 Editor_state
.screen_top1
= {line
=1, pos
=1}
2019 Editor_state
.screen_bottom1
= {}
2020 edit
.draw(Editor_state
)
2021 -- search for a string
2022 edit
.run_after_keychord(Editor_state
, 'C-f')
2023 edit
.run_after_text_input(Editor_state
, 'a')
2024 -- search for previous occurrence
2025 edit
.run_after_keychord(Editor_state
, 'up')
2026 check_eq(Editor_state
.cursor1
.line
, 1, '2/cursor:line')
2027 check_eq(Editor_state
.cursor1
.pos
, 1, '2/cursor:pos')
2030 function test_search_wrap()
2031 App
.screen
.init
{width
=120, height
=60}
2032 Editor_state
= edit
.initialize_test_state()
2033 Editor_state
.lines
= load_array
{'abc'}
2034 Text
.redraw_all(Editor_state
)
2035 Editor_state
.cursor1
= {line
=1, pos
=3}
2036 Editor_state
.screen_top1
= {line
=1, pos
=1}
2037 Editor_state
.screen_bottom1
= {}
2038 edit
.draw(Editor_state
)
2039 -- search for a string
2040 edit
.run_after_keychord(Editor_state
, 'C-f')
2041 edit
.run_after_text_input(Editor_state
, 'a')
2042 edit
.run_after_keychord(Editor_state
, 'return')
2044 check_eq(Editor_state
.cursor1
.line
, 1, '1/cursor:line')
2045 check_eq(Editor_state
.cursor1
.pos
, 1, '1/cursor:pos')
2048 function test_search_wrap_upwards()
2049 App
.screen
.init
{width
=120, height
=60}
2050 Editor_state
= edit
.initialize_test_state()
2051 Editor_state
.lines
= load_array
{'abc abd'}
2052 Text
.redraw_all(Editor_state
)
2053 Editor_state
.cursor1
= {line
=1, pos
=1}
2054 Editor_state
.screen_top1
= {line
=1, pos
=1}
2055 Editor_state
.screen_bottom1
= {}
2056 edit
.draw(Editor_state
)
2057 -- search upwards for a string
2058 edit
.run_after_keychord(Editor_state
, 'C-f')
2059 edit
.run_after_text_input(Editor_state
, 'a')
2060 edit
.run_after_keychord(Editor_state
, 'up')
2062 check_eq(Editor_state
.cursor1
.line
, 1, '1/cursor:line')
2063 check_eq(Editor_state
.cursor1
.pos
, 5, '1/cursor:pos')