remove a condition
[view.love.git] / text_tests.lua
blob0384b0bc05671801eaaa00f8d9cb687706d91a37
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 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 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_on_wrapping_line()
445 -- display a wrapping line
446 App.screen.init{width=75, height=80}
447 Editor_state = edit.initialize_test_state()
448 -- 12345678901234
449 Editor_state.lines = load_array{"madam I'm adam"}
450 Text.redraw_all(Editor_state)
451 Editor_state.cursor1 = {line=1, pos=1}
452 Editor_state.screen_top1 = {line=1, pos=1}
453 Editor_state.screen_bottom1 = {}
454 edit.draw(Editor_state)
455 local y = Editor_state.top
456 App.screen.check(y, 'madam ', 'baseline/screen:1')
457 y = y + Editor_state.line_height
458 App.screen.check(y, "I'm ad", 'baseline/screen:2')
459 y = y + Editor_state.line_height
460 -- click past end of second screen line
461 edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
462 -- cursor moves to end of screen line
463 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
464 check_eq(Editor_state.cursor1.pos, 12, 'cursor:pos')
467 function test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen()
468 -- display a wrapping line from its second screen line
469 App.screen.init{width=75, height=80}
470 Editor_state = edit.initialize_test_state()
471 -- 12345678901234
472 Editor_state.lines = load_array{"madam I'm adam"}
473 Text.redraw_all(Editor_state)
474 Editor_state.cursor1 = {line=1, pos=8}
475 Editor_state.screen_top1 = {line=1, pos=7}
476 Editor_state.screen_bottom1 = {}
477 edit.draw(Editor_state)
478 local y = Editor_state.top
479 App.screen.check(y, "I'm ad", 'baseline/screen:2')
480 y = y + Editor_state.line_height
481 -- click past end of second screen line
482 edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
483 -- cursor moves to end of screen line
484 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
485 check_eq(Editor_state.cursor1.pos, 12, 'cursor:pos')
488 function test_click_past_end_of_wrapping_line()
489 -- display a wrapping line
490 App.screen.init{width=75, height=80}
491 Editor_state = edit.initialize_test_state()
492 -- 12345678901234
493 Editor_state.lines = load_array{"madam I'm adam"}
494 Text.redraw_all(Editor_state)
495 Editor_state.cursor1 = {line=1, pos=1}
496 Editor_state.screen_top1 = {line=1, pos=1}
497 Editor_state.screen_bottom1 = {}
498 edit.draw(Editor_state)
499 local y = Editor_state.top
500 App.screen.check(y, 'madam ', 'baseline/screen:1')
501 y = y + Editor_state.line_height
502 App.screen.check(y, "I'm ad", 'baseline/screen:2')
503 y = y + Editor_state.line_height
504 App.screen.check(y, 'am', 'baseline/screen:3')
505 y = y + Editor_state.line_height
506 -- click past the end of it
507 edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
508 -- cursor moves to end of line
509 check_eq(Editor_state.cursor1.pos, 15, 'cursor') -- one more than the number of UTF-8 code-points
512 function test_click_past_end_of_wrapping_line_containing_non_ascii()
513 -- display a wrapping line containing non-ASCII
514 App.screen.init{width=75, height=80}
515 Editor_state = edit.initialize_test_state()
516 -- 12345678901234
517 Editor_state.lines = load_array{'madam I’m adam'} -- notice the non-ASCII apostrophe
518 Text.redraw_all(Editor_state)
519 Editor_state.cursor1 = {line=1, pos=1}
520 Editor_state.screen_top1 = {line=1, pos=1}
521 Editor_state.screen_bottom1 = {}
522 edit.draw(Editor_state)
523 local y = Editor_state.top
524 App.screen.check(y, 'madam ', 'baseline/screen:1')
525 y = y + Editor_state.line_height
526 App.screen.check(y, 'I’m ad', 'baseline/screen:2')
527 y = y + Editor_state.line_height
528 App.screen.check(y, 'am', 'baseline/screen:3')
529 y = y + Editor_state.line_height
530 -- click past the end of it
531 edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
532 -- cursor moves to end of line
533 check_eq(Editor_state.cursor1.pos, 15, 'cursor') -- one more than the number of UTF-8 code-points
536 function test_click_past_end_of_word_wrapping_line()
537 -- display a long line wrapping at a word boundary on a screen of more realistic length
538 App.screen.init{width=160, height=80}
539 Editor_state = edit.initialize_test_state()
540 -- 0 1 2
541 -- 123456789012345678901
542 Editor_state.lines = load_array{'the quick brown fox jumped over the lazy dog'}
543 Text.redraw_all(Editor_state)
544 Editor_state.cursor1 = {line=1, pos=1}
545 Editor_state.screen_top1 = {line=1, pos=1}
546 Editor_state.screen_bottom1 = {}
547 edit.draw(Editor_state)
548 local y = Editor_state.top
549 App.screen.check(y, 'the quick brown fox ', 'baseline/screen:1')
550 y = y + Editor_state.line_height
551 -- click past the end of the screen line
552 edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
553 -- cursor moves to end of screen line
554 check_eq(Editor_state.cursor1.pos, 20, 'cursor')
557 function test_select_text()
558 -- display a line of text
559 App.screen.init{width=75, height=80}
560 Editor_state = edit.initialize_test_state()
561 Editor_state.lines = load_array{'abc def'}
562 Text.redraw_all(Editor_state)
563 Editor_state.cursor1 = {line=1, pos=1}
564 Editor_state.screen_top1 = {line=1, pos=1}
565 Editor_state.screen_bottom1 = {}
566 edit.draw(Editor_state)
567 -- select a letter
568 App.fake_key_press('lshift')
569 edit.run_after_keychord(Editor_state, 'S-right')
570 App.fake_key_release('lshift')
571 edit.key_release(Editor_state, 'lshift')
572 -- selection persists even after shift is released
573 check_eq(Editor_state.selection1.line, 1, 'selection:line')
574 check_eq(Editor_state.selection1.pos, 1, 'selection:pos')
575 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
576 check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
579 function test_cursor_movement_without_shift_resets_selection()
580 -- display a line of text with some part selected
581 App.screen.init{width=75, height=80}
582 Editor_state = edit.initialize_test_state()
583 Editor_state.lines = load_array{'abc'}
584 Text.redraw_all(Editor_state)
585 Editor_state.cursor1 = {line=1, pos=1}
586 Editor_state.selection1 = {line=1, pos=2}
587 Editor_state.screen_top1 = {line=1, pos=1}
588 Editor_state.screen_bottom1 = {}
589 edit.draw(Editor_state)
590 -- press an arrow key without shift
591 edit.run_after_keychord(Editor_state, 'right')
592 -- no change to data, selection is reset
593 check_nil(Editor_state.selection1.line, 'check')
594 check_eq(Editor_state.lines[1].data, 'abc', 'data')
597 function test_edit_deletes_selection()
598 -- display a line of text with some part selected
599 App.screen.init{width=75, height=80}
600 Editor_state = edit.initialize_test_state()
601 Editor_state.lines = load_array{'abc'}
602 Text.redraw_all(Editor_state)
603 Editor_state.cursor1 = {line=1, pos=1}
604 Editor_state.selection1 = {line=1, pos=2}
605 Editor_state.screen_top1 = {line=1, pos=1}
606 Editor_state.screen_bottom1 = {}
607 edit.draw(Editor_state)
608 -- press a key
609 edit.run_after_text_input(Editor_state, 'x')
610 -- selected text is deleted and replaced with the key
611 check_eq(Editor_state.lines[1].data, 'xbc', 'check')
614 function test_edit_with_shift_key_deletes_selection()
615 -- display a line of text with some part selected
616 App.screen.init{width=75, height=80}
617 Editor_state = edit.initialize_test_state()
618 Editor_state.lines = load_array{'abc'}
619 Text.redraw_all(Editor_state)
620 Editor_state.cursor1 = {line=1, pos=1}
621 Editor_state.selection1 = {line=1, pos=2}
622 Editor_state.screen_top1 = {line=1, pos=1}
623 Editor_state.screen_bottom1 = {}
624 edit.draw(Editor_state)
625 -- mimic precise keypresses for a capital letter
626 App.fake_key_press('lshift')
627 edit.keychord_press(Editor_state, 'd', 'd')
628 edit.text_input(Editor_state, 'D')
629 edit.key_release(Editor_state, 'd')
630 App.fake_key_release('lshift')
631 -- selected text is deleted and replaced with the key
632 check_nil(Editor_state.selection1.line, 'check')
633 check_eq(Editor_state.lines[1].data, 'Dbc', 'data')
636 function test_copy_does_not_reset_selection()
637 -- display a line of text with a selection
638 App.screen.init{width=75, height=80}
639 Editor_state = edit.initialize_test_state()
640 Editor_state.lines = load_array{'abc'}
641 Text.redraw_all(Editor_state)
642 Editor_state.cursor1 = {line=1, pos=1}
643 Editor_state.selection1 = {line=1, pos=2}
644 Editor_state.screen_top1 = {line=1, pos=1}
645 Editor_state.screen_bottom1 = {}
646 edit.draw(Editor_state)
647 -- copy selection
648 edit.run_after_keychord(Editor_state, 'C-c')
649 check_eq(App.clipboard, 'a', 'clipboard')
650 -- selection is reset since shift key is not pressed
651 check(Editor_state.selection1.line, 'check')
654 function test_cut()
655 -- display a line of text with some part selected
656 App.screen.init{width=75, height=80}
657 Editor_state = edit.initialize_test_state()
658 Editor_state.lines = load_array{'abc'}
659 Text.redraw_all(Editor_state)
660 Editor_state.cursor1 = {line=1, pos=1}
661 Editor_state.selection1 = {line=1, pos=2}
662 Editor_state.screen_top1 = {line=1, pos=1}
663 Editor_state.screen_bottom1 = {}
664 edit.draw(Editor_state)
665 -- press a key
666 edit.run_after_keychord(Editor_state, 'C-x')
667 check_eq(App.clipboard, 'a', 'clipboard')
668 -- selected text is deleted
669 check_eq(Editor_state.lines[1].data, 'bc', 'data')
672 function test_paste_replaces_selection()
673 -- display a line of text with a selection
674 App.screen.init{width=75, height=80}
675 Editor_state = edit.initialize_test_state()
676 Editor_state.lines = load_array{'abc', 'def'}
677 Text.redraw_all(Editor_state)
678 Editor_state.cursor1 = {line=2, pos=1}
679 Editor_state.selection1 = {line=1, pos=1}
680 Editor_state.screen_top1 = {line=1, pos=1}
681 Editor_state.screen_bottom1 = {}
682 edit.draw(Editor_state)
683 -- set clipboard
684 App.clipboard = 'xyz'
685 -- paste selection
686 edit.run_after_keychord(Editor_state, 'C-v')
687 -- selection is reset since shift key is not pressed
688 -- selection includes the newline, so it's also deleted
689 check_eq(Editor_state.lines[1].data, 'xyzdef', 'check')
692 function test_deleting_selection_may_scroll()
693 -- display lines 2/3/4
694 App.screen.init{width=120, height=60}
695 Editor_state = edit.initialize_test_state()
696 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
697 Text.redraw_all(Editor_state)
698 Editor_state.cursor1 = {line=3, pos=2}
699 Editor_state.screen_top1 = {line=2, pos=1}
700 Editor_state.screen_bottom1 = {}
701 edit.draw(Editor_state)
702 local y = Editor_state.top
703 App.screen.check(y, 'def', 'baseline/screen:1')
704 y = y + Editor_state.line_height
705 App.screen.check(y, 'ghi', 'baseline/screen:2')
706 y = y + Editor_state.line_height
707 App.screen.check(y, 'jkl', 'baseline/screen:3')
708 -- set up a selection starting above the currently displayed page
709 Editor_state.selection1 = {line=1, pos=2}
710 -- delete selection
711 edit.run_after_keychord(Editor_state, 'backspace')
712 -- page scrolls up
713 check_eq(Editor_state.screen_top1.line, 1, 'check')
714 check_eq(Editor_state.lines[1].data, 'ahi', 'data')
717 function test_edit_wrapping_text()
718 App.screen.init{width=50, height=60}
719 Editor_state = edit.initialize_test_state()
720 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
721 Text.redraw_all(Editor_state)
722 Editor_state.cursor1 = {line=2, pos=4}
723 Editor_state.screen_top1 = {line=1, pos=1}
724 Editor_state.screen_bottom1 = {}
725 edit.draw(Editor_state)
726 edit.run_after_text_input(Editor_state, 'g')
727 local y = Editor_state.top
728 App.screen.check(y, 'abc', 'screen:1')
729 y = y + Editor_state.line_height
730 App.screen.check(y, 'de', 'screen:2')
731 y = y + Editor_state.line_height
732 App.screen.check(y, 'fg', 'screen:3')
735 function test_insert_newline()
736 -- display a few lines
737 App.screen.init{width=Editor_state.left+30, height=60}
738 Editor_state = edit.initialize_test_state()
739 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
740 Text.redraw_all(Editor_state)
741 Editor_state.cursor1 = {line=1, pos=2}
742 Editor_state.screen_top1 = {line=1, pos=1}
743 Editor_state.screen_bottom1 = {}
744 edit.draw(Editor_state)
745 local y = Editor_state.top
746 App.screen.check(y, 'abc', 'baseline/screen:1')
747 y = y + Editor_state.line_height
748 App.screen.check(y, 'def', 'baseline/screen:2')
749 y = y + Editor_state.line_height
750 App.screen.check(y, 'ghi', 'baseline/screen:3')
751 -- hitting the enter key splits the line
752 edit.run_after_keychord(Editor_state, 'return')
753 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
754 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
755 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
756 y = Editor_state.top
757 App.screen.check(y, 'a', 'screen:1')
758 y = y + Editor_state.line_height
759 App.screen.check(y, 'bc', 'screen:2')
760 y = y + Editor_state.line_height
761 App.screen.check(y, 'def', 'screen:3')
764 function test_insert_newline_at_start_of_line()
765 -- display a line
766 App.screen.init{width=Editor_state.left+30, height=60}
767 Editor_state = edit.initialize_test_state()
768 Editor_state.lines = load_array{'abc'}
769 Text.redraw_all(Editor_state)
770 Editor_state.cursor1 = {line=1, pos=1}
771 Editor_state.screen_top1 = {line=1, pos=1}
772 Editor_state.screen_bottom1 = {}
773 -- hitting the enter key splits the line
774 edit.run_after_keychord(Editor_state, 'return')
775 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
776 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
777 check_eq(Editor_state.lines[1].data, '', 'data:1')
778 check_eq(Editor_state.lines[2].data, 'abc', 'data:2')
781 function test_insert_from_clipboard()
782 -- display a few lines
783 App.screen.init{width=Editor_state.left+30, height=60}
784 Editor_state = edit.initialize_test_state()
785 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
786 Text.redraw_all(Editor_state)
787 Editor_state.cursor1 = {line=1, pos=2}
788 Editor_state.screen_top1 = {line=1, pos=1}
789 Editor_state.screen_bottom1 = {}
790 edit.draw(Editor_state)
791 local y = Editor_state.top
792 App.screen.check(y, 'abc', 'baseline/screen:1')
793 y = y + Editor_state.line_height
794 App.screen.check(y, 'def', 'baseline/screen:2')
795 y = y + Editor_state.line_height
796 App.screen.check(y, 'ghi', 'baseline/screen:3')
797 -- paste some text including a newline, check that new line is created
798 App.clipboard = 'xy\nz'
799 edit.run_after_keychord(Editor_state, 'C-v')
800 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
801 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
802 check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
803 y = Editor_state.top
804 App.screen.check(y, 'axy', 'screen:1')
805 y = y + Editor_state.line_height
806 App.screen.check(y, 'zbc', 'screen:2')
807 y = y + Editor_state.line_height
808 App.screen.check(y, 'def', 'screen:3')
811 function test_select_text_using_mouse()
812 App.screen.init{width=50, height=60}
813 Editor_state = edit.initialize_test_state()
814 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
815 Text.redraw_all(Editor_state)
816 Editor_state.cursor1 = {line=1, pos=1}
817 Editor_state.screen_top1 = {line=1, pos=1}
818 Editor_state.screen_bottom1 = {}
819 Editor_state.selection1 = {}
820 edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
821 -- press and hold on first location
822 edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
823 -- drag and release somewhere else
824 edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
825 check_eq(Editor_state.selection1.line, 1, 'selection:line')
826 check_eq(Editor_state.selection1.pos, 2, 'selection:pos')
827 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
828 check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
831 function test_select_text_using_mouse_and_shift()
832 App.screen.init{width=50, height=60}
833 Editor_state = edit.initialize_test_state()
834 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
835 Text.redraw_all(Editor_state)
836 Editor_state.cursor1 = {line=1, pos=1}
837 Editor_state.screen_top1 = {line=1, pos=1}
838 Editor_state.screen_bottom1 = {}
839 Editor_state.selection1 = {}
840 edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
841 -- click on first location
842 edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
843 edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
844 -- hold down shift and click somewhere else
845 App.fake_key_press('lshift')
846 edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
847 edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
848 App.fake_key_release('lshift')
849 check_eq(Editor_state.selection1.line, 1, 'selection:line')
850 check_eq(Editor_state.selection1.pos, 2, 'selection:pos')
851 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
852 check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
855 function test_select_text_repeatedly_using_mouse_and_shift()
856 App.screen.init{width=50, height=60}
857 Editor_state = edit.initialize_test_state()
858 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
859 Text.redraw_all(Editor_state)
860 Text.redraw_all(Editor_state)
861 Editor_state.cursor1 = {line=1, pos=1}
862 Editor_state.screen_top1 = {line=1, pos=1}
863 Editor_state.screen_bottom1 = {}
864 Editor_state.selection1 = {}
865 edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
866 -- click on first location
867 edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
868 edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
869 -- hold down shift and click on a second location
870 App.fake_key_press('lshift')
871 edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
872 edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
873 -- hold down shift and click at a third location
874 App.fake_key_press('lshift')
875 edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
876 edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+Editor_state.line_height+5, 1)
877 App.fake_key_release('lshift')
878 -- selection is between first and third location. forget the second location, not the first.
879 check_eq(Editor_state.selection1.line, 1, 'selection:line')
880 check_eq(Editor_state.selection1.pos, 2, 'selection:pos')
881 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
882 check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
885 function test_cut_without_selection()
886 -- display a few lines
887 App.screen.init{width=Editor_state.left+30, height=60}
888 Editor_state = edit.initialize_test_state()
889 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
890 Text.redraw_all(Editor_state)
891 Editor_state.cursor1 = {line=1, pos=2}
892 Editor_state.screen_top1 = {line=1, pos=1}
893 Editor_state.screen_bottom1 = {}
894 Editor_state.selection1 = {}
895 edit.draw(Editor_state)
896 -- try to cut without selecting text
897 edit.run_after_keychord(Editor_state, 'C-x')
898 -- no crash
899 check_nil(Editor_state.selection1.line, 'check')
902 function test_pagedown()
903 App.screen.init{width=120, height=45}
904 Editor_state = edit.initialize_test_state()
905 Editor_state.lines = load_array{'abc', 'def', 'ghi'}
906 Text.redraw_all(Editor_state)
907 Editor_state.cursor1 = {line=1, pos=1}
908 Editor_state.screen_top1 = {line=1, pos=1}
909 Editor_state.screen_bottom1 = {}
910 -- initially the first two lines are displayed
911 edit.draw(Editor_state)
912 local y = Editor_state.top
913 App.screen.check(y, 'abc', 'baseline/screen:1')
914 y = y + Editor_state.line_height
915 App.screen.check(y, 'def', 'baseline/screen:2')
916 -- after pagedown the bottom line becomes the top
917 edit.run_after_keychord(Editor_state, 'pagedown')
918 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
919 check_eq(Editor_state.cursor1.line, 2, 'cursor')
920 y = Editor_state.top
921 App.screen.check(y, 'def', 'screen:1')
922 y = y + Editor_state.line_height
923 App.screen.check(y, 'ghi', 'screen:2')
926 function test_pagedown_skips_drawings()
927 -- some lines of text with a drawing intermixed
928 local drawing_width = 50
929 App.screen.init{width=Editor_state.left+drawing_width, height=80}
930 Editor_state = edit.initialize_test_state()
931 Editor_state.lines = load_array{'abc', -- height 15
932 '```lines', '```', -- height 25
933 'def', -- height 15
934 'ghi'} -- height 15
935 Text.redraw_all(Editor_state)
936 check_eq(Editor_state.lines[2].mode, 'drawing', 'baseline/lines')
937 Editor_state.cursor1 = {line=1, pos=1}
938 Editor_state.screen_top1 = {line=1, pos=1}
939 Editor_state.screen_bottom1 = {}
940 local drawing_height = Drawing_padding_height + drawing_width/2 -- default
941 -- initially the screen displays the first line and the drawing
942 -- 15px margin + 15px line1 + 10px margin + 25px drawing + 10px margin = 75px < screen height 80px
943 edit.draw(Editor_state)
944 local y = Editor_state.top
945 App.screen.check(y, 'abc', 'baseline/screen:1')
946 -- after pagedown the screen draws the drawing up top
947 -- 15px margin + 10px margin + 25px drawing + 10px margin + 15px line3 = 75px < screen height 80px
948 edit.run_after_keychord(Editor_state, 'pagedown')
949 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
950 check_eq(Editor_state.cursor1.line, 3, 'cursor')
951 y = Editor_state.top + drawing_height
952 App.screen.check(y, 'def', 'screen:1')
955 function test_pagedown_often_shows_start_of_wrapping_line()
956 -- draw a few lines ending in part of a wrapping line
957 App.screen.init{width=50, height=60}
958 Editor_state = edit.initialize_test_state()
959 Editor_state.lines = load_array{'abc', 'def ghi jkl', 'mno'}
960 Text.redraw_all(Editor_state)
961 Editor_state.cursor1 = {line=1, pos=1}
962 Editor_state.screen_top1 = {line=1, pos=1}
963 Editor_state.screen_bottom1 = {}
964 edit.draw(Editor_state)
965 local y = Editor_state.top
966 App.screen.check(y, 'abc', 'baseline/screen:1')
967 y = y + Editor_state.line_height
968 App.screen.check(y, 'def ', 'baseline/screen:2')
969 y = y + Editor_state.line_height
970 App.screen.check(y, 'ghi ', 'baseline/screen:3')
971 -- after pagedown we start drawing from the bottom _line_ (multiple screen lines)
972 edit.run_after_keychord(Editor_state, 'pagedown')
973 check_eq(Editor_state.screen_top1.line, 2, 'screen_top:line')
974 check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
975 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
976 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
977 y = Editor_state.top
978 App.screen.check(y, 'def ', 'screen:1')
979 y = y + Editor_state.line_height
980 App.screen.check(y, 'ghi ', 'screen:2')
981 y = y + Editor_state.line_height
982 App.screen.check(y, 'jkl', 'screen:3')
985 function test_pagedown_can_start_from_middle_of_long_wrapping_line()
986 -- draw a few lines starting from a very long wrapping line
987 App.screen.init{width=Editor_state.left+30, height=60}
988 Editor_state = edit.initialize_test_state()
989 Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu vwx yza bcd efg hij', 'XYZ'}
990 Text.redraw_all(Editor_state)
991 Editor_state.cursor1 = {line=1, pos=2}
992 Editor_state.screen_top1 = {line=1, pos=1}
993 Editor_state.screen_bottom1 = {}
994 edit.draw(Editor_state)
995 local y = Editor_state.top
996 App.screen.check(y, 'abc ', 'baseline/screen:1')
997 y = y + Editor_state.line_height
998 App.screen.check(y, 'def ', 'baseline/screen:2')
999 y = y + Editor_state.line_height
1000 App.screen.check(y, 'ghi ', 'baseline/screen:3')
1001 -- after pagedown we scroll down the very long wrapping line
1002 edit.run_after_keychord(Editor_state, 'pagedown')
1003 check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')
1004 check_eq(Editor_state.screen_top1.pos, 9, 'screen_top:pos')
1005 y = Editor_state.top
1006 App.screen.check(y, 'ghi ', 'screen:1')
1007 y = y + Editor_state.line_height
1008 App.screen.check(y, 'jkl ', 'screen:2')
1009 y = y + Editor_state.line_height
1010 App.screen.check(y, 'mno ', 'screen:3')
1013 function test_pagedown_never_moves_up()
1014 -- draw the final screen line of a wrapping line
1015 App.screen.init{width=Editor_state.left+30, height=60}
1016 Editor_state = edit.initialize_test_state()
1017 Editor_state.lines = load_array{'abc def ghi'}
1018 Text.redraw_all(Editor_state)
1019 Editor_state.cursor1 = {line=1, pos=9}
1020 Editor_state.screen_top1 = {line=1, pos=9}
1021 Editor_state.screen_bottom1 = {}
1022 edit.draw(Editor_state)
1023 -- pagedown makes no change
1024 edit.run_after_keychord(Editor_state, 'pagedown')
1025 check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')
1026 check_eq(Editor_state.screen_top1.pos, 9, 'screen_top:pos')
1029 function test_down_arrow_moves_cursor()
1030 App.screen.init{width=120, height=60}
1031 Editor_state = edit.initialize_test_state()
1032 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1033 Text.redraw_all(Editor_state)
1034 Editor_state.cursor1 = {line=1, pos=1}
1035 Editor_state.screen_top1 = {line=1, pos=1}
1036 Editor_state.screen_bottom1 = {}
1037 -- initially the first three lines are displayed
1038 edit.draw(Editor_state)
1039 local y = Editor_state.top
1040 App.screen.check(y, 'abc', 'baseline/screen:1')
1041 y = y + Editor_state.line_height
1042 App.screen.check(y, 'def', 'baseline/screen:2')
1043 y = y + Editor_state.line_height
1044 App.screen.check(y, 'ghi', 'baseline/screen:3')
1045 -- after hitting the down arrow, the cursor moves down by 1 line
1046 edit.run_after_keychord(Editor_state, 'down')
1047 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1048 check_eq(Editor_state.cursor1.line, 2, 'cursor')
1049 -- the screen is unchanged
1050 y = Editor_state.top
1051 App.screen.check(y, 'abc', 'screen:1')
1052 y = y + Editor_state.line_height
1053 App.screen.check(y, 'def', 'screen:2')
1054 y = y + Editor_state.line_height
1055 App.screen.check(y, 'ghi', 'screen:3')
1058 function test_down_arrow_scrolls_down_by_one_line()
1059 -- display the first three lines with the cursor on the bottom line
1060 App.screen.init{width=120, height=60}
1061 Editor_state = edit.initialize_test_state()
1062 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1063 Text.redraw_all(Editor_state)
1064 Editor_state.cursor1 = {line=3, pos=1}
1065 Editor_state.screen_top1 = {line=1, pos=1}
1066 Editor_state.screen_bottom1 = {}
1067 edit.draw(Editor_state)
1068 local y = Editor_state.top
1069 App.screen.check(y, 'abc', 'baseline/screen:1')
1070 y = y + Editor_state.line_height
1071 App.screen.check(y, 'def', 'baseline/screen:2')
1072 y = y + Editor_state.line_height
1073 App.screen.check(y, 'ghi', 'baseline/screen:3')
1074 -- after hitting the down arrow the screen scrolls down by one line
1075 edit.run_after_keychord(Editor_state, 'down')
1076 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1077 check_eq(Editor_state.cursor1.line, 4, 'cursor')
1078 y = Editor_state.top
1079 App.screen.check(y, 'def', 'screen:1')
1080 y = y + Editor_state.line_height
1081 App.screen.check(y, 'ghi', 'screen:2')
1082 y = y + Editor_state.line_height
1083 App.screen.check(y, 'jkl', 'screen:3')
1086 function test_down_arrow_scrolls_down_by_one_screen_line()
1087 -- display the first three lines with the cursor on the bottom line
1088 App.screen.init{width=Editor_state.left+30, height=60}
1089 Editor_state = edit.initialize_test_state()
1090 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1091 Text.redraw_all(Editor_state)
1092 Editor_state.cursor1 = {line=3, pos=1}
1093 Editor_state.screen_top1 = {line=1, pos=1}
1094 Editor_state.screen_bottom1 = {}
1095 edit.draw(Editor_state)
1096 local y = Editor_state.top
1097 App.screen.check(y, 'abc', 'baseline/screen:1')
1098 y = y + Editor_state.line_height
1099 App.screen.check(y, 'def', 'baseline/screen:2')
1100 y = y + Editor_state.line_height
1101 App.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1102 -- after hitting the down arrow the screen scrolls down by one line
1103 edit.run_after_keychord(Editor_state, 'down')
1104 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1105 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1106 check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
1107 y = Editor_state.top
1108 App.screen.check(y, 'def', 'screen:1')
1109 y = y + Editor_state.line_height
1110 App.screen.check(y, 'ghi ', 'screen:2')
1111 y = y + Editor_state.line_height
1112 App.screen.check(y, 'jkl', 'screen:3')
1115 function test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word()
1116 -- display the first three lines with the cursor on the bottom line
1117 App.screen.init{width=Editor_state.left+30, height=60}
1118 Editor_state = edit.initialize_test_state()
1119 Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}
1120 Text.redraw_all(Editor_state)
1121 Editor_state.cursor1 = {line=3, pos=1}
1122 Editor_state.screen_top1 = {line=1, pos=1}
1123 Editor_state.screen_bottom1 = {}
1124 edit.draw(Editor_state)
1125 local y = Editor_state.top
1126 App.screen.check(y, 'abc', 'baseline/screen:1')
1127 y = y + Editor_state.line_height
1128 App.screen.check(y, 'def', 'baseline/screen:2')
1129 y = y + Editor_state.line_height
1130 App.screen.check(y, 'ghij', 'baseline/screen:3')
1131 -- after hitting the down arrow the screen scrolls down by one line
1132 edit.run_after_keychord(Editor_state, 'down')
1133 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1134 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1135 check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
1136 y = Editor_state.top
1137 App.screen.check(y, 'def', 'screen:1')
1138 y = y + Editor_state.line_height
1139 App.screen.check(y, 'ghij', 'screen:2')
1140 y = y + Editor_state.line_height
1141 App.screen.check(y, 'kl', 'screen:3')
1144 function test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up()
1145 App.screen.init{width=Editor_state.left+30, height=60}
1146 Editor_state = edit.initialize_test_state()
1147 Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}
1148 Text.redraw_all(Editor_state)
1149 Editor_state.cursor1 = {line=3, pos=1}
1150 Editor_state.screen_top1 = {line=1, pos=1}
1151 Editor_state.screen_bottom1 = {}
1152 edit.draw(Editor_state)
1153 local y = Editor_state.top
1154 App.screen.check(y, 'abc', 'baseline/screen:1')
1155 y = y + Editor_state.line_height
1156 App.screen.check(y, 'def', 'baseline/screen:2')
1157 y = y + Editor_state.line_height
1158 App.screen.check(y, 'ghij', 'baseline/screen:3')
1159 -- after hitting pagedown the screen scrolls down to start of a long line
1160 edit.run_after_keychord(Editor_state, 'pagedown')
1161 check_eq(Editor_state.screen_top1.line, 3, 'baseline2/screen_top')
1162 check_eq(Editor_state.cursor1.line, 3, 'baseline2/cursor:line')
1163 check_eq(Editor_state.cursor1.pos, 1, 'baseline2/cursor:pos')
1164 -- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll up
1165 edit.run_after_keychord(Editor_state, 'down')
1166 check_eq(Editor_state.screen_top1.line, 3, 'screen_top')
1167 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1168 check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
1169 y = Editor_state.top
1170 App.screen.check(y, 'ghij', 'screen:1')
1171 y = y + Editor_state.line_height
1172 App.screen.check(y, 'kl', 'screen:2')
1173 y = y + Editor_state.line_height
1174 App.screen.check(y, 'mno', 'screen:3')
1177 function test_up_arrow_moves_cursor()
1178 -- display the first 3 lines with the cursor on the bottom line
1179 App.screen.init{width=120, height=60}
1180 Editor_state = edit.initialize_test_state()
1181 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1182 Text.redraw_all(Editor_state)
1183 Editor_state.cursor1 = {line=3, pos=1}
1184 Editor_state.screen_top1 = {line=1, pos=1}
1185 Editor_state.screen_bottom1 = {}
1186 edit.draw(Editor_state)
1187 local y = Editor_state.top
1188 App.screen.check(y, 'abc', 'baseline/screen:1')
1189 y = y + Editor_state.line_height
1190 App.screen.check(y, 'def', 'baseline/screen:2')
1191 y = y + Editor_state.line_height
1192 App.screen.check(y, 'ghi', 'baseline/screen:3')
1193 -- after hitting the up arrow the cursor moves up by 1 line
1194 edit.run_after_keychord(Editor_state, 'up')
1195 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1196 check_eq(Editor_state.cursor1.line, 2, 'cursor')
1197 -- the screen is unchanged
1198 y = Editor_state.top
1199 App.screen.check(y, 'abc', 'screen:1')
1200 y = y + Editor_state.line_height
1201 App.screen.check(y, 'def', 'screen:2')
1202 y = y + Editor_state.line_height
1203 App.screen.check(y, 'ghi', 'screen:3')
1206 function test_up_arrow_scrolls_up_by_one_line()
1207 -- display the lines 2/3/4 with the cursor on line 2
1208 App.screen.init{width=120, height=60}
1209 Editor_state = edit.initialize_test_state()
1210 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1211 Text.redraw_all(Editor_state)
1212 Editor_state.cursor1 = {line=2, pos=1}
1213 Editor_state.screen_top1 = {line=2, pos=1}
1214 Editor_state.screen_bottom1 = {}
1215 edit.draw(Editor_state)
1216 local y = Editor_state.top
1217 App.screen.check(y, 'def', 'baseline/screen:1')
1218 y = y + Editor_state.line_height
1219 App.screen.check(y, 'ghi', 'baseline/screen:2')
1220 y = y + Editor_state.line_height
1221 App.screen.check(y, 'jkl', 'baseline/screen:3')
1222 -- after hitting the up arrow the screen scrolls up by one line
1223 edit.run_after_keychord(Editor_state, 'up')
1224 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1225 check_eq(Editor_state.cursor1.line, 1, 'cursor')
1226 y = Editor_state.top
1227 App.screen.check(y, 'abc', 'screen:1')
1228 y = y + Editor_state.line_height
1229 App.screen.check(y, 'def', 'screen:2')
1230 y = y + Editor_state.line_height
1231 App.screen.check(y, 'ghi', 'screen:3')
1234 function test_up_arrow_scrolls_up_by_one_screen_line()
1235 -- display lines starting from second screen line of a line
1236 App.screen.init{width=Editor_state.left+30, height=60}
1237 Editor_state = edit.initialize_test_state()
1238 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1239 Text.redraw_all(Editor_state)
1240 Editor_state.cursor1 = {line=3, pos=6}
1241 Editor_state.screen_top1 = {line=3, pos=5}
1242 Editor_state.screen_bottom1 = {}
1243 edit.draw(Editor_state)
1244 local y = Editor_state.top
1245 App.screen.check(y, 'jkl', 'baseline/screen:1')
1246 y = y + Editor_state.line_height
1247 App.screen.check(y, 'mno', 'baseline/screen:2')
1248 -- after hitting the up arrow the screen scrolls up to first screen line
1249 edit.run_after_keychord(Editor_state, 'up')
1250 y = Editor_state.top
1251 App.screen.check(y, 'ghi ', 'screen:1')
1252 y = y + Editor_state.line_height
1253 App.screen.check(y, 'jkl', 'screen:2')
1254 y = y + Editor_state.line_height
1255 App.screen.check(y, 'mno', 'screen:3')
1256 check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')
1257 check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
1258 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1259 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1262 function test_up_arrow_scrolls_up_to_final_screen_line()
1263 -- display lines starting just after a long line
1264 App.screen.init{width=Editor_state.left+30, height=60}
1265 Editor_state = edit.initialize_test_state()
1266 Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}
1267 Text.redraw_all(Editor_state)
1268 Editor_state.cursor1 = {line=2, pos=1}
1269 Editor_state.screen_top1 = {line=2, pos=1}
1270 Editor_state.screen_bottom1 = {}
1271 edit.draw(Editor_state)
1272 local y = Editor_state.top
1273 App.screen.check(y, 'ghi', 'baseline/screen:1')
1274 y = y + Editor_state.line_height
1275 App.screen.check(y, 'jkl', 'baseline/screen:2')
1276 y = y + Editor_state.line_height
1277 App.screen.check(y, 'mno', 'baseline/screen:3')
1278 -- after hitting the up arrow the screen scrolls up to final screen line of previous line
1279 edit.run_after_keychord(Editor_state, 'up')
1280 y = Editor_state.top
1281 App.screen.check(y, 'def', 'screen:1')
1282 y = y + Editor_state.line_height
1283 App.screen.check(y, 'ghi', 'screen:2')
1284 y = y + Editor_state.line_height
1285 App.screen.check(y, 'jkl', 'screen:3')
1286 check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')
1287 check_eq(Editor_state.screen_top1.pos, 5, 'screen_top:pos')
1288 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1289 check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
1292 function test_up_arrow_scrolls_up_to_empty_line()
1293 -- display a screenful of text with an empty line just above it outside the screen
1294 App.screen.init{width=120, height=60}
1295 Editor_state = edit.initialize_test_state()
1296 Editor_state.lines = load_array{'', 'abc', 'def', 'ghi', 'jkl'}
1297 Text.redraw_all(Editor_state)
1298 Editor_state.cursor1 = {line=2, pos=1}
1299 Editor_state.screen_top1 = {line=2, pos=1}
1300 Editor_state.screen_bottom1 = {}
1301 edit.draw(Editor_state)
1302 local y = Editor_state.top
1303 App.screen.check(y, 'abc', 'baseline/screen:1')
1304 y = y + Editor_state.line_height
1305 App.screen.check(y, 'def', 'baseline/screen:2')
1306 y = y + Editor_state.line_height
1307 App.screen.check(y, 'ghi', 'baseline/screen:3')
1308 -- after hitting the up arrow the screen scrolls up by one line
1309 edit.run_after_keychord(Editor_state, 'up')
1310 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1311 check_eq(Editor_state.cursor1.line, 1, 'cursor')
1312 y = Editor_state.top
1313 -- empty first line
1314 y = y + Editor_state.line_height
1315 App.screen.check(y, 'abc', 'screen:2')
1316 y = y + Editor_state.line_height
1317 App.screen.check(y, 'def', 'screen:3')
1320 function test_pageup()
1321 App.screen.init{width=120, height=45}
1322 Editor_state = edit.initialize_test_state()
1323 Editor_state.lines = load_array{'abc', 'def', 'ghi'}
1324 Text.redraw_all(Editor_state)
1325 Editor_state.cursor1 = {line=2, pos=1}
1326 Editor_state.screen_top1 = {line=2, pos=1}
1327 Editor_state.screen_bottom1 = {}
1328 -- initially the last two lines are displayed
1329 edit.draw(Editor_state)
1330 local y = Editor_state.top
1331 App.screen.check(y, 'def', 'baseline/screen:1')
1332 y = y + Editor_state.line_height
1333 App.screen.check(y, 'ghi', 'baseline/screen:2')
1334 -- after pageup the cursor goes to first line
1335 edit.run_after_keychord(Editor_state, 'pageup')
1336 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1337 check_eq(Editor_state.cursor1.line, 1, 'cursor')
1338 y = Editor_state.top
1339 App.screen.check(y, 'abc', 'screen:1')
1340 y = y + Editor_state.line_height
1341 App.screen.check(y, 'def', 'screen:2')
1344 function test_pageup_scrolls_up_by_screen_line()
1345 -- display the first three lines with the cursor on the bottom line
1346 App.screen.init{width=Editor_state.left+30, height=60}
1347 Editor_state = edit.initialize_test_state()
1348 Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}
1349 Text.redraw_all(Editor_state)
1350 Editor_state.cursor1 = {line=2, pos=1}
1351 Editor_state.screen_top1 = {line=2, pos=1}
1352 Editor_state.screen_bottom1 = {}
1353 edit.draw(Editor_state)
1354 local y = Editor_state.top
1355 App.screen.check(y, 'ghi', 'baseline/screen:1')
1356 y = y + Editor_state.line_height
1357 App.screen.check(y, 'jkl', 'baseline/screen:2')
1358 y = y + Editor_state.line_height
1359 App.screen.check(y, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1360 -- after hitting the page-up key the screen scrolls up to top
1361 edit.run_after_keychord(Editor_state, 'pageup')
1362 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1363 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1364 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1365 y = Editor_state.top
1366 App.screen.check(y, 'abc ', 'screen:1')
1367 y = y + Editor_state.line_height
1368 App.screen.check(y, 'def', 'screen:2')
1369 y = y + Editor_state.line_height
1370 App.screen.check(y, 'ghi', 'screen:3')
1373 function test_pageup_scrolls_up_from_middle_screen_line()
1374 -- display a few lines starting from the middle of a line (Editor_state.cursor1.pos > 1)
1375 App.screen.init{width=Editor_state.left+30, height=60}
1376 Editor_state = edit.initialize_test_state()
1377 Editor_state.lines = load_array{'abc def', 'ghi jkl', 'mno'}
1378 Text.redraw_all(Editor_state)
1379 Editor_state.cursor1 = {line=2, pos=5}
1380 Editor_state.screen_top1 = {line=2, pos=5}
1381 Editor_state.screen_bottom1 = {}
1382 edit.draw(Editor_state)
1383 local y = Editor_state.top
1384 App.screen.check(y, 'jkl', 'baseline/screen:2')
1385 y = y + Editor_state.line_height
1386 App.screen.check(y, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1387 -- after hitting the page-up key the screen scrolls up to top
1388 edit.run_after_keychord(Editor_state, 'pageup')
1389 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1390 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1391 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1392 y = Editor_state.top
1393 App.screen.check(y, 'abc ', 'screen:1')
1394 y = y + Editor_state.line_height
1395 App.screen.check(y, 'def', 'screen:2')
1396 y = y + Editor_state.line_height
1397 App.screen.check(y, 'ghi ', 'screen:3')
1400 function test_enter_on_bottom_line_scrolls_down()
1401 -- display a few lines with cursor on bottom line
1402 App.screen.init{width=Editor_state.left+30, height=60}
1403 Editor_state = edit.initialize_test_state()
1404 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1405 Text.redraw_all(Editor_state)
1406 Editor_state.cursor1 = {line=3, pos=2}
1407 Editor_state.screen_top1 = {line=1, pos=1}
1408 Editor_state.screen_bottom1 = {}
1409 edit.draw(Editor_state)
1410 local y = Editor_state.top
1411 App.screen.check(y, 'abc', 'baseline/screen:1')
1412 y = y + Editor_state.line_height
1413 App.screen.check(y, 'def', 'baseline/screen:2')
1414 y = y + Editor_state.line_height
1415 App.screen.check(y, 'ghi', 'baseline/screen:3')
1416 -- after hitting the enter key the screen scrolls down
1417 edit.run_after_keychord(Editor_state, 'return')
1418 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1419 check_eq(Editor_state.cursor1.line, 4, 'cursor:line')
1420 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1421 y = Editor_state.top
1422 App.screen.check(y, 'def', 'screen:1')
1423 y = y + Editor_state.line_height
1424 App.screen.check(y, 'g', 'screen:2')
1425 y = y + Editor_state.line_height
1426 App.screen.check(y, 'hi', 'screen:3')
1429 function test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1430 -- display just the bottom line on screen
1431 App.screen.init{width=Editor_state.left+30, height=60}
1432 Editor_state = edit.initialize_test_state()
1433 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1434 Text.redraw_all(Editor_state)
1435 Editor_state.cursor1 = {line=4, pos=2}
1436 Editor_state.screen_top1 = {line=4, pos=1}
1437 Editor_state.screen_bottom1 = {}
1438 edit.draw(Editor_state)
1439 local y = Editor_state.top
1440 App.screen.check(y, 'jkl', 'baseline/screen:1')
1441 -- after hitting the enter key the screen does not scroll down
1442 edit.run_after_keychord(Editor_state, 'return')
1443 check_eq(Editor_state.screen_top1.line, 4, 'screen_top')
1444 check_eq(Editor_state.cursor1.line, 5, 'cursor:line')
1445 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1446 y = Editor_state.top
1447 App.screen.check(y, 'j', 'screen:1')
1448 y = y + Editor_state.line_height
1449 App.screen.check(y, 'kl', 'screen:2')
1452 function test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1453 -- display just an empty bottom line on screen
1454 App.screen.init{width=Editor_state.left+30, height=60}
1455 Editor_state = edit.initialize_test_state()
1456 Editor_state.lines = load_array{'abc', ''}
1457 Text.redraw_all(Editor_state)
1458 Editor_state.cursor1 = {line=2, pos=1}
1459 Editor_state.screen_top1 = {line=2, pos=1}
1460 Editor_state.screen_bottom1 = {}
1461 edit.draw(Editor_state)
1462 -- after hitting the inserting_text key the screen does not scroll down
1463 edit.run_after_text_input(Editor_state, 'a')
1464 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1465 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
1466 check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
1467 local y = Editor_state.top
1468 App.screen.check(y, 'a', 'screen:1')
1471 function test_typing_on_bottom_line_scrolls_down()
1472 -- display a few lines with cursor on bottom line
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=3, pos=4}
1478 Editor_state.screen_top1 = {line=1, pos=1}
1479 Editor_state.screen_bottom1 = {}
1480 edit.draw(Editor_state)
1481 local y = Editor_state.top
1482 App.screen.check(y, 'abc', 'baseline/screen:1')
1483 y = y + Editor_state.line_height
1484 App.screen.check(y, 'def', 'baseline/screen:2')
1485 y = y + Editor_state.line_height
1486 App.screen.check(y, 'ghi', 'baseline/screen:3')
1487 -- after typing something the line wraps and the screen scrolls down
1488 edit.run_after_text_input(Editor_state, 'j')
1489 edit.run_after_text_input(Editor_state, 'k')
1490 edit.run_after_text_input(Editor_state, 'l')
1491 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1492 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1493 check_eq(Editor_state.cursor1.pos, 7, 'cursor:pos')
1494 y = Editor_state.top
1495 App.screen.check(y, 'def', 'screen:1')
1496 y = y + Editor_state.line_height
1497 App.screen.check(y, 'ghij', 'screen:2')
1498 y = y + Editor_state.line_height
1499 App.screen.check(y, 'kl', 'screen:3')
1502 function test_left_arrow_scrolls_up_in_wrapped_line()
1503 -- display lines starting from second screen line of a line
1504 App.screen.init{width=Editor_state.left+30, height=60}
1505 Editor_state = edit.initialize_test_state()
1506 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1507 Text.redraw_all(Editor_state)
1508 Editor_state.screen_top1 = {line=3, pos=5}
1509 Editor_state.screen_bottom1 = {}
1510 -- cursor is at top of screen
1511 Editor_state.cursor1 = {line=3, pos=5}
1512 edit.draw(Editor_state)
1513 local y = Editor_state.top
1514 App.screen.check(y, 'jkl', 'baseline/screen:1')
1515 y = y + Editor_state.line_height
1516 App.screen.check(y, 'mno', 'baseline/screen:2')
1517 -- after hitting the left arrow the screen scrolls up to first screen line
1518 edit.run_after_keychord(Editor_state, 'left')
1519 y = Editor_state.top
1520 App.screen.check(y, 'ghi ', 'screen:1')
1521 y = y + Editor_state.line_height
1522 App.screen.check(y, 'jkl', 'screen:2')
1523 y = y + Editor_state.line_height
1524 App.screen.check(y, 'mno', 'screen:3')
1525 check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')
1526 check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
1527 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1528 check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
1531 function test_right_arrow_scrolls_down_in_wrapped_line()
1532 -- display the first three lines with the cursor on the bottom line
1533 App.screen.init{width=Editor_state.left+30, height=60}
1534 Editor_state = edit.initialize_test_state()
1535 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1536 Text.redraw_all(Editor_state)
1537 Editor_state.screen_top1 = {line=1, pos=1}
1538 Editor_state.screen_bottom1 = {}
1539 -- cursor is at bottom right of screen
1540 Editor_state.cursor1 = {line=3, pos=5}
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') -- line wrapping includes trailing whitespace
1548 -- after hitting the right arrow the screen scrolls down by one line
1549 edit.run_after_keychord(Editor_state, 'right')
1550 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1551 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1552 check_eq(Editor_state.cursor1.pos, 6, 'cursor:pos')
1553 y = Editor_state.top
1554 App.screen.check(y, 'def', 'screen:1')
1555 y = y + Editor_state.line_height
1556 App.screen.check(y, 'ghi ', 'screen:2')
1557 y = y + Editor_state.line_height
1558 App.screen.check(y, 'jkl', 'screen:3')
1561 function test_home_scrolls_up_in_wrapped_line()
1562 -- display lines starting from second screen line of a line
1563 App.screen.init{width=Editor_state.left+30, height=60}
1564 Editor_state = edit.initialize_test_state()
1565 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1566 Text.redraw_all(Editor_state)
1567 Editor_state.screen_top1 = {line=3, pos=5}
1568 Editor_state.screen_bottom1 = {}
1569 -- cursor is at top of screen
1570 Editor_state.cursor1 = {line=3, pos=5}
1571 edit.draw(Editor_state)
1572 local y = Editor_state.top
1573 App.screen.check(y, 'jkl', 'baseline/screen:1')
1574 y = y + Editor_state.line_height
1575 App.screen.check(y, 'mno', 'baseline/screen:2')
1576 -- after hitting home the screen scrolls up to first screen line
1577 edit.run_after_keychord(Editor_state, 'home')
1578 y = Editor_state.top
1579 App.screen.check(y, 'ghi ', 'screen:1')
1580 y = y + Editor_state.line_height
1581 App.screen.check(y, 'jkl', 'screen:2')
1582 y = y + Editor_state.line_height
1583 App.screen.check(y, 'mno', 'screen:3')
1584 check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')
1585 check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
1586 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1587 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1590 function test_end_scrolls_down_in_wrapped_line()
1591 -- display the first three lines with the cursor on the bottom line
1592 App.screen.init{width=Editor_state.left+30, height=60}
1593 Editor_state = edit.initialize_test_state()
1594 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1595 Text.redraw_all(Editor_state)
1596 Editor_state.screen_top1 = {line=1, pos=1}
1597 Editor_state.screen_bottom1 = {}
1598 -- cursor is at bottom right of screen
1599 Editor_state.cursor1 = {line=3, pos=5}
1600 edit.draw(Editor_state)
1601 local y = Editor_state.top
1602 App.screen.check(y, 'abc', 'baseline/screen:1')
1603 y = y + Editor_state.line_height
1604 App.screen.check(y, 'def', 'baseline/screen:2')
1605 y = y + Editor_state.line_height
1606 App.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1607 -- after hitting end the screen scrolls down by one line
1608 edit.run_after_keychord(Editor_state, 'end')
1609 check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
1610 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1611 check_eq(Editor_state.cursor1.pos, 8, 'cursor:pos')
1612 y = Editor_state.top
1613 App.screen.check(y, 'def', 'screen:1')
1614 y = y + Editor_state.line_height
1615 App.screen.check(y, 'ghi ', 'screen:2')
1616 y = y + Editor_state.line_height
1617 App.screen.check(y, 'jkl', 'screen:3')
1620 function test_position_cursor_on_recently_edited_wrapping_line()
1621 -- draw a line wrapping over 2 screen lines
1622 App.screen.init{width=100, height=200}
1623 Editor_state = edit.initialize_test_state()
1624 Editor_state.lines = load_array{'abc def ghi jkl mno pqr ', 'xyz'}
1625 Text.redraw_all(Editor_state)
1626 Editor_state.cursor1 = {line=1, pos=25}
1627 Editor_state.screen_top1 = {line=1, pos=1}
1628 Editor_state.screen_bottom1 = {}
1629 edit.draw(Editor_state)
1630 local y = Editor_state.top
1631 App.screen.check(y, 'abc def ghi ', 'baseline1/screen:1')
1632 y = y + Editor_state.line_height
1633 App.screen.check(y, 'jkl mno pqr ', 'baseline1/screen:2')
1634 y = y + Editor_state.line_height
1635 App.screen.check(y, 'xyz', 'baseline1/screen:3')
1636 -- add to the line until it's wrapping over 3 screen lines
1637 edit.run_after_text_input(Editor_state, 's')
1638 edit.run_after_text_input(Editor_state, 't')
1639 edit.run_after_text_input(Editor_state, 'u')
1640 check_eq(Editor_state.cursor1.pos, 28, 'cursor:pos')
1641 y = Editor_state.top
1642 App.screen.check(y, 'abc def ghi ', 'baseline2/screen:1')
1643 y = y + Editor_state.line_height
1644 App.screen.check(y, 'jkl mno pqr ', 'baseline2/screen:2')
1645 y = y + Editor_state.line_height
1646 App.screen.check(y, 'stu', 'baseline2/screen:3')
1647 -- try to move the cursor earlier in the third screen line by clicking the mouse
1648 edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+Editor_state.line_height*2+5, 1)
1649 -- cursor should move
1650 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1651 check_eq(Editor_state.cursor1.pos, 26, 'cursor:pos')
1654 function test_backspace_can_scroll_up()
1655 -- display the lines 2/3/4 with the cursor on line 2
1656 App.screen.init{width=120, height=60}
1657 Editor_state = edit.initialize_test_state()
1658 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1659 Text.redraw_all(Editor_state)
1660 Editor_state.cursor1 = {line=2, pos=1}
1661 Editor_state.screen_top1 = {line=2, pos=1}
1662 Editor_state.screen_bottom1 = {}
1663 edit.draw(Editor_state)
1664 local y = Editor_state.top
1665 App.screen.check(y, 'def', 'baseline/screen:1')
1666 y = y + Editor_state.line_height
1667 App.screen.check(y, 'ghi', 'baseline/screen:2')
1668 y = y + Editor_state.line_height
1669 App.screen.check(y, 'jkl', 'baseline/screen:3')
1670 -- after hitting backspace the screen scrolls up by one line
1671 edit.run_after_keychord(Editor_state, 'backspace')
1672 check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
1673 check_eq(Editor_state.cursor1.line, 1, 'cursor')
1674 y = Editor_state.top
1675 App.screen.check(y, 'abcdef', 'screen:1')
1676 y = y + Editor_state.line_height
1677 App.screen.check(y, 'ghi', 'screen:2')
1678 y = y + Editor_state.line_height
1679 App.screen.check(y, 'jkl', 'screen:3')
1682 function test_backspace_can_scroll_up_screen_line()
1683 -- display lines starting from second screen line of a line
1684 App.screen.init{width=Editor_state.left+30, height=60}
1685 Editor_state = edit.initialize_test_state()
1686 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1687 Text.redraw_all(Editor_state)
1688 Editor_state.cursor1 = {line=3, pos=5}
1689 Editor_state.screen_top1 = {line=3, pos=5}
1690 Editor_state.screen_bottom1 = {}
1691 edit.draw(Editor_state)
1692 local y = Editor_state.top
1693 App.screen.check(y, 'jkl', 'baseline/screen:1')
1694 y = y + Editor_state.line_height
1695 App.screen.check(y, 'mno', 'baseline/screen:2')
1696 -- after hitting backspace the screen scrolls up by one screen line
1697 edit.run_after_keychord(Editor_state, 'backspace')
1698 y = Editor_state.top
1699 App.screen.check(y, 'ghij', 'screen:1')
1700 y = y + Editor_state.line_height
1701 App.screen.check(y, 'kl', 'screen:2')
1702 y = y + Editor_state.line_height
1703 App.screen.check(y, 'mno', 'screen:3')
1704 check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')
1705 check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
1706 check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
1707 check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
1710 function test_backspace_past_line_boundary()
1711 -- position cursor at start of a (non-first) line
1712 App.screen.init{width=Editor_state.left+30, height=60}
1713 Editor_state = edit.initialize_test_state()
1714 Editor_state.lines = load_array{'abc', 'def'}
1715 Text.redraw_all(Editor_state)
1716 Editor_state.cursor1 = {line=2, pos=1}
1717 -- backspace joins with previous line
1718 edit.run_after_keychord(Editor_state, 'backspace')
1719 check_eq(Editor_state.lines[1].data, 'abcdef', 'check')
1722 -- some tests for operating over selections created using Shift- chords
1723 -- we're just testing delete_selection, and it works the same for all keys
1725 function test_backspace_over_selection()
1726 -- select just one character within a line with cursor before selection
1727 App.screen.init{width=Editor_state.left+30, height=60}
1728 Editor_state = edit.initialize_test_state()
1729 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
1730 Text.redraw_all(Editor_state)
1731 Editor_state.cursor1 = {line=1, pos=1}
1732 Editor_state.selection1 = {line=1, pos=2}
1733 -- backspace deletes the selected character, even though it's after the cursor
1734 edit.run_after_keychord(Editor_state, 'backspace')
1735 check_eq(Editor_state.lines[1].data, 'bc', 'data')
1736 -- cursor (remains) at start of selection
1737 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1738 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1739 -- selection is cleared
1740 check_nil(Editor_state.selection1.line, 'selection')
1743 function test_backspace_over_selection_reverse()
1744 -- select just one character within a line with cursor after selection
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=1, pos=2}
1750 Editor_state.selection1 = {line=1, pos=1}
1751 -- backspace deletes the selected character
1752 edit.run_after_keychord(Editor_state, 'backspace')
1753 check_eq(Editor_state.lines[1].data, 'bc', 'data')
1754 -- cursor moves to start of selection
1755 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1756 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1757 -- selection is cleared
1758 check_nil(Editor_state.selection1.line, 'selection')
1761 function test_backspace_over_multiple_lines()
1762 -- select just one character within a line with cursor after selection
1763 App.screen.init{width=Editor_state.left+30, height=60}
1764 Editor_state = edit.initialize_test_state()
1765 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
1766 Text.redraw_all(Editor_state)
1767 Editor_state.cursor1 = {line=1, pos=2}
1768 Editor_state.selection1 = {line=4, pos=2}
1769 -- backspace deletes the region and joins the remaining portions of lines on either side
1770 edit.run_after_keychord(Editor_state, 'backspace')
1771 check_eq(Editor_state.lines[1].data, 'akl', 'data:1')
1772 check_eq(Editor_state.lines[2].data, 'mno', 'data:2')
1773 -- cursor remains at start of selection
1774 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1775 check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
1776 -- selection is cleared
1777 check_nil(Editor_state.selection1.line, 'selection')
1780 function test_backspace_to_end_of_line()
1781 -- select region from cursor to end of line
1782 App.screen.init{width=Editor_state.left+30, height=60}
1783 Editor_state = edit.initialize_test_state()
1784 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
1785 Text.redraw_all(Editor_state)
1786 Editor_state.cursor1 = {line=1, pos=2}
1787 Editor_state.selection1 = {line=1, pos=4}
1788 -- backspace deletes rest of line without joining to any other line
1789 edit.run_after_keychord(Editor_state, 'backspace')
1790 check_eq(Editor_state.lines[1].data, 'a', 'data:1')
1791 check_eq(Editor_state.lines[2].data, 'def', 'data:2')
1792 -- cursor remains at start of selection
1793 check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
1794 check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
1795 -- selection is cleared
1796 check_nil(Editor_state.selection1.line, 'selection')
1799 function test_backspace_to_start_of_line()
1800 -- select region from cursor to start of line
1801 App.screen.init{width=Editor_state.left+30, height=60}
1802 Editor_state = edit.initialize_test_state()
1803 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
1804 Text.redraw_all(Editor_state)
1805 Editor_state.cursor1 = {line=2, pos=1}
1806 Editor_state.selection1 = {line=2, pos=3}
1807 -- backspace deletes beginning of line without joining to any other line
1808 edit.run_after_keychord(Editor_state, 'backspace')
1809 check_eq(Editor_state.lines[1].data, 'abc', 'data:1')
1810 check_eq(Editor_state.lines[2].data, 'f', 'data:2')
1811 -- cursor remains at start of selection
1812 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
1813 check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
1814 -- selection is cleared
1815 check_nil(Editor_state.selection1.line, 'selection')
1818 function test_undo_insert_text()
1819 App.screen.init{width=120, height=60}
1820 Editor_state = edit.initialize_test_state()
1821 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
1822 Text.redraw_all(Editor_state)
1823 Editor_state.cursor1 = {line=2, pos=4}
1824 Editor_state.screen_top1 = {line=1, pos=1}
1825 Editor_state.screen_bottom1 = {}
1826 -- insert a character
1827 edit.draw(Editor_state)
1828 edit.run_after_text_input(Editor_state, 'g')
1829 check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')
1830 check_eq(Editor_state.cursor1.pos, 5, 'baseline/cursor:pos')
1831 check_nil(Editor_state.selection1.line, 'baseline/selection:line')
1832 check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')
1833 local y = Editor_state.top
1834 App.screen.check(y, 'abc', 'baseline/screen:1')
1835 y = y + Editor_state.line_height
1836 App.screen.check(y, 'defg', 'baseline/screen:2')
1837 y = y + Editor_state.line_height
1838 App.screen.check(y, 'xyz', 'baseline/screen:3')
1839 -- undo
1840 edit.run_after_keychord(Editor_state, 'C-z')
1841 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
1842 check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
1843 check_nil(Editor_state.selection1.line, 'selection:line')
1844 check_nil(Editor_state.selection1.pos, 'selection:pos')
1845 y = Editor_state.top
1846 App.screen.check(y, 'abc', 'screen:1')
1847 y = y + Editor_state.line_height
1848 App.screen.check(y, 'def', 'screen:2')
1849 y = y + Editor_state.line_height
1850 App.screen.check(y, 'xyz', 'screen:3')
1853 function test_undo_delete_text()
1854 App.screen.init{width=120, height=60}
1855 Editor_state = edit.initialize_test_state()
1856 Editor_state.lines = load_array{'abc', 'defg', 'xyz'}
1857 Text.redraw_all(Editor_state)
1858 Editor_state.cursor1 = {line=2, pos=5}
1859 Editor_state.screen_top1 = {line=1, pos=1}
1860 Editor_state.screen_bottom1 = {}
1861 -- delete a character
1862 edit.run_after_keychord(Editor_state, 'backspace')
1863 check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')
1864 check_eq(Editor_state.cursor1.pos, 4, 'baseline/cursor:pos')
1865 check_nil(Editor_state.selection1.line, 'baseline/selection:line')
1866 check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')
1867 local y = Editor_state.top
1868 App.screen.check(y, 'abc', 'baseline/screen:1')
1869 y = y + Editor_state.line_height
1870 App.screen.check(y, 'def', 'baseline/screen:2')
1871 y = y + Editor_state.line_height
1872 App.screen.check(y, 'xyz', 'baseline/screen:3')
1873 -- undo
1874 --? -- after undo, the backspaced key is selected
1875 edit.run_after_keychord(Editor_state, 'C-z')
1876 check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
1877 check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
1878 check_nil(Editor_state.selection1.line, 'selection:line')
1879 check_nil(Editor_state.selection1.pos, 'selection:pos')
1880 --? check_eq(Editor_state.selection1.line, 2, 'selection:line')
1881 --? check_eq(Editor_state.selection1.pos, 4, 'selection:pos')
1882 y = Editor_state.top
1883 App.screen.check(y, 'abc', 'screen:1')
1884 y = y + Editor_state.line_height
1885 App.screen.check(y, 'defg', 'screen:2')
1886 y = y + Editor_state.line_height
1887 App.screen.check(y, 'xyz', 'screen:3')
1890 function test_undo_restores_selection()
1891 -- display a line of text with some part selected
1892 App.screen.init{width=75, height=80}
1893 Editor_state = edit.initialize_test_state()
1894 Editor_state.lines = load_array{'abc'}
1895 Text.redraw_all(Editor_state)
1896 Editor_state.cursor1 = {line=1, pos=1}
1897 Editor_state.selection1 = {line=1, pos=2}
1898 Editor_state.screen_top1 = {line=1, pos=1}
1899 Editor_state.screen_bottom1 = {}
1900 edit.draw(Editor_state)
1901 -- delete selected text
1902 edit.run_after_text_input(Editor_state, 'x')
1903 check_eq(Editor_state.lines[1].data, 'xbc', 'baseline')
1904 check_nil(Editor_state.selection1.line, 'baseline:selection')
1905 -- undo
1906 edit.run_after_keychord(Editor_state, 'C-z')
1907 edit.run_after_keychord(Editor_state, 'C-z')
1908 -- selection is restored
1909 check_eq(Editor_state.selection1.line, 1, 'line')
1910 check_eq(Editor_state.selection1.pos, 2, 'pos')
1913 function test_search()
1914 App.screen.init{width=120, height=60}
1915 Editor_state = edit.initialize_test_state()
1916 Editor_state.lines = load_array{'```lines', '```', 'def', 'ghi', 'deg'}
1917 Text.redraw_all(Editor_state)
1918 Editor_state.cursor1 = {line=1, pos=1}
1919 Editor_state.screen_top1 = {line=1, pos=1}
1920 Editor_state.screen_bottom1 = {}
1921 edit.draw(Editor_state)
1922 -- search for a string
1923 edit.run_after_keychord(Editor_state, 'C-f')
1924 edit.run_after_text_input(Editor_state, 'd')
1925 edit.run_after_keychord(Editor_state, 'return')
1926 check_eq(Editor_state.cursor1.line, 2, '1/cursor:line')
1927 check_eq(Editor_state.cursor1.pos, 1, '1/cursor:pos')
1928 -- reset cursor
1929 Editor_state.cursor1 = {line=1, pos=1}
1930 Editor_state.screen_top1 = {line=1, pos=1}
1931 -- search for second occurrence
1932 edit.run_after_keychord(Editor_state, 'C-f')
1933 edit.run_after_text_input(Editor_state, 'de')
1934 edit.run_after_keychord(Editor_state, 'down')
1935 edit.run_after_keychord(Editor_state, 'return')
1936 check_eq(Editor_state.cursor1.line, 4, '2/cursor:line')
1937 check_eq(Editor_state.cursor1.pos, 1, '2/cursor:pos')
1940 function test_search_upwards()
1941 App.screen.init{width=120, height=60}
1942 Editor_state = edit.initialize_test_state()
1943 Editor_state.lines = load_array{'abc abd'}
1944 Text.redraw_all(Editor_state)
1945 Editor_state.cursor1 = {line=1, pos=2}
1946 Editor_state.screen_top1 = {line=1, pos=1}
1947 Editor_state.screen_bottom1 = {}
1948 edit.draw(Editor_state)
1949 -- search for a string
1950 edit.run_after_keychord(Editor_state, 'C-f')
1951 edit.run_after_text_input(Editor_state, 'a')
1952 -- search for previous occurrence
1953 edit.run_after_keychord(Editor_state, 'up')
1954 check_eq(Editor_state.cursor1.line, 1, '2/cursor:line')
1955 check_eq(Editor_state.cursor1.pos, 1, '2/cursor:pos')
1958 function test_search_wrap()
1959 App.screen.init{width=120, height=60}
1960 Editor_state = edit.initialize_test_state()
1961 Editor_state.lines = load_array{'abc'}
1962 Text.redraw_all(Editor_state)
1963 Editor_state.cursor1 = {line=1, pos=3}
1964 Editor_state.screen_top1 = {line=1, pos=1}
1965 Editor_state.screen_bottom1 = {}
1966 edit.draw(Editor_state)
1967 -- search for a string
1968 edit.run_after_keychord(Editor_state, 'C-f')
1969 edit.run_after_text_input(Editor_state, 'a')
1970 edit.run_after_keychord(Editor_state, 'return')
1971 -- cursor wraps
1972 check_eq(Editor_state.cursor1.line, 1, '1/cursor:line')
1973 check_eq(Editor_state.cursor1.pos, 1, '1/cursor:pos')
1976 function test_search_wrap_upwards()
1977 App.screen.init{width=120, height=60}
1978 Editor_state = edit.initialize_test_state()
1979 Editor_state.lines = load_array{'abc abd'}
1980 Text.redraw_all(Editor_state)
1981 Editor_state.cursor1 = {line=1, pos=1}
1982 Editor_state.screen_top1 = {line=1, pos=1}
1983 Editor_state.screen_bottom1 = {}
1984 edit.draw(Editor_state)
1985 -- search upwards for a string
1986 edit.run_after_keychord(Editor_state, 'C-f')
1987 edit.run_after_text_input(Editor_state, 'a')
1988 edit.run_after_keychord(Editor_state, 'up')
1989 -- cursor wraps
1990 check_eq(Editor_state.cursor1.line, 1, '1/cursor:line')
1991 check_eq(Editor_state.cursor1.pos, 5, '1/cursor:pos')