get rid of to_text
[lines.love.git] / text_tests.lua
blob4fe0ca82e1bfe2fce1ec4e0d00d1f5905f3f5229
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')
14 end
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')
26 end
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')
40 end
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')
55 end
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')
66 end
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')
78 end
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')
89 end
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)
298 -- cursor moves
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)
316 -- cursor moves
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)
383 -- cursor moves
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)
402 -- cursor moves
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()
448 -- 12345678901234
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()
471 -- 12345678901234
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()
492 -- 12345678901234
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()
516 -- 12345678901234
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()
540 -- 0 1 2
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)
567 -- select a letter
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)
608 -- press a key
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)
647 -- copy selection
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')
654 function test_cut()
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)
665 -- press a key
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)
683 -- set clipboard
684 App.clipboard = 'xyz'
685 -- paste selection
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}
710 -- delete selection
711 edit.run_after_keychord(Editor_state, 'backspace')
712 -- page scrolls up
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')
756 y = Editor_state.top
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()
765 -- display a 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')
803 y = Editor_state.top
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')
898 -- no crash
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')
920 y = Editor_state.top
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
933 'def', -- height 15
934 'ghi'} -- height 15
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')
977 y = Editor_state.top
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
1065 'ghi'}
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
1238 'ghi'}
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
1385 -- empty first line
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')
1911 -- undo
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')
1945 -- undo
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')
1977 -- undo
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')
2000 -- reset cursor
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')
2043 -- cursor wraps
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')
2061 -- cursor wraps
2062 check_eq(Editor_state.cursor1.line, 1, '1/cursor:line')
2063 check_eq(Editor_state.cursor1.pos, 5, '1/cursor:pos')