Merge lines.love
[view.love.git] / text_tests.lua
blob8c2dc3ae25724f577bbf0e7b6f5f72b93bf93daf
1 -- major tests for text editing flows
2 -- Arguably this should be called edit_tests.lua,
3 -- but that would mess up the git blame at this point.
5 function test_initial_state()
6 App.screen.init{width=120, height=60}
7 Editor_state = edit.initialize_test_state()
8 Editor_state.lines = load_array{}
9 Text.redraw_all(Editor_state)
10 edit.draw(Editor_state)
11 check_eq(#Editor_state.lines, 1, '#lines')
12 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
13 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
14 check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')
15 check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
16 end
18 function test_backspace_from_start_of_final_line()
19 -- display final line of text with cursor at start of it
20 App.screen.init{width=120, height=60}
21 Editor_state = edit.initialize_test_state()
22 Editor_state.lines = load_array{'abc', 'def'}
23 Editor_state.screen_top1 = {line=2, pos=1}
24 Editor_state.cursor1 = {line=2, pos=1}
25 Text.redraw_all(Editor_state)
26 -- backspace scrolls up
27 edit.run_after_keychord(Editor_state, 'backspace')
28 check_eq(#Editor_state.lines, 1, '#lines')
29 check_eq(Editor_state.cursor1.line, 1, 'cursor')
30 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
31 end
33 function test_insert_first_character()
34 App.screen.init{width=120, height=60}
35 Editor_state = edit.initialize_test_state()
36 Editor_state.lines = load_array{}
37 Text.redraw_all(Editor_state)
38 edit.draw(Editor_state)
39 edit.run_after_text_input(Editor_state, 'a')
40 local y = Editor_state.top
41 App.screen.check(y, 'a', 'screen:1')
42 end
44 function test_press_ctrl()
45 -- press ctrl while the cursor is on text
46 App.screen.init{width=50, height=80}
47 Editor_state = edit.initialize_test_state()
48 Editor_state.lines = load_array{''}
49 Text.redraw_all(Editor_state)
50 Editor_state.cursor1 = {line=1, pos=1}
51 Editor_state.screen_top1 = {line=1, pos=1}
52 Editor_state.screen_bottom1 = {}
53 edit.run_after_keychord(Editor_state, 'C-m')
54 end
56 function test_move_left()
57 App.screen.init{width=120, height=60}
58 Editor_state = edit.initialize_test_state()
59 Editor_state.lines = load_array{'a'}
60 Text.redraw_all(Editor_state)
61 Editor_state.cursor1 = {line=1, pos=2}
62 edit.draw(Editor_state)
63 edit.run_after_keychord(Editor_state, 'left')
64 check_eq(Editor_state.cursor1.pos, 1, 'check')
65 end
67 function test_move_right()
68 App.screen.init{width=120, height=60}
69 Editor_state = edit.initialize_test_state()
70 Editor_state.lines = load_array{'a'}
71 Text.redraw_all(Editor_state)
72 Editor_state.cursor1 = {line=1, pos=1}
73 edit.draw(Editor_state)
74 edit.run_after_keychord(Editor_state, 'right')
75 check_eq(Editor_state.cursor1.pos, 2, 'check')
76 end
78 function test_move_left_to_previous_line()
79 App.screen.init{width=120, height=60}
80 Editor_state = edit.initialize_test_state()
81 Editor_state.lines = load_array{'abc', 'def'}
82 Text.redraw_all(Editor_state)
83 Editor_state.cursor1 = {line=2, pos=1}
84 edit.draw(Editor_state)
85 edit.run_after_keychord(Editor_state, 'left')
86 check_eq(Editor_state.cursor1.line, 1, 'line')
87 check_eq(Editor_state.cursor1.pos, 4, 'pos') -- past end of line
88 end
90 function test_move_right_to_next_line()
91 App.screen.init{width=120, height=60}
92 Editor_state = edit.initialize_test_state()
93 Editor_state.lines = load_array{'abc', 'def'}
94 Text.redraw_all(Editor_state)
95 Editor_state.cursor1 = {line=1, pos=4} -- past end of line
96 edit.draw(Editor_state)
97 edit.run_after_keychord(Editor_state, 'right')
98 check_eq(Editor_state.cursor1.line, 2, 'line')
99 check_eq(Editor_state.cursor1.pos, 1, 'pos')
102 function test_move_to_start_of_word()
103 App.screen.init{width=120, height=60}
104 Editor_state = edit.initialize_test_state()
105 Editor_state.lines = load_array{'abc'}
106 Text.redraw_all(Editor_state)
107 Editor_state.cursor1 = {line=1, pos=3}
108 edit.draw(Editor_state)
109 edit.run_after_keychord(Editor_state, 'M-left')
110 check_eq(Editor_state.cursor1.pos, 1, 'check')
113 function test_move_to_start_of_previous_word()
114 App.screen.init{width=120, height=60}
115 Editor_state = edit.initialize_test_state()
116 Editor_state.lines = load_array{'abc def'}
117 Text.redraw_all(Editor_state)
118 Editor_state.cursor1 = {line=1, pos=4} -- at the space between words
119 edit.draw(Editor_state)
120 edit.run_after_keychord(Editor_state, 'M-left')
121 check_eq(Editor_state.cursor1.pos, 1, 'check')
124 function test_skip_to_previous_word()
125 App.screen.init{width=120, height=60}
126 Editor_state = edit.initialize_test_state()
127 Editor_state.lines = load_array{'abc def'}
128 Text.redraw_all(Editor_state)
129 Editor_state.cursor1 = {line=1, pos=5} -- at the start of second word
130 edit.draw(Editor_state)
131 edit.run_after_keychord(Editor_state, 'M-left')
132 check_eq(Editor_state.cursor1.pos, 1, 'check')
135 function test_skip_past_tab_to_previous_word()
136 App.screen.init{width=120, height=60}
137 Editor_state = edit.initialize_test_state()
138 Editor_state.lines = load_array{'abc def\tghi'}
139 Text.redraw_all(Editor_state)
140 Editor_state.cursor1 = {line=1, pos=10} -- within third word
141 edit.draw(Editor_state)
142 edit.run_after_keychord(Editor_state, 'M-left')
143 check_eq(Editor_state.cursor1.pos, 9, 'check')
146 function test_skip_multiple_spaces_to_previous_word()
147 App.screen.init{width=120, height=60}
148 Editor_state = edit.initialize_test_state()
149 Editor_state.lines = load_array{'abc def'}
150 Text.redraw_all(Editor_state)
151 Editor_state.cursor1 = {line=1, pos=6} -- at the start of second word
152 edit.draw(Editor_state)
153 edit.run_after_keychord(Editor_state, 'M-left')
154 check_eq(Editor_state.cursor1.pos, 1, 'check')
157 function test_move_to_start_of_word_on_previous_line()
158 App.screen.init{width=120, height=60}
159 Editor_state = edit.initialize_test_state()
160 Editor_state.lines = load_array{'abc def', 'ghi'}
161 Text.redraw_all(Editor_state)
162 Editor_state.cursor1 = {line=2, pos=1}
163 edit.draw(Editor_state)
164 edit.run_after_keychord(Editor_state, 'M-left')
165 check_eq(Editor_state.cursor1.line, 1, 'line')
166 check_eq(Editor_state.cursor1.pos, 5, 'pos')
169 function test_move_past_end_of_word()
170 App.screen.init{width=120, height=60}
171 Editor_state = edit.initialize_test_state()
172 Editor_state.lines = load_array{'abc def'}
173 Text.redraw_all(Editor_state)
174 Editor_state.cursor1 = {line=1, pos=1}
175 edit.draw(Editor_state)
176 edit.run_after_keychord(Editor_state, 'M-right')
177 check_eq(Editor_state.cursor1.pos, 4, 'check')
180 function test_skip_to_next_word()
181 App.screen.init{width=120, height=60}
182 Editor_state = edit.initialize_test_state()
183 Editor_state.lines = load_array{'abc def'}
184 Text.redraw_all(Editor_state)
185 Editor_state.cursor1 = {line=1, pos=4} -- at the space between words
186 edit.draw(Editor_state)
187 edit.run_after_keychord(Editor_state, 'M-right')
188 check_eq(Editor_state.cursor1.pos, 8, 'check')
191 function test_skip_past_tab_to_next_word()
192 App.screen.init{width=120, height=60}
193 Editor_state = edit.initialize_test_state()
194 Editor_state.lines = load_array{'abc\tdef'}
195 Text.redraw_all(Editor_state)
196 Editor_state.cursor1 = {line=1, pos=1} -- at the space between words
197 edit.draw(Editor_state)
198 edit.run_after_keychord(Editor_state, 'M-right')
199 check_eq(Editor_state.cursor1.pos, 4, 'check')
202 function test_skip_multiple_spaces_to_next_word()
203 App.screen.init{width=120, height=60}
204 Editor_state = edit.initialize_test_state()
205 Editor_state.lines = load_array{'abc def'}
206 Text.redraw_all(Editor_state)
207 Editor_state.cursor1 = {line=1, pos=4} -- at the start of second word
208 edit.draw(Editor_state)
209 edit.run_after_keychord(Editor_state, 'M-right')
210 check_eq(Editor_state.cursor1.pos, 9, 'check')
213 function test_move_past_end_of_word_on_next_line()
214 App.screen.init{width=120, height=60}
215 Editor_state = edit.initialize_test_state()
216 Editor_state.lines = load_array{'abc def', 'ghi'}
217 Text.redraw_all(Editor_state)
218 Editor_state.cursor1 = {line=1, pos=8}
219 edit.draw(Editor_state)
220 edit.run_after_keychord(Editor_state, 'M-right')
221 check_eq(Editor_state.cursor1.line, 2, 'line')
222 check_eq(Editor_state.cursor1.pos, 4, 'pos')
225 function test_click_moves_cursor()
226 App.screen.init{width=50, height=60}
227 Editor_state = edit.initialize_test_state()
228 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
229 Text.redraw_all(Editor_state)
230 Editor_state.cursor1 = {line=1, pos=1}
231 Editor_state.screen_top1 = {line=1, pos=1}
232 Editor_state.screen_bottom1 = {}
233 Editor_state.selection1 = {}
234 edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
235 edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
236 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
237 check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
238 -- selection is empty to avoid perturbing future edits
239 check_nil(Editor_state.selection1.line, 'selection:line')
240 check_nil(Editor_state.selection1.pos, 'selection:pos')
243 function test_click_to_left_of_line()
244 -- display a line with the cursor in the middle
245 App.screen.init{width=50, height=80}
246 Editor_state = edit.initialize_test_state()
247 Editor_state.lines = load_array{'abc'}
248 Text.redraw_all(Editor_state)
249 Editor_state.cursor1 = {line=1, pos=3}
250 Editor_state.screen_top1 = {line=1, pos=1}
251 Editor_state.screen_bottom1 = {}
252 Editor_state.selection1 = {}
253 -- click to the left of the line
254 edit.draw(Editor_state)
255 edit.run_after_mouse_click(Editor_state, Editor_state.left-4,Editor_state.top+5, 1)
256 -- cursor moves to start of line
257 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
258 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
259 check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
262 function test_click_takes_margins_into_account()
263 -- display two lines with cursor on one of them
264 App.screen.init{width=100, height=80}
265 Editor_state = edit.initialize_test_state()
266 Editor_state.left = 50 -- occupy only right side of screen
267 Editor_state.lines = load_array{'abc', 'def'}
268 Text.redraw_all(Editor_state)
269 Editor_state.cursor1 = {line=2, pos=1}
270 Editor_state.screen_top1 = {line=1, pos=1}
271 Editor_state.screen_bottom1 = {}
272 Editor_state.selection1 = {}
273 -- click on the other line
274 edit.draw(Editor_state)
275 edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
276 -- cursor moves
277 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
278 check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
279 check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
282 function test_click_on_empty_line()
283 -- display two lines with the first one empty
284 App.screen.init{width=50, height=80}
285 Editor_state = edit.initialize_test_state()
286 Editor_state.lines = load_array{'', 'def'}
287 Text.redraw_all(Editor_state)
288 Editor_state.cursor1 = {line=2, pos=1}
289 Editor_state.screen_top1 = {line=1, pos=1}
290 Editor_state.screen_bottom1 = {}
291 Editor_state.selection1 = {}
292 -- click on the empty line
293 edit.draw(Editor_state)
294 edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
295 -- cursor moves
296 check_eq(Editor_state.cursor1.line, 1, 'cursor')
297 -- selection remains empty
298 check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
301 function test_click_below_all_lines()
302 -- display one line
303 App.screen.init{width=50, height=80}
304 Editor_state = edit.initialize_test_state()
305 Editor_state.lines = load_array{'abc'}
306 Text.redraw_all(Editor_state)
307 Editor_state.cursor1 = {line=1, pos=1}
308 Editor_state.screen_top1 = {line=1, pos=1}
309 Editor_state.screen_bottom1 = {}
310 Editor_state.selection1 = {}
311 -- click below first line
312 edit.draw(Editor_state)
313 edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+50, 1)
314 -- cursor doesn't move
315 check_eq(Editor_state.cursor1.line, 1, 'cursor')
316 -- selection remains empty
317 check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
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_starting_above_text()
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 -- press mouse above first line of text
842 edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)
843 check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')
844 check_eq(Editor_state.selection1.line, 1, 'selection:line')
845 check_eq(Editor_state.selection1.pos, 1, 'selection:pos')
848 function test_select_text_using_mouse_starting_above_text_wrapping_line()
849 -- first screen line starts in the middle of a line
850 App.screen.init{width=50, height=60}
851 Editor_state = edit.initialize_test_state()
852 Editor_state.lines = load_array{'abc', 'defgh', 'xyz'}
853 Text.redraw_all(Editor_state)
854 Editor_state.cursor1 = {line=2, pos=5}
855 Editor_state.screen_top1 = {line=2, pos=3}
856 Editor_state.screen_bottom1 = {}
857 -- press mouse above first line of text
858 edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)
859 -- selection is at screen top
860 check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')
861 check_eq(Editor_state.selection1.line, 2, 'selection:line')
862 check_eq(Editor_state.selection1.pos, 3, 'selection:pos')
865 function test_select_text_using_mouse_starting_below_text()
866 -- I'd like to test what happens when a mouse click is below some page of
867 -- text, potentially even in the middle of a line.
868 -- However, it's brittle to set up a text line boundary just right.
869 -- So I'm going to just check things below the bottom of the final line of
870 -- text when it's in the middle of the screen.
871 -- final screen line ends in the middle of screen
872 App.screen.init{width=50, height=60}
873 Editor_state = edit.initialize_test_state()
874 Editor_state.lines = load_array{'abcde'}
875 Text.redraw_all(Editor_state)
876 Editor_state.cursor1 = {line=1, pos=1}
877 Editor_state.screen_top1 = {line=1, pos=1}
878 Editor_state.screen_bottom1 = {}
879 edit.draw(Editor_state)
880 local y = Editor_state.top
881 App.screen.check(y, 'ab', 'baseline:screen:1')
882 y = y + Editor_state.line_height
883 App.screen.check(y, 'cde', 'baseline:screen:2')
884 -- press mouse above first line of text
885 edit.run_after_mouse_press(Editor_state, 5,App.screen.height-5, 1)
886 -- selection is past bottom-most text in screen
887 check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')
888 check_eq(Editor_state.selection1.line, 1, 'selection:line')
889 check_eq(Editor_state.selection1.pos, 6, 'selection:pos')
892 function test_select_text_using_mouse_and_shift()
893 App.screen.init{width=50, height=60}
894 Editor_state = edit.initialize_test_state()
895 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
896 Text.redraw_all(Editor_state)
897 Editor_state.cursor1 = {line=1, pos=1}
898 Editor_state.screen_top1 = {line=1, pos=1}
899 Editor_state.screen_bottom1 = {}
900 Editor_state.selection1 = {}
901 edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
902 -- click on first location
903 edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
904 edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
905 -- hold down shift and click somewhere else
906 App.fake_key_press('lshift')
907 edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
908 edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
909 App.fake_key_release('lshift')
910 check_eq(Editor_state.selection1.line, 1, 'selection:line')
911 check_eq(Editor_state.selection1.pos, 2, 'selection:pos')
912 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
913 check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
916 function test_select_text_repeatedly_using_mouse_and_shift()
917 App.screen.init{width=50, height=60}
918 Editor_state = edit.initialize_test_state()
919 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
920 Text.redraw_all(Editor_state)
921 Text.redraw_all(Editor_state)
922 Editor_state.cursor1 = {line=1, pos=1}
923 Editor_state.screen_top1 = {line=1, pos=1}
924 Editor_state.screen_bottom1 = {}
925 Editor_state.selection1 = {}
926 edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
927 -- click on first location
928 edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
929 edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
930 -- hold down shift and click on a second location
931 App.fake_key_press('lshift')
932 edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
933 edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
934 -- hold down shift and click at a third location
935 App.fake_key_press('lshift')
936 edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
937 edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+Editor_state.line_height+5, 1)
938 App.fake_key_release('lshift')
939 -- selection is between first and third location. forget the second location, not the first.
940 check_eq(Editor_state.selection1.line, 1, 'selection:line')
941 check_eq(Editor_state.selection1.pos, 2, 'selection:pos')
942 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
943 check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
946 function test_select_all_text()
947 -- display a single line of text
948 App.screen.init{width=75, height=80}
949 Editor_state = edit.initialize_test_state()
950 Editor_state.lines = load_array{'abc def'}
951 Text.redraw_all(Editor_state)
952 Editor_state.cursor1 = {line=1, pos=1}
953 Editor_state.screen_top1 = {line=1, pos=1}
954 Editor_state.screen_bottom1 = {}
955 edit.draw(Editor_state)
956 -- select all
957 App.fake_key_press('lctrl')
958 edit.run_after_keychord(Editor_state, 'C-a')
959 App.fake_key_release('lctrl')
960 edit.key_release(Editor_state, 'lctrl')
961 -- selection
962 check_eq(Editor_state.selection1.line, 1, 'selection:line')
963 check_eq(Editor_state.selection1.pos, 1, 'selection:pos')
964 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
965 check_eq(Editor_state.cursor1.pos, 8, 'cursor:pos')
968 function test_cut_without_selection()
969 -- display a few lines
970 App.screen.init{width=Editor_state.left+30, height=60}
971 Editor_state = edit.initialize_test_state()
972 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
973 Text.redraw_all(Editor_state)
974 Editor_state.cursor1 = {line=1, pos=2}
975 Editor_state.screen_top1 = {line=1, pos=1}
976 Editor_state.screen_bottom1 = {}
977 Editor_state.selection1 = {}
978 edit.draw(Editor_state)
979 -- try to cut without selecting text
980 edit.run_after_keychord(Editor_state, 'C-x')
981 -- no crash
982 check_nil(Editor_state.selection1.line, 'check')
985 function test_pagedown()
986 App.screen.init{width=120, height=45}
987 Editor_state = edit.initialize_test_state()
988 Editor_state.lines = load_array{'abc', 'def', 'ghi'}
989 Text.redraw_all(Editor_state)
990 Editor_state.cursor1 = {line=1, pos=1}
991 Editor_state.screen_top1 = {line=1, pos=1}
992 Editor_state.screen_bottom1 = {}
993 -- initially the first two lines are displayed
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 -- after pagedown the bottom line becomes the top
1000 edit.run_after_keychord(Editor_state, 'pagedown')
1001 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1002 check_eq(Editor_state.cursor1.line, 2, 'cursor')
1003 y = Editor_state.top
1004 App.screen.check(y, 'def', 'screen:1')
1005 y = y + Editor_state.line_height
1006 App.screen.check(y, 'ghi', 'screen:2')
1009 function test_pagedown_often_shows_start_of_wrapping_line()
1010 -- draw a few lines ending in part of a wrapping line
1011 App.screen.init{width=50, height=60}
1012 Editor_state = edit.initialize_test_state()
1013 Editor_state.lines = load_array{'abc', 'def ghi jkl', 'mno'}
1014 Text.redraw_all(Editor_state)
1015 Editor_state.cursor1 = {line=1, pos=1}
1016 Editor_state.screen_top1 = {line=1, pos=1}
1017 Editor_state.screen_bottom1 = {}
1018 edit.draw(Editor_state)
1019 local y = Editor_state.top
1020 App.screen.check(y, 'abc', 'baseline/screen:1')
1021 y = y + Editor_state.line_height
1022 App.screen.check(y, 'def ', 'baseline/screen:2')
1023 y = y + Editor_state.line_height
1024 App.screen.check(y, 'ghi ', 'baseline/screen:3')
1025 -- after pagedown we start drawing from the bottom _line_ (multiple screen lines)
1026 edit.run_after_keychord(Editor_state, 'pagedown')
1027 check_eq(Editor_state.screen_top1.line, 2, 'screen_top:line')
1028 check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
1029 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
1030 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1031 y = Editor_state.top
1032 App.screen.check(y, 'def ', 'screen:1')
1033 y = y + Editor_state.line_height
1034 App.screen.check(y, 'ghi ', 'screen:2')
1035 y = y + Editor_state.line_height
1036 App.screen.check(y, 'jkl', 'screen:3')
1039 function test_pagedown_can_start_from_middle_of_long_wrapping_line()
1040 -- draw a few lines starting from a very long wrapping line
1041 App.screen.init{width=Editor_state.left+30, height=60}
1042 Editor_state = edit.initialize_test_state()
1043 Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu vwx yza bcd efg hij', 'XYZ'}
1044 Text.redraw_all(Editor_state)
1045 Editor_state.cursor1 = {line=1, pos=2}
1046 Editor_state.screen_top1 = {line=1, pos=1}
1047 Editor_state.screen_bottom1 = {}
1048 edit.draw(Editor_state)
1049 local y = Editor_state.top
1050 App.screen.check(y, 'abc ', 'baseline/screen:1')
1051 y = y + Editor_state.line_height
1052 App.screen.check(y, 'def ', 'baseline/screen:2')
1053 y = y + Editor_state.line_height
1054 App.screen.check(y, 'ghi ', 'baseline/screen:3')
1055 -- after pagedown we scroll down the very long wrapping line
1056 edit.run_after_keychord(Editor_state, 'pagedown')
1057 check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')
1058 check_eq(Editor_state.screen_top1.pos, 9, 'screen_top:pos')
1059 y = Editor_state.top
1060 App.screen.check(y, 'ghi ', 'screen:1')
1061 y = y + Editor_state.line_height
1062 App.screen.check(y, 'jkl ', 'screen:2')
1063 y = y + Editor_state.line_height
1064 if Version == '12.0' then
1065 -- HACK: Maybe v12.0 uses a different font? Strange that it only causes
1066 -- issues in a couple of places.
1067 -- We'll need to rethink our tests if issues like this start to multiply.
1068 App.screen.check(y, 'mno ', 'screen:3')
1069 else
1070 App.screen.check(y, 'mn', 'screen:3')
1074 function test_pagedown_never_moves_up()
1075 -- draw the final screen line of a wrapping line
1076 App.screen.init{width=Editor_state.left+30, height=60}
1077 Editor_state = edit.initialize_test_state()
1078 Editor_state.lines = load_array{'abc def ghi'}
1079 Text.redraw_all(Editor_state)
1080 Editor_state.cursor1 = {line=1, pos=9}
1081 Editor_state.screen_top1 = {line=1, pos=9}
1082 Editor_state.screen_bottom1 = {}
1083 edit.draw(Editor_state)
1084 -- pagedown makes no change
1085 edit.run_after_keychord(Editor_state, 'pagedown')
1086 check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')
1087 check_eq(Editor_state.screen_top1.pos, 9, 'screen_top:pos')
1090 function test_down_arrow_moves_cursor()
1091 App.screen.init{width=120, height=60}
1092 Editor_state = edit.initialize_test_state()
1093 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1094 Text.redraw_all(Editor_state)
1095 Editor_state.cursor1 = {line=1, pos=1}
1096 Editor_state.screen_top1 = {line=1, pos=1}
1097 Editor_state.screen_bottom1 = {}
1098 -- initially the first three lines are displayed
1099 edit.draw(Editor_state)
1100 local y = Editor_state.top
1101 App.screen.check(y, 'abc', 'baseline/screen:1')
1102 y = y + Editor_state.line_height
1103 App.screen.check(y, 'def', 'baseline/screen:2')
1104 y = y + Editor_state.line_height
1105 App.screen.check(y, 'ghi', 'baseline/screen:3')
1106 -- after hitting the down arrow, the cursor moves down by 1 line
1107 edit.run_after_keychord(Editor_state, 'down')
1108 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1109 check_eq(Editor_state.cursor1.line, 2, 'cursor')
1110 -- the screen is unchanged
1111 y = Editor_state.top
1112 App.screen.check(y, 'abc', 'screen:1')
1113 y = y + Editor_state.line_height
1114 App.screen.check(y, 'def', 'screen:2')
1115 y = y + Editor_state.line_height
1116 App.screen.check(y, 'ghi', 'screen:3')
1119 function test_down_arrow_scrolls_down_by_one_line()
1120 -- display the first three lines with the cursor on the bottom line
1121 App.screen.init{width=120, height=60}
1122 Editor_state = edit.initialize_test_state()
1123 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1124 Text.redraw_all(Editor_state)
1125 Editor_state.cursor1 = {line=3, pos=1}
1126 Editor_state.screen_top1 = {line=1, pos=1}
1127 Editor_state.screen_bottom1 = {}
1128 edit.draw(Editor_state)
1129 local y = Editor_state.top
1130 App.screen.check(y, 'abc', 'baseline/screen:1')
1131 y = y + Editor_state.line_height
1132 App.screen.check(y, 'def', 'baseline/screen:2')
1133 y = y + Editor_state.line_height
1134 App.screen.check(y, 'ghi', 'baseline/screen:3')
1135 -- after hitting the down arrow the screen scrolls down by one line
1136 edit.run_after_keychord(Editor_state, 'down')
1137 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1138 check_eq(Editor_state.cursor1.line, 4, 'cursor')
1139 y = Editor_state.top
1140 App.screen.check(y, 'def', 'screen:1')
1141 y = y + Editor_state.line_height
1142 App.screen.check(y, 'ghi', 'screen:2')
1143 y = y + Editor_state.line_height
1144 App.screen.check(y, 'jkl', 'screen:3')
1147 function test_down_arrow_scrolls_down_by_one_screen_line()
1148 -- display the first three lines with the cursor on the bottom line
1149 App.screen.init{width=Editor_state.left+30, height=60}
1150 Editor_state = edit.initialize_test_state()
1151 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1152 Text.redraw_all(Editor_state)
1153 Editor_state.cursor1 = {line=3, pos=1}
1154 Editor_state.screen_top1 = {line=1, pos=1}
1155 Editor_state.screen_bottom1 = {}
1156 edit.draw(Editor_state)
1157 local y = Editor_state.top
1158 App.screen.check(y, 'abc', 'baseline/screen:1')
1159 y = y + Editor_state.line_height
1160 App.screen.check(y, 'def', 'baseline/screen:2')
1161 y = y + Editor_state.line_height
1162 App.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1163 -- after hitting the down arrow the screen scrolls down by one line
1164 edit.run_after_keychord(Editor_state, 'down')
1165 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1166 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1167 check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
1168 y = Editor_state.top
1169 App.screen.check(y, 'def', 'screen:1')
1170 y = y + Editor_state.line_height
1171 App.screen.check(y, 'ghi ', 'screen:2')
1172 y = y + Editor_state.line_height
1173 App.screen.check(y, 'jkl', 'screen:3')
1176 function test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word()
1177 -- display the first three lines with the cursor on the bottom line
1178 App.screen.init{width=Editor_state.left+30, height=60}
1179 Editor_state = edit.initialize_test_state()
1180 Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}
1181 Text.redraw_all(Editor_state)
1182 Editor_state.cursor1 = {line=3, pos=1}
1183 Editor_state.screen_top1 = {line=1, pos=1}
1184 Editor_state.screen_bottom1 = {}
1185 edit.draw(Editor_state)
1186 local y = Editor_state.top
1187 App.screen.check(y, 'abc', 'baseline/screen:1')
1188 y = y + Editor_state.line_height
1189 App.screen.check(y, 'def', 'baseline/screen:2')
1190 y = y + Editor_state.line_height
1191 App.screen.check(y, 'ghij', 'baseline/screen:3')
1192 -- after hitting the down arrow the screen scrolls down by one line
1193 edit.run_after_keychord(Editor_state, 'down')
1194 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1195 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1196 check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
1197 y = Editor_state.top
1198 App.screen.check(y, 'def', 'screen:1')
1199 y = y + Editor_state.line_height
1200 App.screen.check(y, 'ghij', 'screen:2')
1201 y = y + Editor_state.line_height
1202 App.screen.check(y, 'kl', 'screen:3')
1205 function test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up()
1206 App.screen.init{width=Editor_state.left+30, height=60}
1207 Editor_state = edit.initialize_test_state()
1208 Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}
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 App.screen.check(y, 'def', 'baseline/screen:2')
1218 y = y + Editor_state.line_height
1219 App.screen.check(y, 'ghij', 'baseline/screen:3')
1220 -- after hitting pagedown the screen scrolls down to start of a long line
1221 edit.run_after_keychord(Editor_state, 'pagedown')
1222 check_eq(Editor_state.screen_top1.line, 3, 'baseline2/screen_top')
1223 check_eq(Editor_state.cursor1.line, 3, 'baseline2/cursor:line')
1224 check_eq(Editor_state.cursor1.pos, 1, 'baseline2/cursor:pos')
1225 -- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll up
1226 edit.run_after_keychord(Editor_state, 'down')
1227 check_eq(Editor_state.screen_top1.line, 3, 'screen_top')
1228 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1229 check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
1230 y = Editor_state.top
1231 App.screen.check(y, 'ghij', 'screen:1')
1232 y = y + Editor_state.line_height
1233 App.screen.check(y, 'kl', 'screen:2')
1234 y = y + Editor_state.line_height
1235 App.screen.check(y, 'mno', 'screen:3')
1238 function test_up_arrow_moves_cursor()
1239 -- display the first 3 lines with the cursor on the bottom line
1240 App.screen.init{width=120, height=60}
1241 Editor_state = edit.initialize_test_state()
1242 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1243 Text.redraw_all(Editor_state)
1244 Editor_state.cursor1 = {line=3, pos=1}
1245 Editor_state.screen_top1 = {line=1, pos=1}
1246 Editor_state.screen_bottom1 = {}
1247 edit.draw(Editor_state)
1248 local y = Editor_state.top
1249 App.screen.check(y, 'abc', 'baseline/screen:1')
1250 y = y + Editor_state.line_height
1251 App.screen.check(y, 'def', 'baseline/screen:2')
1252 y = y + Editor_state.line_height
1253 App.screen.check(y, 'ghi', 'baseline/screen:3')
1254 -- after hitting the up arrow the cursor moves up by 1 line
1255 edit.run_after_keychord(Editor_state, 'up')
1256 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1257 check_eq(Editor_state.cursor1.line, 2, 'cursor')
1258 -- the screen is unchanged
1259 y = Editor_state.top
1260 App.screen.check(y, 'abc', 'screen:1')
1261 y = y + Editor_state.line_height
1262 App.screen.check(y, 'def', 'screen:2')
1263 y = y + Editor_state.line_height
1264 App.screen.check(y, 'ghi', 'screen:3')
1267 function test_up_arrow_scrolls_up_by_one_line()
1268 -- display the lines 2/3/4 with the cursor on line 2
1269 App.screen.init{width=120, height=60}
1270 Editor_state = edit.initialize_test_state()
1271 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1272 Text.redraw_all(Editor_state)
1273 Editor_state.cursor1 = {line=2, pos=1}
1274 Editor_state.screen_top1 = {line=2, pos=1}
1275 Editor_state.screen_bottom1 = {}
1276 edit.draw(Editor_state)
1277 local y = Editor_state.top
1278 App.screen.check(y, 'def', 'baseline/screen:1')
1279 y = y + Editor_state.line_height
1280 App.screen.check(y, 'ghi', 'baseline/screen:2')
1281 y = y + Editor_state.line_height
1282 App.screen.check(y, 'jkl', 'baseline/screen:3')
1283 -- after hitting the up arrow the screen scrolls up by one line
1284 edit.run_after_keychord(Editor_state, 'up')
1285 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1286 check_eq(Editor_state.cursor1.line, 1, 'cursor')
1287 y = Editor_state.top
1288 App.screen.check(y, 'abc', 'screen:1')
1289 y = y + Editor_state.line_height
1290 App.screen.check(y, 'def', 'screen:2')
1291 y = y + Editor_state.line_height
1292 App.screen.check(y, 'ghi', 'screen:3')
1295 function test_up_arrow_scrolls_up_by_one_screen_line()
1296 -- display lines starting from second screen line of a line
1297 App.screen.init{width=Editor_state.left+30, height=60}
1298 Editor_state = edit.initialize_test_state()
1299 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1300 Text.redraw_all(Editor_state)
1301 Editor_state.cursor1 = {line=3, pos=6}
1302 Editor_state.screen_top1 = {line=3, pos=5}
1303 Editor_state.screen_bottom1 = {}
1304 edit.draw(Editor_state)
1305 local y = Editor_state.top
1306 App.screen.check(y, 'jkl', 'baseline/screen:1')
1307 y = y + Editor_state.line_height
1308 App.screen.check(y, 'mno', 'baseline/screen:2')
1309 -- after hitting the up arrow the screen scrolls up to first screen line
1310 edit.run_after_keychord(Editor_state, 'up')
1311 y = Editor_state.top
1312 App.screen.check(y, 'ghi ', 'screen:1')
1313 y = y + Editor_state.line_height
1314 App.screen.check(y, 'jkl', 'screen:2')
1315 y = y + Editor_state.line_height
1316 App.screen.check(y, 'mno', 'screen:3')
1317 check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')
1318 check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
1319 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1320 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1323 function test_up_arrow_scrolls_up_to_final_screen_line()
1324 -- display lines starting just after a long line
1325 App.screen.init{width=Editor_state.left+30, height=60}
1326 Editor_state = edit.initialize_test_state()
1327 Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}
1328 Text.redraw_all(Editor_state)
1329 Editor_state.cursor1 = {line=2, pos=1}
1330 Editor_state.screen_top1 = {line=2, pos=1}
1331 Editor_state.screen_bottom1 = {}
1332 edit.draw(Editor_state)
1333 local y = Editor_state.top
1334 App.screen.check(y, 'ghi', 'baseline/screen:1')
1335 y = y + Editor_state.line_height
1336 App.screen.check(y, 'jkl', 'baseline/screen:2')
1337 y = y + Editor_state.line_height
1338 App.screen.check(y, 'mno', 'baseline/screen:3')
1339 -- after hitting the up arrow the screen scrolls up to final screen line of previous line
1340 edit.run_after_keychord(Editor_state, 'up')
1341 y = Editor_state.top
1342 App.screen.check(y, 'def', 'screen:1')
1343 y = y + Editor_state.line_height
1344 App.screen.check(y, 'ghi', 'screen:2')
1345 y = y + Editor_state.line_height
1346 App.screen.check(y, 'jkl', 'screen:3')
1347 check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')
1348 check_eq(Editor_state.screen_top1.pos, 5, 'screen_top:pos')
1349 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1350 check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
1353 function test_up_arrow_scrolls_up_to_empty_line()
1354 -- display a screenful of text with an empty line just above it outside the screen
1355 App.screen.init{width=120, height=60}
1356 Editor_state = edit.initialize_test_state()
1357 Editor_state.lines = load_array{'', 'abc', 'def', 'ghi', 'jkl'}
1358 Text.redraw_all(Editor_state)
1359 Editor_state.cursor1 = {line=2, pos=1}
1360 Editor_state.screen_top1 = {line=2, pos=1}
1361 Editor_state.screen_bottom1 = {}
1362 edit.draw(Editor_state)
1363 local y = Editor_state.top
1364 App.screen.check(y, 'abc', 'baseline/screen:1')
1365 y = y + Editor_state.line_height
1366 App.screen.check(y, 'def', 'baseline/screen:2')
1367 y = y + Editor_state.line_height
1368 App.screen.check(y, 'ghi', 'baseline/screen:3')
1369 -- after hitting the up arrow the screen scrolls up by one line
1370 edit.run_after_keychord(Editor_state, 'up')
1371 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1372 check_eq(Editor_state.cursor1.line, 1, 'cursor')
1373 y = Editor_state.top
1374 -- empty first line
1375 y = y + Editor_state.line_height
1376 App.screen.check(y, 'abc', 'screen:2')
1377 y = y + Editor_state.line_height
1378 App.screen.check(y, 'def', 'screen:3')
1381 function test_pageup()
1382 App.screen.init{width=120, height=45}
1383 Editor_state = edit.initialize_test_state()
1384 Editor_state.lines = load_array{'abc', 'def', 'ghi'}
1385 Text.redraw_all(Editor_state)
1386 Editor_state.cursor1 = {line=2, pos=1}
1387 Editor_state.screen_top1 = {line=2, pos=1}
1388 Editor_state.screen_bottom1 = {}
1389 -- initially the last two lines are displayed
1390 edit.draw(Editor_state)
1391 local y = Editor_state.top
1392 App.screen.check(y, 'def', 'baseline/screen:1')
1393 y = y + Editor_state.line_height
1394 App.screen.check(y, 'ghi', 'baseline/screen:2')
1395 -- after pageup the cursor goes to first line
1396 edit.run_after_keychord(Editor_state, 'pageup')
1397 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1398 check_eq(Editor_state.cursor1.line, 1, 'cursor')
1399 y = Editor_state.top
1400 App.screen.check(y, 'abc', 'screen:1')
1401 y = y + Editor_state.line_height
1402 App.screen.check(y, 'def', 'screen:2')
1405 function test_pageup_scrolls_up_by_screen_line()
1406 -- display the first three lines with the cursor on the bottom line
1407 App.screen.init{width=Editor_state.left+30, height=60}
1408 Editor_state = edit.initialize_test_state()
1409 Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}
1410 Text.redraw_all(Editor_state)
1411 Editor_state.cursor1 = {line=2, pos=1}
1412 Editor_state.screen_top1 = {line=2, pos=1}
1413 Editor_state.screen_bottom1 = {}
1414 edit.draw(Editor_state)
1415 local y = Editor_state.top
1416 App.screen.check(y, 'ghi', 'baseline/screen:1')
1417 y = y + Editor_state.line_height
1418 App.screen.check(y, 'jkl', 'baseline/screen:2')
1419 y = y + Editor_state.line_height
1420 App.screen.check(y, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1421 -- after hitting the page-up key the screen scrolls up to top
1422 edit.run_after_keychord(Editor_state, 'pageup')
1423 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1424 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1425 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1426 y = Editor_state.top
1427 App.screen.check(y, 'abc ', 'screen:1')
1428 y = y + Editor_state.line_height
1429 App.screen.check(y, 'def', 'screen:2')
1430 y = y + Editor_state.line_height
1431 App.screen.check(y, 'ghi', 'screen:3')
1434 function test_pageup_scrolls_up_from_middle_screen_line()
1435 -- display a few lines starting from the middle of a line (Editor_state.cursor1.pos > 1)
1436 App.screen.init{width=Editor_state.left+30, height=60}
1437 Editor_state = edit.initialize_test_state()
1438 Editor_state.lines = load_array{'abc def', 'ghi jkl', 'mno'}
1439 Text.redraw_all(Editor_state)
1440 Editor_state.cursor1 = {line=2, pos=5}
1441 Editor_state.screen_top1 = {line=2, pos=5}
1442 Editor_state.screen_bottom1 = {}
1443 edit.draw(Editor_state)
1444 local y = Editor_state.top
1445 App.screen.check(y, 'jkl', 'baseline/screen:2')
1446 y = y + Editor_state.line_height
1447 App.screen.check(y, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1448 -- after hitting the page-up key the screen scrolls up to top
1449 edit.run_after_keychord(Editor_state, 'pageup')
1450 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1451 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1452 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1453 y = Editor_state.top
1454 App.screen.check(y, 'abc ', 'screen:1')
1455 y = y + Editor_state.line_height
1456 App.screen.check(y, 'def', 'screen:2')
1457 y = y + Editor_state.line_height
1458 App.screen.check(y, 'ghi ', 'screen:3')
1461 function test_enter_on_bottom_line_scrolls_down()
1462 -- display a few lines with cursor on bottom line
1463 App.screen.init{width=Editor_state.left+30, height=60}
1464 Editor_state = edit.initialize_test_state()
1465 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1466 Text.redraw_all(Editor_state)
1467 Editor_state.cursor1 = {line=3, pos=2}
1468 Editor_state.screen_top1 = {line=1, pos=1}
1469 Editor_state.screen_bottom1 = {}
1470 edit.draw(Editor_state)
1471 local y = Editor_state.top
1472 App.screen.check(y, 'abc', 'baseline/screen:1')
1473 y = y + Editor_state.line_height
1474 App.screen.check(y, 'def', 'baseline/screen:2')
1475 y = y + Editor_state.line_height
1476 App.screen.check(y, 'ghi', 'baseline/screen:3')
1477 -- after hitting the enter key the screen scrolls down
1478 edit.run_after_keychord(Editor_state, 'return')
1479 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1480 check_eq(Editor_state.cursor1.line, 4, 'cursor:line')
1481 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1482 y = Editor_state.top
1483 App.screen.check(y, 'def', 'screen:1')
1484 y = y + Editor_state.line_height
1485 App.screen.check(y, 'g', 'screen:2')
1486 y = y + Editor_state.line_height
1487 App.screen.check(y, 'hi', 'screen:3')
1490 function test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1491 -- display just the bottom line on screen
1492 App.screen.init{width=Editor_state.left+30, height=60}
1493 Editor_state = edit.initialize_test_state()
1494 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1495 Text.redraw_all(Editor_state)
1496 Editor_state.cursor1 = {line=4, pos=2}
1497 Editor_state.screen_top1 = {line=4, pos=1}
1498 Editor_state.screen_bottom1 = {}
1499 edit.draw(Editor_state)
1500 local y = Editor_state.top
1501 App.screen.check(y, 'jkl', 'baseline/screen:1')
1502 -- after hitting the enter key the screen does not scroll down
1503 edit.run_after_keychord(Editor_state, 'return')
1504 check_eq(Editor_state.screen_top1.line, 4, 'screen_top')
1505 check_eq(Editor_state.cursor1.line, 5, 'cursor:line')
1506 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1507 y = Editor_state.top
1508 App.screen.check(y, 'j', 'screen:1')
1509 y = y + Editor_state.line_height
1510 App.screen.check(y, 'kl', 'screen:2')
1513 function test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1514 -- display just an empty bottom line on screen
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', ''}
1518 Text.redraw_all(Editor_state)
1519 Editor_state.cursor1 = {line=2, pos=1}
1520 Editor_state.screen_top1 = {line=2, pos=1}
1521 Editor_state.screen_bottom1 = {}
1522 edit.draw(Editor_state)
1523 -- after hitting the inserting_text key the screen does not scroll down
1524 edit.run_after_text_input(Editor_state, 'a')
1525 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1526 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
1527 check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
1528 local y = Editor_state.top
1529 App.screen.check(y, 'a', 'screen:1')
1532 function test_typing_on_bottom_line_scrolls_down()
1533 -- display a few lines with cursor on bottom line
1534 App.screen.init{width=Editor_state.left+30, height=60}
1535 Editor_state = edit.initialize_test_state()
1536 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1537 Text.redraw_all(Editor_state)
1538 Editor_state.cursor1 = {line=3, pos=4}
1539 Editor_state.screen_top1 = {line=1, pos=1}
1540 Editor_state.screen_bottom1 = {}
1541 edit.draw(Editor_state)
1542 local y = Editor_state.top
1543 App.screen.check(y, 'abc', 'baseline/screen:1')
1544 y = y + Editor_state.line_height
1545 App.screen.check(y, 'def', 'baseline/screen:2')
1546 y = y + Editor_state.line_height
1547 App.screen.check(y, 'ghi', 'baseline/screen:3')
1548 -- after typing something the line wraps and the screen scrolls down
1549 edit.run_after_text_input(Editor_state, 'j')
1550 edit.run_after_text_input(Editor_state, 'k')
1551 edit.run_after_text_input(Editor_state, 'l')
1552 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1553 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1554 check_eq(Editor_state.cursor1.pos, 7, 'cursor:pos')
1555 y = Editor_state.top
1556 App.screen.check(y, 'def', 'screen:1')
1557 y = y + Editor_state.line_height
1558 App.screen.check(y, 'ghij', 'screen:2')
1559 y = y + Editor_state.line_height
1560 App.screen.check(y, 'kl', 'screen:3')
1563 function test_left_arrow_scrolls_up_in_wrapped_line()
1564 -- display lines starting from second screen line of a line
1565 App.screen.init{width=Editor_state.left+30, height=60}
1566 Editor_state = edit.initialize_test_state()
1567 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1568 Text.redraw_all(Editor_state)
1569 Editor_state.screen_top1 = {line=3, pos=5}
1570 Editor_state.screen_bottom1 = {}
1571 -- cursor is at top of screen
1572 Editor_state.cursor1 = {line=3, pos=5}
1573 edit.draw(Editor_state)
1574 local y = Editor_state.top
1575 App.screen.check(y, 'jkl', 'baseline/screen:1')
1576 y = y + Editor_state.line_height
1577 App.screen.check(y, 'mno', 'baseline/screen:2')
1578 -- after hitting the left arrow the screen scrolls up to first screen line
1579 edit.run_after_keychord(Editor_state, 'left')
1580 y = Editor_state.top
1581 App.screen.check(y, 'ghi ', 'screen:1')
1582 y = y + Editor_state.line_height
1583 App.screen.check(y, 'jkl', 'screen:2')
1584 y = y + Editor_state.line_height
1585 App.screen.check(y, 'mno', 'screen:3')
1586 check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')
1587 check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
1588 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1589 check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
1592 function test_right_arrow_scrolls_down_in_wrapped_line()
1593 -- display the first three lines with the cursor on the bottom line
1594 App.screen.init{width=Editor_state.left+30, height=60}
1595 Editor_state = edit.initialize_test_state()
1596 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1597 Text.redraw_all(Editor_state)
1598 Editor_state.screen_top1 = {line=1, pos=1}
1599 Editor_state.screen_bottom1 = {}
1600 -- cursor is at bottom right of screen
1601 Editor_state.cursor1 = {line=3, pos=5}
1602 edit.draw(Editor_state)
1603 local y = Editor_state.top
1604 App.screen.check(y, 'abc', 'baseline/screen:1')
1605 y = y + Editor_state.line_height
1606 App.screen.check(y, 'def', 'baseline/screen:2')
1607 y = y + Editor_state.line_height
1608 App.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1609 -- after hitting the right arrow the screen scrolls down by one line
1610 edit.run_after_keychord(Editor_state, 'right')
1611 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1612 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1613 check_eq(Editor_state.cursor1.pos, 6, 'cursor:pos')
1614 y = Editor_state.top
1615 App.screen.check(y, 'def', 'screen:1')
1616 y = y + Editor_state.line_height
1617 App.screen.check(y, 'ghi ', 'screen:2')
1618 y = y + Editor_state.line_height
1619 App.screen.check(y, 'jkl', 'screen:3')
1622 function test_home_scrolls_up_in_wrapped_line()
1623 -- display lines starting from second screen line of a line
1624 App.screen.init{width=Editor_state.left+30, height=60}
1625 Editor_state = edit.initialize_test_state()
1626 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1627 Text.redraw_all(Editor_state)
1628 Editor_state.screen_top1 = {line=3, pos=5}
1629 Editor_state.screen_bottom1 = {}
1630 -- cursor is at top of screen
1631 Editor_state.cursor1 = {line=3, pos=5}
1632 edit.draw(Editor_state)
1633 local y = Editor_state.top
1634 App.screen.check(y, 'jkl', 'baseline/screen:1')
1635 y = y + Editor_state.line_height
1636 App.screen.check(y, 'mno', 'baseline/screen:2')
1637 -- after hitting home the screen scrolls up to first screen line
1638 edit.run_after_keychord(Editor_state, 'home')
1639 y = Editor_state.top
1640 App.screen.check(y, 'ghi ', 'screen:1')
1641 y = y + Editor_state.line_height
1642 App.screen.check(y, 'jkl', 'screen:2')
1643 y = y + Editor_state.line_height
1644 App.screen.check(y, 'mno', 'screen:3')
1645 check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')
1646 check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
1647 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1648 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1651 function test_end_scrolls_down_in_wrapped_line()
1652 -- display the first three lines with the cursor on the bottom line
1653 App.screen.init{width=Editor_state.left+30, height=60}
1654 Editor_state = edit.initialize_test_state()
1655 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1656 Text.redraw_all(Editor_state)
1657 Editor_state.screen_top1 = {line=1, pos=1}
1658 Editor_state.screen_bottom1 = {}
1659 -- cursor is at bottom right of screen
1660 Editor_state.cursor1 = {line=3, pos=5}
1661 edit.draw(Editor_state)
1662 local y = Editor_state.top
1663 App.screen.check(y, 'abc', 'baseline/screen:1')
1664 y = y + Editor_state.line_height
1665 App.screen.check(y, 'def', 'baseline/screen:2')
1666 y = y + Editor_state.line_height
1667 App.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1668 -- after hitting end the screen scrolls down by one line
1669 edit.run_after_keychord(Editor_state, 'end')
1670 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1671 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1672 check_eq(Editor_state.cursor1.pos, 8, 'cursor:pos')
1673 y = Editor_state.top
1674 App.screen.check(y, 'def', 'screen:1')
1675 y = y + Editor_state.line_height
1676 App.screen.check(y, 'ghi ', 'screen:2')
1677 y = y + Editor_state.line_height
1678 App.screen.check(y, 'jkl', 'screen:3')
1681 function test_position_cursor_on_recently_edited_wrapping_line()
1682 -- draw a line wrapping over 2 screen lines
1683 App.screen.init{width=100, height=200}
1684 Editor_state = edit.initialize_test_state()
1685 Editor_state.lines = load_array{'abc def ghi jkl mno pqr ', 'xyz'}
1686 Text.redraw_all(Editor_state)
1687 Editor_state.cursor1 = {line=1, pos=25}
1688 Editor_state.screen_top1 = {line=1, pos=1}
1689 Editor_state.screen_bottom1 = {}
1690 edit.draw(Editor_state)
1691 local y = Editor_state.top
1692 App.screen.check(y, 'abc def ghi ', 'baseline1/screen:1')
1693 y = y + Editor_state.line_height
1694 App.screen.check(y, 'jkl mno pqr ', 'baseline1/screen:2')
1695 y = y + Editor_state.line_height
1696 App.screen.check(y, 'xyz', 'baseline1/screen:3')
1697 -- add to the line until it's wrapping over 3 screen lines
1698 edit.run_after_text_input(Editor_state, 's')
1699 edit.run_after_text_input(Editor_state, 't')
1700 edit.run_after_text_input(Editor_state, 'u')
1701 check_eq(Editor_state.cursor1.pos, 28, 'cursor:pos')
1702 y = Editor_state.top
1703 App.screen.check(y, 'abc def ghi ', 'baseline2/screen:1')
1704 y = y + Editor_state.line_height
1705 App.screen.check(y, 'jkl mno pqr ', 'baseline2/screen:2')
1706 y = y + Editor_state.line_height
1707 App.screen.check(y, 'stu', 'baseline2/screen:3')
1708 -- try to move the cursor earlier in the third screen line by clicking the mouse
1709 edit.run_after_mouse_release(Editor_state, Editor_state.left+2,Editor_state.top+Editor_state.line_height*2+5, 1)
1710 -- cursor should move
1711 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1712 check_eq(Editor_state.cursor1.pos, 25, 'cursor:pos')
1715 function test_backspace_can_scroll_up()
1716 -- display the lines 2/3/4 with the cursor on line 2
1717 App.screen.init{width=120, height=60}
1718 Editor_state = edit.initialize_test_state()
1719 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1720 Text.redraw_all(Editor_state)
1721 Editor_state.cursor1 = {line=2, pos=1}
1722 Editor_state.screen_top1 = {line=2, pos=1}
1723 Editor_state.screen_bottom1 = {}
1724 edit.draw(Editor_state)
1725 local y = Editor_state.top
1726 App.screen.check(y, 'def', 'baseline/screen:1')
1727 y = y + Editor_state.line_height
1728 App.screen.check(y, 'ghi', 'baseline/screen:2')
1729 y = y + Editor_state.line_height
1730 App.screen.check(y, 'jkl', 'baseline/screen:3')
1731 -- after hitting backspace the screen scrolls up by one line
1732 edit.run_after_keychord(Editor_state, 'backspace')
1733 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1734 check_eq(Editor_state.cursor1.line, 1, 'cursor')
1735 y = Editor_state.top
1736 App.screen.check(y, 'abcdef', 'screen:1')
1737 y = y + Editor_state.line_height
1738 App.screen.check(y, 'ghi', 'screen:2')
1739 y = y + Editor_state.line_height
1740 App.screen.check(y, 'jkl', 'screen:3')
1743 function test_backspace_can_scroll_up_screen_line()
1744 -- display lines starting from second screen line of a line
1745 App.screen.init{width=Editor_state.left+30, height=60}
1746 Editor_state = edit.initialize_test_state()
1747 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1748 Text.redraw_all(Editor_state)
1749 Editor_state.cursor1 = {line=3, pos=5}
1750 Editor_state.screen_top1 = {line=3, pos=5}
1751 Editor_state.screen_bottom1 = {}
1752 edit.draw(Editor_state)
1753 local y = Editor_state.top
1754 App.screen.check(y, 'jkl', 'baseline/screen:1')
1755 y = y + Editor_state.line_height
1756 App.screen.check(y, 'mno', 'baseline/screen:2')
1757 -- after hitting backspace the screen scrolls up by one screen line
1758 edit.run_after_keychord(Editor_state, 'backspace')
1759 y = Editor_state.top
1760 App.screen.check(y, 'ghij', 'screen:1')
1761 y = y + Editor_state.line_height
1762 App.screen.check(y, 'kl', 'screen:2')
1763 y = y + Editor_state.line_height
1764 App.screen.check(y, 'mno', 'screen:3')
1765 check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')
1766 check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
1767 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1768 check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
1771 function test_backspace_past_line_boundary()
1772 -- position cursor at start of a (non-first) line
1773 App.screen.init{width=Editor_state.left+30, height=60}
1774 Editor_state = edit.initialize_test_state()
1775 Editor_state.lines = load_array{'abc', 'def'}
1776 Text.redraw_all(Editor_state)
1777 Editor_state.cursor1 = {line=2, pos=1}
1778 -- backspace joins with previous line
1779 edit.run_after_keychord(Editor_state, 'backspace')
1780 check_eq(Editor_state.lines[1].data, 'abcdef', 'check')
1783 -- some tests for operating over selections created using Shift- chords
1784 -- we're just testing delete_selection, and it works the same for all keys
1786 function test_backspace_over_selection()
1787 -- select just one character within a line with cursor before selection
1788 App.screen.init{width=Editor_state.left+30, height=60}
1789 Editor_state = edit.initialize_test_state()
1790 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
1791 Text.redraw_all(Editor_state)
1792 Editor_state.cursor1 = {line=1, pos=1}
1793 Editor_state.selection1 = {line=1, pos=2}
1794 -- backspace deletes the selected character, even though it's after the cursor
1795 edit.run_after_keychord(Editor_state, 'backspace')
1796 check_eq(Editor_state.lines[1].data, 'bc', 'data')
1797 -- cursor (remains) at start of selection
1798 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1799 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1800 -- selection is cleared
1801 check_nil(Editor_state.selection1.line, 'selection')
1804 function test_backspace_over_selection_reverse()
1805 -- select just one character within a line with cursor after selection
1806 App.screen.init{width=Editor_state.left+30, height=60}
1807 Editor_state = edit.initialize_test_state()
1808 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
1809 Text.redraw_all(Editor_state)
1810 Editor_state.cursor1 = {line=1, pos=2}
1811 Editor_state.selection1 = {line=1, pos=1}
1812 -- backspace deletes the selected character
1813 edit.run_after_keychord(Editor_state, 'backspace')
1814 check_eq(Editor_state.lines[1].data, 'bc', 'data')
1815 -- cursor moves to start of selection
1816 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1817 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1818 -- selection is cleared
1819 check_nil(Editor_state.selection1.line, 'selection')
1822 function test_backspace_over_multiple_lines()
1823 -- select just one character within a line with cursor after selection
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=4, pos=2}
1830 -- backspace deletes the region and joins the remaining portions of lines on either side
1831 edit.run_after_keychord(Editor_state, 'backspace')
1832 check_eq(Editor_state.lines[1].data, 'akl', 'data:1')
1833 check_eq(Editor_state.lines[2].data, 'mno', '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_end_of_line()
1842 -- select region from cursor to end 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=1, pos=2}
1848 Editor_state.selection1 = {line=1, pos=4}
1849 -- backspace deletes rest of line without joining to any other line
1850 edit.run_after_keychord(Editor_state, 'backspace')
1851 check_eq(Editor_state.lines[1].data, 'a', 'data:1')
1852 check_eq(Editor_state.lines[2].data, 'def', 'data:2')
1853 -- cursor remains at start of selection
1854 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1855 check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
1856 -- selection is cleared
1857 check_nil(Editor_state.selection1.line, 'selection')
1860 function test_backspace_to_start_of_line()
1861 -- select region from cursor to start of line
1862 App.screen.init{width=Editor_state.left+30, height=60}
1863 Editor_state = edit.initialize_test_state()
1864 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
1865 Text.redraw_all(Editor_state)
1866 Editor_state.cursor1 = {line=2, pos=1}
1867 Editor_state.selection1 = {line=2, pos=3}
1868 -- backspace deletes beginning of line without joining to any other line
1869 edit.run_after_keychord(Editor_state, 'backspace')
1870 check_eq(Editor_state.lines[1].data, 'abc', 'data:1')
1871 check_eq(Editor_state.lines[2].data, 'f', 'data:2')
1872 -- cursor remains at start of selection
1873 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
1874 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1875 -- selection is cleared
1876 check_nil(Editor_state.selection1.line, 'selection')
1879 function test_undo_insert_text()
1880 App.screen.init{width=120, height=60}
1881 Editor_state = edit.initialize_test_state()
1882 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
1883 Text.redraw_all(Editor_state)
1884 Editor_state.cursor1 = {line=2, pos=4}
1885 Editor_state.screen_top1 = {line=1, pos=1}
1886 Editor_state.screen_bottom1 = {}
1887 -- insert a character
1888 edit.draw(Editor_state)
1889 edit.run_after_text_input(Editor_state, 'g')
1890 check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')
1891 check_eq(Editor_state.cursor1.pos, 5, 'baseline/cursor:pos')
1892 check_nil(Editor_state.selection1.line, 'baseline/selection:line')
1893 check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')
1894 local y = Editor_state.top
1895 App.screen.check(y, 'abc', 'baseline/screen:1')
1896 y = y + Editor_state.line_height
1897 App.screen.check(y, 'defg', 'baseline/screen:2')
1898 y = y + Editor_state.line_height
1899 App.screen.check(y, 'xyz', 'baseline/screen:3')
1900 -- undo
1901 edit.run_after_keychord(Editor_state, 'C-z')
1902 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
1903 check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
1904 check_nil(Editor_state.selection1.line, 'selection:line')
1905 check_nil(Editor_state.selection1.pos, 'selection:pos')
1906 y = Editor_state.top
1907 App.screen.check(y, 'abc', 'screen:1')
1908 y = y + Editor_state.line_height
1909 App.screen.check(y, 'def', 'screen:2')
1910 y = y + Editor_state.line_height
1911 App.screen.check(y, 'xyz', 'screen:3')
1914 function test_undo_delete_text()
1915 App.screen.init{width=120, height=60}
1916 Editor_state = edit.initialize_test_state()
1917 Editor_state.lines = load_array{'abc', 'defg', 'xyz'}
1918 Text.redraw_all(Editor_state)
1919 Editor_state.cursor1 = {line=2, pos=5}
1920 Editor_state.screen_top1 = {line=1, pos=1}
1921 Editor_state.screen_bottom1 = {}
1922 -- delete a character
1923 edit.run_after_keychord(Editor_state, 'backspace')
1924 check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')
1925 check_eq(Editor_state.cursor1.pos, 4, 'baseline/cursor:pos')
1926 check_nil(Editor_state.selection1.line, 'baseline/selection:line')
1927 check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')
1928 local y = Editor_state.top
1929 App.screen.check(y, 'abc', 'baseline/screen:1')
1930 y = y + Editor_state.line_height
1931 App.screen.check(y, 'def', 'baseline/screen:2')
1932 y = y + Editor_state.line_height
1933 App.screen.check(y, 'xyz', 'baseline/screen:3')
1934 -- undo
1935 --? -- after undo, the backspaced key is selected
1936 edit.run_after_keychord(Editor_state, 'C-z')
1937 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
1938 check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
1939 check_nil(Editor_state.selection1.line, 'selection:line')
1940 check_nil(Editor_state.selection1.pos, 'selection:pos')
1941 --? check_eq(Editor_state.selection1.line, 2, 'selection:line')
1942 --? check_eq(Editor_state.selection1.pos, 4, 'selection:pos')
1943 y = Editor_state.top
1944 App.screen.check(y, 'abc', 'screen:1')
1945 y = y + Editor_state.line_height
1946 App.screen.check(y, 'defg', 'screen:2')
1947 y = y + Editor_state.line_height
1948 App.screen.check(y, 'xyz', 'screen:3')
1951 function test_undo_restores_selection()
1952 -- display a line of text with some part selected
1953 App.screen.init{width=75, height=80}
1954 Editor_state = edit.initialize_test_state()
1955 Editor_state.lines = load_array{'abc'}
1956 Text.redraw_all(Editor_state)
1957 Editor_state.cursor1 = {line=1, pos=1}
1958 Editor_state.selection1 = {line=1, pos=2}
1959 Editor_state.screen_top1 = {line=1, pos=1}
1960 Editor_state.screen_bottom1 = {}
1961 edit.draw(Editor_state)
1962 -- delete selected text
1963 edit.run_after_text_input(Editor_state, 'x')
1964 check_eq(Editor_state.lines[1].data, 'xbc', 'baseline')
1965 check_nil(Editor_state.selection1.line, 'baseline:selection')
1966 -- undo
1967 edit.run_after_keychord(Editor_state, 'C-z')
1968 edit.run_after_keychord(Editor_state, 'C-z')
1969 -- selection is restored
1970 check_eq(Editor_state.selection1.line, 1, 'line')
1971 check_eq(Editor_state.selection1.pos, 2, 'pos')
1974 function test_search()
1975 App.screen.init{width=120, height=60}
1976 Editor_state = edit.initialize_test_state()
1977 Editor_state.lines = load_array{'abc', 'def', 'ghi', '’deg'} -- contains unicode quote in final line
1978 Text.redraw_all(Editor_state)
1979 Editor_state.cursor1 = {line=1, pos=1}
1980 Editor_state.screen_top1 = {line=1, pos=1}
1981 Editor_state.screen_bottom1 = {}
1982 edit.draw(Editor_state)
1983 -- search for a string
1984 edit.run_after_keychord(Editor_state, 'C-f')
1985 edit.run_after_text_input(Editor_state, 'd')
1986 edit.run_after_keychord(Editor_state, 'return')
1987 check_eq(Editor_state.cursor1.line, 2, '1/cursor:line')
1988 check_eq(Editor_state.cursor1.pos, 1, '1/cursor:pos')
1989 -- reset cursor
1990 Editor_state.cursor1 = {line=1, pos=1}
1991 Editor_state.screen_top1 = {line=1, pos=1}
1992 -- search for second occurrence
1993 edit.run_after_keychord(Editor_state, 'C-f')
1994 edit.run_after_text_input(Editor_state, 'de')
1995 edit.run_after_keychord(Editor_state, 'down')
1996 edit.run_after_keychord(Editor_state, 'return')
1997 check_eq(Editor_state.cursor1.line, 4, '2/cursor:line')
1998 check_eq(Editor_state.cursor1.pos, 2, '2/cursor:pos')
2001 function test_search_upwards()
2002 App.screen.init{width=120, height=60}
2003 Editor_state = edit.initialize_test_state()
2004 Editor_state.lines = load_array{'’abc', 'abd'} -- contains unicode quote
2005 Text.redraw_all(Editor_state)
2006 Editor_state.cursor1 = {line=2, pos=1}
2007 Editor_state.screen_top1 = {line=1, pos=1}
2008 Editor_state.screen_bottom1 = {}
2009 edit.draw(Editor_state)
2010 -- search for a string
2011 edit.run_after_keychord(Editor_state, 'C-f')
2012 edit.run_after_text_input(Editor_state, 'a')
2013 -- search for previous occurrence
2014 edit.run_after_keychord(Editor_state, 'up')
2015 check_eq(Editor_state.cursor1.line, 1, '2/cursor:line')
2016 check_eq(Editor_state.cursor1.pos, 2, '2/cursor:pos')
2019 function test_search_wrap()
2020 App.screen.init{width=120, height=60}
2021 Editor_state = edit.initialize_test_state()
2022 Editor_state.lines = load_array{'’abc', 'def'} -- contains unicode quote in first line
2023 Text.redraw_all(Editor_state)
2024 Editor_state.cursor1 = {line=2, pos=1}
2025 Editor_state.screen_top1 = {line=1, pos=1}
2026 Editor_state.screen_bottom1 = {}
2027 edit.draw(Editor_state)
2028 -- search for a string
2029 edit.run_after_keychord(Editor_state, 'C-f')
2030 edit.run_after_text_input(Editor_state, 'a')
2031 edit.run_after_keychord(Editor_state, 'return')
2032 -- cursor wraps
2033 check_eq(Editor_state.cursor1.line, 1, '1/cursor:line')
2034 check_eq(Editor_state.cursor1.pos, 2, '1/cursor:pos')
2037 function test_search_wrap_upwards()
2038 App.screen.init{width=120, height=60}
2039 Editor_state = edit.initialize_test_state()
2040 Editor_state.lines = load_array{'abc ’abd'} -- contains unicode quote
2041 Text.redraw_all(Editor_state)
2042 Editor_state.cursor1 = {line=1, pos=1}
2043 Editor_state.screen_top1 = {line=1, pos=1}
2044 Editor_state.screen_bottom1 = {}
2045 edit.draw(Editor_state)
2046 -- search upwards for a string
2047 edit.run_after_keychord(Editor_state, 'C-f')
2048 edit.run_after_text_input(Editor_state, 'a')
2049 edit.run_after_keychord(Editor_state, 'up')
2050 -- cursor wraps
2051 check_eq(Editor_state.cursor1.line, 1, '1/cursor:line')
2052 check_eq(Editor_state.cursor1.pos, 6, '1/cursor:pos')