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