fix some arg names
[lines.love.git] / source_text_tests.lua
blob795fc0c94eecebef000e03df1a19d9253f9e4b64
1 -- major tests for text editing flows
3 function test_initial_state()
4 io.write('\ntest_initial_state')
5 App.screen.init{width=120, height=60}
6 Editor_state = edit.initialize_test_state()
7 Editor_state.lines = load_array{}
8 Text.redraw_all(Editor_state)
9 edit.draw(Editor_state)
10 check_eq(#Editor_state.lines, 1, 'F - test_initial_state/#lines')
11 check_eq(Editor_state.cursor1.line, 1, 'F - test_initial_state/cursor:line')
12 check_eq(Editor_state.cursor1.pos, 1, 'F - test_initial_state/cursor:pos')
13 check_eq(Editor_state.screen_top1.line, 1, 'F - test_initial_state/screen_top:line')
14 check_eq(Editor_state.screen_top1.pos, 1, 'F - test_initial_state/screen_top:pos')
15 end
17 function test_click_to_create_drawing()
18 io.write('\ntest_click_to_create_drawing')
19 App.screen.init{width=120, height=60}
20 Editor_state = edit.initialize_test_state()
21 Editor_state.lines = load_array{}
22 Text.redraw_all(Editor_state)
23 edit.draw(Editor_state)
24 edit.run_after_mouse_click(Editor_state, 8,Editor_state.top+8, 1)
25 -- cursor skips drawing to always remain on text
26 check_eq(#Editor_state.lines, 2, 'F - test_click_to_create_drawing/#lines')
27 check_eq(Editor_state.cursor1.line, 2, 'F - test_click_to_create_drawing/cursor')
28 end
30 function test_backspace_to_delete_drawing()
31 io.write('\ntest_backspace_to_delete_drawing')
32 -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
33 App.screen.init{width=120, height=60}
34 Editor_state = edit.initialize_test_state()
35 Editor_state.lines = load_array{'```lines', '```', ''}
36 Text.redraw_all(Editor_state)
37 -- cursor is on text as always (outside tests this will get initialized correctly)
38 Editor_state.cursor1.line = 2
39 -- backspacing deletes the drawing
40 edit.run_after_keychord(Editor_state, 'backspace')
41 check_eq(#Editor_state.lines, 1, 'F - test_backspace_to_delete_drawing/#lines')
42 check_eq(Editor_state.cursor1.line, 1, 'F - test_backspace_to_delete_drawing/cursor')
43 end
45 function test_backspace_from_start_of_final_line()
46 io.write('\ntest_backspace_from_start_of_final_line')
47 -- display final line of text with cursor at start of it
48 App.screen.init{width=120, height=60}
49 Editor_state = edit.initialize_test_state()
50 Editor_state.lines = load_array{'abc', 'def'}
51 Editor_state.screen_top1 = {line=2, pos=1}
52 Editor_state.cursor1 = {line=2, pos=1}
53 Text.redraw_all(Editor_state)
54 -- backspace scrolls up
55 edit.run_after_keychord(Editor_state, 'backspace')
56 check_eq(#Editor_state.lines, 1, 'F - test_backspace_from_start_of_final_line/#lines')
57 check_eq(Editor_state.cursor1.line, 1, 'F - test_backspace_from_start_of_final_line/cursor')
58 check_eq(Editor_state.screen_top1.line, 1, 'F - test_backspace_from_start_of_final_line/screen_top')
59 end
61 function test_insert_first_character()
62 io.write('\ntest_insert_first_character')
63 App.screen.init{width=120, height=60}
64 Editor_state = edit.initialize_test_state()
65 Editor_state.lines = load_array{}
66 Text.redraw_all(Editor_state)
67 edit.draw(Editor_state)
68 edit.run_after_text_input(Editor_state, 'a')
69 local y = Editor_state.top
70 App.screen.check(y, 'a', 'F - test_insert_first_character/screen:1')
71 end
73 function test_press_ctrl()
74 io.write('\ntest_press_ctrl')
75 -- press ctrl while the cursor is on text
76 App.screen.init{width=50, height=80}
77 Editor_state = edit.initialize_test_state()
78 Editor_state.lines = load_array{''}
79 Text.redraw_all(Editor_state)
80 Editor_state.cursor1 = {line=1, pos=1}
81 Editor_state.screen_top1 = {line=1, pos=1}
82 Editor_state.screen_bottom1 = {}
83 edit.run_after_keychord(Editor_state, 'C-m')
84 end
86 function test_move_left()
87 io.write('\ntest_move_left')
88 App.screen.init{width=120, height=60}
89 Editor_state = edit.initialize_test_state()
90 Editor_state.lines = load_array{'a'}
91 Text.redraw_all(Editor_state)
92 Editor_state.cursor1 = {line=1, pos=2}
93 edit.draw(Editor_state)
94 edit.run_after_keychord(Editor_state, 'left')
95 check_eq(Editor_state.cursor1.pos, 1, 'F - test_move_left')
96 end
98 function test_move_right()
99 io.write('\ntest_move_right')
100 App.screen.init{width=120, height=60}
101 Editor_state = edit.initialize_test_state()
102 Editor_state.lines = load_array{'a'}
103 Text.redraw_all(Editor_state)
104 Editor_state.cursor1 = {line=1, pos=1}
105 edit.draw(Editor_state)
106 edit.run_after_keychord(Editor_state, 'right')
107 check_eq(Editor_state.cursor1.pos, 2, 'F - test_move_right')
110 function test_move_left_to_previous_line()
111 io.write('\ntest_move_left_to_previous_line')
112 App.screen.init{width=120, height=60}
113 Editor_state = edit.initialize_test_state()
114 Editor_state.lines = load_array{'abc', 'def'}
115 Text.redraw_all(Editor_state)
116 Editor_state.cursor1 = {line=2, pos=1}
117 edit.draw(Editor_state)
118 edit.run_after_keychord(Editor_state, 'left')
119 check_eq(Editor_state.cursor1.line, 1, 'F - test_move_left_to_previous_line/line')
120 check_eq(Editor_state.cursor1.pos, 4, 'F - test_move_left_to_previous_line/pos') -- past end of line
123 function test_move_right_to_next_line()
124 io.write('\ntest_move_right_to_next_line')
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=4} -- past end of line
130 edit.draw(Editor_state)
131 edit.run_after_keychord(Editor_state, 'right')
132 check_eq(Editor_state.cursor1.line, 2, 'F - test_move_right_to_next_line/line')
133 check_eq(Editor_state.cursor1.pos, 1, 'F - test_move_right_to_next_line/pos')
136 function test_move_to_start_of_word()
137 io.write('\ntest_move_to_start_of_word')
138 App.screen.init{width=120, height=60}
139 Editor_state = edit.initialize_test_state()
140 Editor_state.lines = load_array{'abc'}
141 Text.redraw_all(Editor_state)
142 Editor_state.cursor1 = {line=1, pos=3}
143 edit.draw(Editor_state)
144 edit.run_after_keychord(Editor_state, 'M-left')
145 check_eq(Editor_state.cursor1.pos, 1, 'F - test_move_to_start_of_word')
148 function test_move_to_start_of_previous_word()
149 io.write('\ntest_move_to_start_of_previous_word')
150 App.screen.init{width=120, height=60}
151 Editor_state = edit.initialize_test_state()
152 Editor_state.lines = load_array{'abc def'}
153 Text.redraw_all(Editor_state)
154 Editor_state.cursor1 = {line=1, pos=4} -- at the space between words
155 edit.draw(Editor_state)
156 edit.run_after_keychord(Editor_state, 'M-left')
157 check_eq(Editor_state.cursor1.pos, 1, 'F - test_move_to_start_of_previous_word')
160 function test_skip_to_previous_word()
161 io.write('\ntest_skip_to_previous_word')
162 App.screen.init{width=120, height=60}
163 Editor_state = edit.initialize_test_state()
164 Editor_state.lines = load_array{'abc def'}
165 Text.redraw_all(Editor_state)
166 Editor_state.cursor1 = {line=1, pos=5} -- at the start of second word
167 edit.draw(Editor_state)
168 edit.run_after_keychord(Editor_state, 'M-left')
169 check_eq(Editor_state.cursor1.pos, 1, 'F - test_skip_to_previous_word')
172 function test_skip_past_tab_to_previous_word()
173 io.write('\ntest_skip_past_tab_to_previous_word')
174 App.screen.init{width=120, height=60}
175 Editor_state = edit.initialize_test_state()
176 Editor_state.lines = load_array{'abc def\tghi'}
177 Text.redraw_all(Editor_state)
178 Editor_state.cursor1 = {line=1, pos=10} -- within third word
179 edit.draw(Editor_state)
180 edit.run_after_keychord(Editor_state, 'M-left')
181 check_eq(Editor_state.cursor1.pos, 9, 'F - test_skip_past_tab_to_previous_word')
184 function test_skip_multiple_spaces_to_previous_word()
185 io.write('\ntest_skip_multiple_spaces_to_previous_word')
186 App.screen.init{width=120, height=60}
187 Editor_state = edit.initialize_test_state()
188 Editor_state.lines = load_array{'abc def'}
189 Text.redraw_all(Editor_state)
190 Editor_state.cursor1 = {line=1, pos=6} -- at the start of second word
191 edit.draw(Editor_state)
192 edit.run_after_keychord(Editor_state, 'M-left')
193 check_eq(Editor_state.cursor1.pos, 1, 'F - test_skip_multiple_spaces_to_previous_word')
196 function test_move_to_start_of_word_on_previous_line()
197 io.write('\ntest_move_to_start_of_word_on_previous_line')
198 App.screen.init{width=120, height=60}
199 Editor_state = edit.initialize_test_state()
200 Editor_state.lines = load_array{'abc def', 'ghi'}
201 Text.redraw_all(Editor_state)
202 Editor_state.cursor1 = {line=2, pos=1}
203 edit.draw(Editor_state)
204 edit.run_after_keychord(Editor_state, 'M-left')
205 check_eq(Editor_state.cursor1.line, 1, 'F - test_move_to_start_of_word_on_previous_line/line')
206 check_eq(Editor_state.cursor1.pos, 5, 'F - test_move_to_start_of_word_on_previous_line/pos')
209 function test_move_past_end_of_word()
210 io.write('\ntest_move_past_end_of_word')
211 App.screen.init{width=120, height=60}
212 Editor_state = edit.initialize_test_state()
213 Editor_state.lines = load_array{'abc def'}
214 Text.redraw_all(Editor_state)
215 Editor_state.cursor1 = {line=1, pos=1}
216 edit.draw(Editor_state)
217 edit.run_after_keychord(Editor_state, 'M-right')
218 check_eq(Editor_state.cursor1.pos, 4, 'F - test_move_past_end_of_word')
221 function test_skip_to_next_word()
222 io.write('\ntest_skip_to_next_word')
223 App.screen.init{width=120, height=60}
224 Editor_state = edit.initialize_test_state()
225 Editor_state.lines = load_array{'abc def'}
226 Text.redraw_all(Editor_state)
227 Editor_state.cursor1 = {line=1, pos=4} -- at the space between words
228 edit.draw(Editor_state)
229 edit.run_after_keychord(Editor_state, 'M-right')
230 check_eq(Editor_state.cursor1.pos, 8, 'F - test_skip_to_next_word')
233 function test_skip_past_tab_to_next_word()
234 io.write('\ntest_skip_past_tab_to_next_word')
235 App.screen.init{width=120, height=60}
236 Editor_state = edit.initialize_test_state()
237 Editor_state.lines = load_array{'abc\tdef'}
238 Text.redraw_all(Editor_state)
239 Editor_state.cursor1 = {line=1, pos=1} -- at the space between words
240 edit.draw(Editor_state)
241 edit.run_after_keychord(Editor_state, 'M-right')
242 check_eq(Editor_state.cursor1.pos, 4, 'F - test_skip_past_tab_to_next_word')
245 function test_skip_multiple_spaces_to_next_word()
246 io.write('\ntest_skip_multiple_spaces_to_next_word')
247 App.screen.init{width=120, height=60}
248 Editor_state = edit.initialize_test_state()
249 Editor_state.lines = load_array{'abc def'}
250 Text.redraw_all(Editor_state)
251 Editor_state.cursor1 = {line=1, pos=4} -- at the start of second word
252 edit.draw(Editor_state)
253 edit.run_after_keychord(Editor_state, 'M-right')
254 check_eq(Editor_state.cursor1.pos, 9, 'F - test_skip_multiple_spaces_to_next_word')
257 function test_move_past_end_of_word_on_next_line()
258 io.write('\ntest_move_past_end_of_word_on_next_line')
259 App.screen.init{width=120, height=60}
260 Editor_state = edit.initialize_test_state()
261 Editor_state.lines = load_array{'abc def', 'ghi'}
262 Text.redraw_all(Editor_state)
263 Editor_state.cursor1 = {line=1, pos=8}
264 edit.draw(Editor_state)
265 edit.run_after_keychord(Editor_state, 'M-right')
266 check_eq(Editor_state.cursor1.line, 2, 'F - test_move_past_end_of_word_on_next_line/line')
267 check_eq(Editor_state.cursor1.pos, 4, 'F - test_move_past_end_of_word_on_next_line/pos')
270 function test_click_moves_cursor()
271 io.write('\ntest_click_moves_cursor')
272 App.screen.init{width=50, height=60}
273 Editor_state = edit.initialize_test_state()
274 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
275 Text.redraw_all(Editor_state)
276 Editor_state.cursor1 = {line=1, pos=1}
277 Editor_state.screen_top1 = {line=1, pos=1}
278 Editor_state.screen_bottom1 = {}
279 Editor_state.selection1 = {}
280 edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
281 edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
282 check_eq(Editor_state.cursor1.line, 1, 'F - test_click_moves_cursor/cursor:line')
283 check_eq(Editor_state.cursor1.pos, 2, 'F - test_click_moves_cursor/cursor:pos')
284 -- selection is empty to avoid perturbing future edits
285 check_nil(Editor_state.selection1.line, 'F - test_click_moves_cursor/selection:line')
286 check_nil(Editor_state.selection1.pos, 'F - test_click_moves_cursor/selection:pos')
289 function test_click_to_left_of_line()
290 io.write('\ntest_click_to_left_of_line')
291 -- display a line with the cursor in the middle
292 App.screen.init{width=50, height=80}
293 Editor_state = edit.initialize_test_state()
294 Editor_state.lines = load_array{'abc'}
295 Text.redraw_all(Editor_state)
296 Editor_state.cursor1 = {line=1, pos=3}
297 Editor_state.screen_top1 = {line=1, pos=1}
298 Editor_state.screen_bottom1 = {}
299 -- click to the left of the line
300 edit.draw(Editor_state)
301 edit.run_after_mouse_click(Editor_state, Editor_state.left-4,Editor_state.top+5, 1)
302 -- cursor moves to start of line
303 check_eq(Editor_state.cursor1.line, 1, 'F - test_click_to_left_of_line/cursor:line')
304 check_eq(Editor_state.cursor1.pos, 1, 'F - test_click_to_left_of_line/cursor:pos')
305 check_nil(Editor_state.selection1.line, 'F - test_click_to_left_of_line/selection is empty to avoid perturbing future edits')
308 function test_click_takes_margins_into_account()
309 io.write('\ntest_click_takes_margins_into_account')
310 -- display two lines with cursor on one of them
311 App.screen.init{width=100, height=80}
312 Editor_state = edit.initialize_test_state()
313 Editor_state.left = 50 -- occupy only right side of screen
314 Editor_state.lines = load_array{'abc', 'def'}
315 Text.redraw_all(Editor_state)
316 Editor_state.cursor1 = {line=2, pos=1}
317 Editor_state.screen_top1 = {line=1, pos=1}
318 Editor_state.screen_bottom1 = {}
319 -- click on the other line
320 edit.draw(Editor_state)
321 edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
322 -- cursor moves
323 check_eq(Editor_state.cursor1.line, 1, 'F - test_click_takes_margins_into_account/cursor:line')
324 check_eq(Editor_state.cursor1.pos, 2, 'F - test_click_takes_margins_into_account/cursor:pos')
325 check_nil(Editor_state.selection1.line, 'F - test_click_takes_margins_into_account/selection is empty to avoid perturbing future edits')
328 function test_click_on_empty_line()
329 io.write('\ntest_click_on_empty_line')
330 -- display two lines with the first one empty
331 App.screen.init{width=50, height=80}
332 Editor_state = edit.initialize_test_state()
333 Editor_state.lines = load_array{'', 'def'}
334 Text.redraw_all(Editor_state)
335 Editor_state.cursor1 = {line=2, pos=1}
336 Editor_state.screen_top1 = {line=1, pos=1}
337 Editor_state.screen_bottom1 = {}
338 -- click on the empty line
339 edit.draw(Editor_state)
340 edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
341 -- cursor moves
342 check_eq(Editor_state.cursor1.line, 1, 'F - test_click_on_empty_line/cursor')
345 function test_draw_text()
346 io.write('\ntest_draw_text')
347 App.screen.init{width=120, height=60}
348 Editor_state = edit.initialize_test_state()
349 Editor_state.lines = load_array{'abc', 'def', 'ghi'}
350 Text.redraw_all(Editor_state)
351 Editor_state.cursor1 = {line=1, pos=1}
352 Editor_state.screen_top1 = {line=1, pos=1}
353 Editor_state.screen_bottom1 = {}
354 edit.draw(Editor_state)
355 local y = Editor_state.top
356 App.screen.check(y, 'abc', 'F - test_draw_text/screen:1')
357 y = y + Editor_state.line_height
358 App.screen.check(y, 'def', 'F - test_draw_text/screen:2')
359 y = y + Editor_state.line_height
360 App.screen.check(y, 'ghi', 'F - test_draw_text/screen:3')
363 function test_draw_wrapping_text()
364 io.write('\ntest_draw_wrapping_text')
365 App.screen.init{width=50, height=60}
366 Editor_state = edit.initialize_test_state()
367 Editor_state.lines = load_array{'abc', 'defgh', 'xyz'}
368 Text.redraw_all(Editor_state)
369 Editor_state.cursor1 = {line=1, pos=1}
370 Editor_state.screen_top1 = {line=1, pos=1}
371 Editor_state.screen_bottom1 = {}
372 edit.draw(Editor_state)
373 local y = Editor_state.top
374 App.screen.check(y, 'abc', 'F - test_draw_wrapping_text/screen:1')
375 y = y + Editor_state.line_height
376 App.screen.check(y, 'de', 'F - test_draw_wrapping_text/screen:2')
377 y = y + Editor_state.line_height
378 App.screen.check(y, 'fgh', 'F - test_draw_wrapping_text/screen:3')
381 function test_draw_word_wrapping_text()
382 io.write('\ntest_draw_word_wrapping_text')
383 App.screen.init{width=60, height=60}
384 Editor_state = edit.initialize_test_state()
385 Editor_state.lines = load_array{'abc def ghi', 'jkl'}
386 Text.redraw_all(Editor_state)
387 Editor_state.cursor1 = {line=1, pos=1}
388 Editor_state.screen_top1 = {line=1, pos=1}
389 Editor_state.screen_bottom1 = {}
390 edit.draw(Editor_state)
391 local y = Editor_state.top
392 App.screen.check(y, 'abc ', 'F - test_draw_word_wrapping_text/screen:1')
393 y = y + Editor_state.line_height
394 App.screen.check(y, 'def ', 'F - test_draw_word_wrapping_text/screen:2')
395 y = y + Editor_state.line_height
396 App.screen.check(y, 'ghi', 'F - test_draw_word_wrapping_text/screen:3')
399 function test_click_on_wrapping_line()
400 io.write('\ntest_click_on_wrapping_line')
401 -- display two lines with cursor on one of them
402 App.screen.init{width=50, height=80}
403 Editor_state = edit.initialize_test_state()
404 Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu'}
405 Text.redraw_all(Editor_state)
406 Editor_state.cursor1 = {line=1, pos=20}
407 Editor_state.screen_top1 = {line=1, pos=1}
408 Editor_state.screen_bottom1 = {}
409 -- click on the other line
410 edit.draw(Editor_state)
411 edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
412 -- cursor moves
413 check_eq(Editor_state.cursor1.line, 1, 'F - test_click_on_wrapping_line/cursor:line')
414 check_eq(Editor_state.cursor1.pos, 2, 'F - test_click_on_wrapping_line/cursor:pos')
415 check_nil(Editor_state.selection1.line, 'F - test_click_on_wrapping_line/selection is empty to avoid perturbing future edits')
418 function test_click_on_wrapping_line_takes_margins_into_account()
419 io.write('\ntest_click_on_wrapping_line_takes_margins_into_account')
420 -- display two lines with cursor on one of them
421 App.screen.init{width=100, height=80}
422 Editor_state = edit.initialize_test_state()
423 Editor_state.left = 50 -- occupy only right side of screen
424 Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu'}
425 Text.redraw_all(Editor_state)
426 Editor_state.cursor1 = {line=1, pos=20}
427 Editor_state.screen_top1 = {line=1, pos=1}
428 Editor_state.screen_bottom1 = {}
429 -- click on the other line
430 edit.draw(Editor_state)
431 edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
432 -- cursor moves
433 check_eq(Editor_state.cursor1.line, 1, 'F - test_click_on_wrapping_line_takes_margins_into_account/cursor:line')
434 check_eq(Editor_state.cursor1.pos, 2, 'F - test_click_on_wrapping_line_takes_margins_into_account/cursor:pos')
435 check_nil(Editor_state.selection1.line, 'F - test_click_on_wrapping_line_takes_margins_into_account/selection is empty to avoid perturbing future edits')
438 function test_draw_text_wrapping_within_word()
439 -- arrange a screen line that needs to be split within a word
440 io.write('\ntest_draw_text_wrapping_within_word')
441 App.screen.init{width=60, height=60}
442 Editor_state = edit.initialize_test_state()
443 Editor_state.lines = load_array{'abcd e fghijk', 'xyz'}
444 Text.redraw_all(Editor_state)
445 Editor_state.cursor1 = {line=1, pos=1}
446 Editor_state.screen_top1 = {line=1, pos=1}
447 Editor_state.screen_bottom1 = {}
448 edit.draw(Editor_state)
449 local y = Editor_state.top
450 App.screen.check(y, 'abcd ', 'F - test_draw_text_wrapping_within_word/screen:1')
451 y = y + Editor_state.line_height
452 App.screen.check(y, 'e fgh', 'F - test_draw_text_wrapping_within_word/screen:2')
453 y = y + Editor_state.line_height
454 App.screen.check(y, 'ijk', 'F - test_draw_text_wrapping_within_word/screen:3')
457 function test_draw_wrapping_text_containing_non_ascii()
458 -- draw a long line containing non-ASCII
459 io.write('\ntest_draw_wrapping_text_containing_non_ascii')
460 App.screen.init{width=60, height=60}
461 Editor_state = edit.initialize_test_state()
462 Editor_state.lines = load_array{'madam I’m adam', 'xyz'} -- notice the non-ASCII apostrophe
463 Text.redraw_all(Editor_state)
464 Editor_state.cursor1 = {line=1, pos=1}
465 Editor_state.screen_top1 = {line=1, pos=1}
466 Editor_state.screen_bottom1 = {}
467 edit.draw(Editor_state)
468 local y = Editor_state.top
469 App.screen.check(y, 'mad', 'F - test_draw_wrapping_text_containing_non_ascii/screen:1')
470 y = y + Editor_state.line_height
471 App.screen.check(y, 'am I', 'F - test_draw_wrapping_text_containing_non_ascii/screen:2')
472 y = y + Editor_state.line_height
473 App.screen.check(y, '’m a', 'F - test_draw_wrapping_text_containing_non_ascii/screen:3')
476 function test_click_on_wrapping_line()
477 io.write('\ntest_click_on_wrapping_line')
478 -- display a wrapping line
479 App.screen.init{width=75, height=80}
480 Editor_state = edit.initialize_test_state()
481 -- 12345678901234
482 Editor_state.lines = load_array{"madam I'm adam"}
483 Text.redraw_all(Editor_state)
484 Editor_state.cursor1 = {line=1, pos=1}
485 Editor_state.screen_top1 = {line=1, pos=1}
486 Editor_state.screen_bottom1 = {}
487 edit.draw(Editor_state)
488 local y = Editor_state.top
489 App.screen.check(y, 'madam ', 'F - test_click_on_wrapping_line/baseline/screen:1')
490 y = y + Editor_state.line_height
491 App.screen.check(y, "I'm ad", 'F - test_click_on_wrapping_line/baseline/screen:2')
492 y = y + Editor_state.line_height
493 -- click past end of second screen line
494 edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
495 -- cursor moves to end of screen line
496 check_eq(Editor_state.cursor1.line, 1, 'F - test_click_on_wrapping_line/cursor:line')
497 check_eq(Editor_state.cursor1.pos, 12, 'F - test_click_on_wrapping_line/cursor:pos')
500 function test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen()
501 io.write('\ntest_click_on_wrapping_line_rendered_from_partway_at_top_of_screen')
502 -- display a wrapping line from its second screen line
503 App.screen.init{width=75, height=80}
504 Editor_state = edit.initialize_test_state()
505 -- 12345678901234
506 Editor_state.lines = load_array{"madam I'm adam"}
507 Text.redraw_all(Editor_state)
508 Editor_state.cursor1 = {line=1, pos=8}
509 Editor_state.screen_top1 = {line=1, pos=7}
510 Editor_state.screen_bottom1 = {}
511 edit.draw(Editor_state)
512 local y = Editor_state.top
513 App.screen.check(y, "I'm ad", 'F - test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen/baseline/screen:2')
514 y = y + Editor_state.line_height
515 -- click past end of second screen line
516 edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
517 -- cursor moves to end of screen line
518 check_eq(Editor_state.cursor1.line, 1, 'F - test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen/cursor:line')
519 check_eq(Editor_state.cursor1.pos, 12, 'F - test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen/cursor:pos')
522 function test_click_past_end_of_wrapping_line()
523 io.write('\ntest_click_past_end_of_wrapping_line')
524 -- display a wrapping line
525 App.screen.init{width=75, height=80}
526 Editor_state = edit.initialize_test_state()
527 -- 12345678901234
528 Editor_state.lines = load_array{"madam I'm adam"}
529 Text.redraw_all(Editor_state)
530 Editor_state.cursor1 = {line=1, pos=1}
531 Editor_state.screen_top1 = {line=1, pos=1}
532 Editor_state.screen_bottom1 = {}
533 edit.draw(Editor_state)
534 local y = Editor_state.top
535 App.screen.check(y, 'madam ', 'F - test_click_past_end_of_wrapping_line/baseline/screen:1')
536 y = y + Editor_state.line_height
537 App.screen.check(y, "I'm ad", 'F - test_click_past_end_of_wrapping_line/baseline/screen:2')
538 y = y + Editor_state.line_height
539 App.screen.check(y, 'am', 'F - test_click_past_end_of_wrapping_line/baseline/screen:3')
540 y = y + Editor_state.line_height
541 -- click past the end of it
542 edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
543 -- cursor moves to end of line
544 check_eq(Editor_state.cursor1.pos, 15, 'F - test_click_past_end_of_wrapping_line/cursor') -- one more than the number of UTF-8 code-points
547 function test_click_past_end_of_wrapping_line_containing_non_ascii()
548 io.write('\ntest_click_past_end_of_wrapping_line_containing_non_ascii')
549 -- display a wrapping line containing non-ASCII
550 App.screen.init{width=75, height=80}
551 Editor_state = edit.initialize_test_state()
552 -- 12345678901234
553 Editor_state.lines = load_array{'madam I’m adam'} -- notice the non-ASCII apostrophe
554 Text.redraw_all(Editor_state)
555 Editor_state.cursor1 = {line=1, pos=1}
556 Editor_state.screen_top1 = {line=1, pos=1}
557 Editor_state.screen_bottom1 = {}
558 edit.draw(Editor_state)
559 local y = Editor_state.top
560 App.screen.check(y, 'madam ', 'F - test_click_past_end_of_wrapping_line_containing_non_ascii/baseline/screen:1')
561 y = y + Editor_state.line_height
562 App.screen.check(y, 'I’m ad', 'F - test_click_past_end_of_wrapping_line_containing_non_ascii/baseline/screen:2')
563 y = y + Editor_state.line_height
564 App.screen.check(y, 'am', 'F - test_click_past_end_of_wrapping_line_containing_non_ascii/baseline/screen:3')
565 y = y + Editor_state.line_height
566 -- click past the end of it
567 edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
568 -- cursor moves to end of line
569 check_eq(Editor_state.cursor1.pos, 15, 'F - test_click_past_end_of_wrapping_line_containing_non_ascii/cursor') -- one more than the number of UTF-8 code-points
572 function test_click_past_end_of_word_wrapping_line()
573 io.write('\ntest_click_past_end_of_word_wrapping_line')
574 -- display a long line wrapping at a word boundary on a screen of more realistic length
575 App.screen.init{width=160, height=80}
576 Editor_state = edit.initialize_test_state()
577 -- 0 1 2
578 -- 123456789012345678901
579 Editor_state.lines = load_array{'the quick brown fox jumped over the lazy dog'}
580 Text.redraw_all(Editor_state)
581 Editor_state.cursor1 = {line=1, pos=1}
582 Editor_state.screen_top1 = {line=1, pos=1}
583 Editor_state.screen_bottom1 = {}
584 edit.draw(Editor_state)
585 local y = Editor_state.top
586 App.screen.check(y, 'the quick brown fox ', 'F - test_click_past_end_of_word_wrapping_line/baseline/screen:1')
587 y = y + Editor_state.line_height
588 -- click past the end of the screen line
589 edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
590 -- cursor moves to end of screen line
591 check_eq(Editor_state.cursor1.pos, 20, 'F - test_click_past_end_of_word_wrapping_line/cursor')
594 function test_select_text()
595 io.write('\ntest_select_text')
596 -- display a line of text
597 App.screen.init{width=75, height=80}
598 Editor_state = edit.initialize_test_state()
599 Editor_state.lines = load_array{'abc def'}
600 Text.redraw_all(Editor_state)
601 Editor_state.cursor1 = {line=1, pos=1}
602 Editor_state.screen_top1 = {line=1, pos=1}
603 Editor_state.screen_bottom1 = {}
604 edit.draw(Editor_state)
605 -- select a letter
606 App.fake_key_press('lshift')
607 edit.run_after_keychord(Editor_state, 'S-right')
608 App.fake_key_release('lshift')
609 edit.key_release(Editor_state, 'lshift')
610 -- selection persists even after shift is released
611 check_eq(Editor_state.selection1.line, 1, 'F - test_select_text/selection:line')
612 check_eq(Editor_state.selection1.pos, 1, 'F - test_select_text/selection:pos')
613 check_eq(Editor_state.cursor1.line, 1, 'F - test_select_text/cursor:line')
614 check_eq(Editor_state.cursor1.pos, 2, 'F - test_select_text/cursor:pos')
617 function test_cursor_movement_without_shift_resets_selection()
618 io.write('\ntest_cursor_movement_without_shift_resets_selection')
619 -- display a line of text with some part selected
620 App.screen.init{width=75, height=80}
621 Editor_state = edit.initialize_test_state()
622 Editor_state.lines = load_array{'abc'}
623 Text.redraw_all(Editor_state)
624 Editor_state.cursor1 = {line=1, pos=1}
625 Editor_state.selection1 = {line=1, pos=2}
626 Editor_state.screen_top1 = {line=1, pos=1}
627 Editor_state.screen_bottom1 = {}
628 edit.draw(Editor_state)
629 -- press an arrow key without shift
630 edit.run_after_keychord(Editor_state, 'right')
631 -- no change to data, selection is reset
632 check_nil(Editor_state.selection1.line, 'F - test_cursor_movement_without_shift_resets_selection')
633 check_eq(Editor_state.lines[1].data, 'abc', 'F - test_cursor_movement_without_shift_resets_selection/data')
636 function test_edit_deletes_selection()
637 io.write('\ntest_edit_deletes_selection')
638 -- display a line of text with some part selected
639 App.screen.init{width=75, height=80}
640 Editor_state = edit.initialize_test_state()
641 Editor_state.lines = load_array{'abc'}
642 Text.redraw_all(Editor_state)
643 Editor_state.cursor1 = {line=1, pos=1}
644 Editor_state.selection1 = {line=1, pos=2}
645 Editor_state.screen_top1 = {line=1, pos=1}
646 Editor_state.screen_bottom1 = {}
647 edit.draw(Editor_state)
648 -- press a key
649 edit.run_after_text_input(Editor_state, 'x')
650 -- selected text is deleted and replaced with the key
651 check_eq(Editor_state.lines[1].data, 'xbc', 'F - test_edit_deletes_selection')
654 function test_edit_with_shift_key_deletes_selection()
655 io.write('\ntest_edit_with_shift_key_deletes_selection')
656 -- display a line of text with some part selected
657 App.screen.init{width=75, height=80}
658 Editor_state = edit.initialize_test_state()
659 Editor_state.lines = load_array{'abc'}
660 Text.redraw_all(Editor_state)
661 Editor_state.cursor1 = {line=1, pos=1}
662 Editor_state.selection1 = {line=1, pos=2}
663 Editor_state.screen_top1 = {line=1, pos=1}
664 Editor_state.screen_bottom1 = {}
665 edit.draw(Editor_state)
666 -- mimic precise keypresses for a capital letter
667 App.fake_key_press('lshift')
668 edit.keychord_press(Editor_state, 'd', 'd')
669 edit.text_input(Editor_state, 'D')
670 edit.key_release(Editor_state, 'd')
671 App.fake_key_release('lshift')
672 -- selected text is deleted and replaced with the key
673 check_nil(Editor_state.selection1.line, 'F - test_edit_with_shift_key_deletes_selection')
674 check_eq(Editor_state.lines[1].data, 'Dbc', 'F - test_edit_with_shift_key_deletes_selection/data')
677 function test_copy_does_not_reset_selection()
678 io.write('\ntest_copy_does_not_reset_selection')
679 -- display a line of text with a selection
680 App.screen.init{width=75, height=80}
681 Editor_state = edit.initialize_test_state()
682 Editor_state.lines = load_array{'abc'}
683 Text.redraw_all(Editor_state)
684 Editor_state.cursor1 = {line=1, pos=1}
685 Editor_state.selection1 = {line=1, pos=2}
686 Editor_state.screen_top1 = {line=1, pos=1}
687 Editor_state.screen_bottom1 = {}
688 edit.draw(Editor_state)
689 -- copy selection
690 edit.run_after_keychord(Editor_state, 'C-c')
691 check_eq(App.clipboard, 'a', 'F - test_copy_does_not_reset_selection/clipboard')
692 -- selection is reset since shift key is not pressed
693 check(Editor_state.selection1.line, 'F - test_copy_does_not_reset_selection')
696 function test_cut()
697 io.write('\ntest_cut')
698 -- display a line of text with some part selected
699 App.screen.init{width=75, height=80}
700 Editor_state = edit.initialize_test_state()
701 Editor_state.lines = load_array{'abc'}
702 Text.redraw_all(Editor_state)
703 Editor_state.cursor1 = {line=1, pos=1}
704 Editor_state.selection1 = {line=1, pos=2}
705 Editor_state.screen_top1 = {line=1, pos=1}
706 Editor_state.screen_bottom1 = {}
707 edit.draw(Editor_state)
708 -- press a key
709 edit.run_after_keychord(Editor_state, 'C-x')
710 check_eq(App.clipboard, 'a', 'F - test_cut/clipboard')
711 -- selected text is deleted
712 check_eq(Editor_state.lines[1].data, 'bc', 'F - test_cut/data')
715 function test_paste_replaces_selection()
716 io.write('\ntest_paste_replaces_selection')
717 -- display a line of text with a selection
718 App.screen.init{width=75, height=80}
719 Editor_state = edit.initialize_test_state()
720 Editor_state.lines = load_array{'abc', 'def'}
721 Text.redraw_all(Editor_state)
722 Editor_state.cursor1 = {line=2, pos=1}
723 Editor_state.selection1 = {line=1, pos=1}
724 Editor_state.screen_top1 = {line=1, pos=1}
725 Editor_state.screen_bottom1 = {}
726 edit.draw(Editor_state)
727 -- set clipboard
728 App.clipboard = 'xyz'
729 -- paste selection
730 edit.run_after_keychord(Editor_state, 'C-v')
731 -- selection is reset since shift key is not pressed
732 -- selection includes the newline, so it's also deleted
733 check_eq(Editor_state.lines[1].data, 'xyzdef', 'F - test_paste_replaces_selection')
736 function test_deleting_selection_may_scroll()
737 io.write('\ntest_deleting_selection_may_scroll')
738 -- display lines 2/3/4
739 App.screen.init{width=120, height=60}
740 Editor_state = edit.initialize_test_state()
741 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
742 Text.redraw_all(Editor_state)
743 Editor_state.cursor1 = {line=3, pos=2}
744 Editor_state.screen_top1 = {line=2, pos=1}
745 Editor_state.screen_bottom1 = {}
746 edit.draw(Editor_state)
747 local y = Editor_state.top
748 App.screen.check(y, 'def', 'F - test_deleting_selection_may_scroll/baseline/screen:1')
749 y = y + Editor_state.line_height
750 App.screen.check(y, 'ghi', 'F - test_deleting_selection_may_scroll/baseline/screen:2')
751 y = y + Editor_state.line_height
752 App.screen.check(y, 'jkl', 'F - test_deleting_selection_may_scroll/baseline/screen:3')
753 -- set up a selection starting above the currently displayed page
754 Editor_state.selection1 = {line=1, pos=2}
755 -- delete selection
756 edit.run_after_keychord(Editor_state, 'backspace')
757 -- page scrolls up
758 check_eq(Editor_state.screen_top1.line, 1, 'F - test_deleting_selection_may_scroll')
759 check_eq(Editor_state.lines[1].data, 'ahi', 'F - test_deleting_selection_may_scroll/data')
762 function test_edit_wrapping_text()
763 io.write('\ntest_edit_wrapping_text')
764 App.screen.init{width=50, height=60}
765 Editor_state = edit.initialize_test_state()
766 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
767 Text.redraw_all(Editor_state)
768 Editor_state.cursor1 = {line=2, pos=4}
769 Editor_state.screen_top1 = {line=1, pos=1}
770 Editor_state.screen_bottom1 = {}
771 edit.draw(Editor_state)
772 edit.run_after_text_input(Editor_state, 'g')
773 local y = Editor_state.top
774 App.screen.check(y, 'abc', 'F - test_edit_wrapping_text/screen:1')
775 y = y + Editor_state.line_height
776 App.screen.check(y, 'de', 'F - test_edit_wrapping_text/screen:2')
777 y = y + Editor_state.line_height
778 App.screen.check(y, 'fg', 'F - test_edit_wrapping_text/screen:3')
781 function test_insert_newline()
782 io.write('\ntest_insert_newline')
783 -- display a few lines
784 App.screen.init{width=Editor_state.left+30, height=60}
785 Editor_state = edit.initialize_test_state()
786 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
787 Text.redraw_all(Editor_state)
788 Editor_state.cursor1 = {line=1, pos=2}
789 Editor_state.screen_top1 = {line=1, pos=1}
790 Editor_state.screen_bottom1 = {}
791 edit.draw(Editor_state)
792 local y = Editor_state.top
793 App.screen.check(y, 'abc', 'F - test_insert_newline/baseline/screen:1')
794 y = y + Editor_state.line_height
795 App.screen.check(y, 'def', 'F - test_insert_newline/baseline/screen:2')
796 y = y + Editor_state.line_height
797 App.screen.check(y, 'ghi', 'F - test_insert_newline/baseline/screen:3')
798 -- hitting the enter key splits the line
799 edit.run_after_keychord(Editor_state, 'return')
800 check_eq(Editor_state.screen_top1.line, 1, 'F - test_insert_newline/screen_top')
801 check_eq(Editor_state.cursor1.line, 2, 'F - test_insert_newline/cursor:line')
802 check_eq(Editor_state.cursor1.pos, 1, 'F - test_insert_newline/cursor:pos')
803 y = Editor_state.top
804 App.screen.check(y, 'a', 'F - test_insert_newline/screen:1')
805 y = y + Editor_state.line_height
806 App.screen.check(y, 'bc', 'F - test_insert_newline/screen:2')
807 y = y + Editor_state.line_height
808 App.screen.check(y, 'def', 'F - test_insert_newline/screen:3')
811 function test_insert_newline_at_start_of_line()
812 io.write('\ntest_insert_newline_at_start_of_line')
813 -- display a line
814 App.screen.init{width=Editor_state.left+30, height=60}
815 Editor_state = edit.initialize_test_state()
816 Editor_state.lines = load_array{'abc'}
817 Text.redraw_all(Editor_state)
818 Editor_state.cursor1 = {line=1, pos=1}
819 Editor_state.screen_top1 = {line=1, pos=1}
820 Editor_state.screen_bottom1 = {}
821 -- hitting the enter key splits the line
822 edit.run_after_keychord(Editor_state, 'return')
823 check_eq(Editor_state.cursor1.line, 2, 'F - test_insert_newline_at_start_of_line/cursor:line')
824 check_eq(Editor_state.cursor1.pos, 1, 'F - test_insert_newline_at_start_of_line/cursor:pos')
825 check_eq(Editor_state.lines[1].data, '', 'F - test_insert_newline_at_start_of_line/data:1')
826 check_eq(Editor_state.lines[2].data, 'abc', 'F - test_insert_newline_at_start_of_line/data:2')
829 function test_insert_from_clipboard()
830 io.write('\ntest_insert_from_clipboard')
831 -- display a few lines
832 App.screen.init{width=Editor_state.left+30, height=60}
833 Editor_state = edit.initialize_test_state()
834 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
835 Text.redraw_all(Editor_state)
836 Editor_state.cursor1 = {line=1, pos=2}
837 Editor_state.screen_top1 = {line=1, pos=1}
838 Editor_state.screen_bottom1 = {}
839 edit.draw(Editor_state)
840 local y = Editor_state.top
841 App.screen.check(y, 'abc', 'F - test_insert_from_clipboard/baseline/screen:1')
842 y = y + Editor_state.line_height
843 App.screen.check(y, 'def', 'F - test_insert_from_clipboard/baseline/screen:2')
844 y = y + Editor_state.line_height
845 App.screen.check(y, 'ghi', 'F - test_insert_from_clipboard/baseline/screen:3')
846 -- paste some text including a newline, check that new line is created
847 App.clipboard = 'xy\nz'
848 edit.run_after_keychord(Editor_state, 'C-v')
849 check_eq(Editor_state.screen_top1.line, 1, 'F - test_insert_from_clipboard/screen_top')
850 check_eq(Editor_state.cursor1.line, 2, 'F - test_insert_from_clipboard/cursor:line')
851 check_eq(Editor_state.cursor1.pos, 2, 'F - test_insert_from_clipboard/cursor:pos')
852 y = Editor_state.top
853 App.screen.check(y, 'axy', 'F - test_insert_from_clipboard/screen:1')
854 y = y + Editor_state.line_height
855 App.screen.check(y, 'zbc', 'F - test_insert_from_clipboard/screen:2')
856 y = y + Editor_state.line_height
857 App.screen.check(y, 'def', 'F - test_insert_from_clipboard/screen:3')
860 function test_select_text_using_mouse()
861 io.write('\ntest_select_text_using_mouse')
862 App.screen.init{width=50, height=60}
863 Editor_state = edit.initialize_test_state()
864 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
865 Text.redraw_all(Editor_state)
866 Editor_state.cursor1 = {line=1, pos=1}
867 Editor_state.screen_top1 = {line=1, pos=1}
868 Editor_state.screen_bottom1 = {}
869 Editor_state.selection1 = {}
870 edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
871 -- press and hold on first location
872 edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
873 -- drag and release somewhere else
874 edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
875 check_eq(Editor_state.selection1.line, 1, 'F - test_select_text_using_mouse/selection:line')
876 check_eq(Editor_state.selection1.pos, 2, 'F - test_select_text_using_mouse/selection:pos')
877 check_eq(Editor_state.cursor1.line, 2, 'F - test_select_text_using_mouse/cursor:line')
878 check_eq(Editor_state.cursor1.pos, 4, 'F - test_select_text_using_mouse/cursor:pos')
881 function test_select_text_using_mouse_and_shift()
882 io.write('\ntest_select_text_using_mouse_and_shift')
883 App.screen.init{width=50, height=60}
884 Editor_state = edit.initialize_test_state()
885 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
886 Text.redraw_all(Editor_state)
887 Editor_state.cursor1 = {line=1, pos=1}
888 Editor_state.screen_top1 = {line=1, pos=1}
889 Editor_state.screen_bottom1 = {}
890 Editor_state.selection1 = {}
891 edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
892 -- click on first location
893 edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
894 edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
895 -- hold down shift and click somewhere else
896 App.fake_key_press('lshift')
897 edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
898 edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
899 App.fake_key_release('lshift')
900 check_eq(Editor_state.selection1.line, 1, 'F - test_select_text_using_mouse_and_shift/selection:line')
901 check_eq(Editor_state.selection1.pos, 2, 'F - test_select_text_using_mouse_and_shift/selection:pos')
902 check_eq(Editor_state.cursor1.line, 2, 'F - test_select_text_using_mouse_and_shift/cursor:line')
903 check_eq(Editor_state.cursor1.pos, 4, 'F - test_select_text_using_mouse_and_shift/cursor:pos')
906 function test_select_text_repeatedly_using_mouse_and_shift()
907 io.write('\ntest_select_text_repeatedly_using_mouse_and_shift')
908 App.screen.init{width=50, height=60}
909 Editor_state = edit.initialize_test_state()
910 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
911 Text.redraw_all(Editor_state)
912 Text.redraw_all(Editor_state)
913 Editor_state.cursor1 = {line=1, pos=1}
914 Editor_state.screen_top1 = {line=1, pos=1}
915 Editor_state.screen_bottom1 = {}
916 Editor_state.selection1 = {}
917 edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
918 -- click on first location
919 edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
920 edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
921 -- hold down shift and click on a second location
922 App.fake_key_press('lshift')
923 edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
924 edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
925 -- hold down shift and click at a third location
926 App.fake_key_press('lshift')
927 edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
928 edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+Editor_state.line_height+5, 1)
929 App.fake_key_release('lshift')
930 -- selection is between first and third location. forget the second location, not the first.
931 check_eq(Editor_state.selection1.line, 1, 'F - test_select_text_repeatedly_using_mouse_and_shift/selection:line')
932 check_eq(Editor_state.selection1.pos, 2, 'F - test_select_text_repeatedly_using_mouse_and_shift/selection:pos')
933 check_eq(Editor_state.cursor1.line, 2, 'F - test_select_text_repeatedly_using_mouse_and_shift/cursor:line')
934 check_eq(Editor_state.cursor1.pos, 2, 'F - test_select_text_repeatedly_using_mouse_and_shift/cursor:pos')
937 function test_cut_without_selection()
938 io.write('\ntest_cut_without_selection')
939 -- display a few lines
940 App.screen.init{width=Editor_state.left+30, height=60}
941 Editor_state = edit.initialize_test_state()
942 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
943 Text.redraw_all(Editor_state)
944 Editor_state.cursor1 = {line=1, pos=2}
945 Editor_state.screen_top1 = {line=1, pos=1}
946 Editor_state.screen_bottom1 = {}
947 Editor_state.selection1 = {}
948 edit.draw(Editor_state)
949 -- try to cut without selecting text
950 edit.run_after_keychord(Editor_state, 'C-x')
951 -- no crash
952 check_nil(Editor_state.selection1.line, 'F - test_cut_without_selection')
955 function test_pagedown()
956 io.write('\ntest_pagedown')
957 App.screen.init{width=120, height=45}
958 Editor_state = edit.initialize_test_state()
959 Editor_state.lines = load_array{'abc', 'def', 'ghi'}
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 -- initially the first two lines are displayed
965 edit.draw(Editor_state)
966 local y = Editor_state.top
967 App.screen.check(y, 'abc', 'F - test_pagedown/baseline/screen:1')
968 y = y + Editor_state.line_height
969 App.screen.check(y, 'def', 'F - test_pagedown/baseline/screen:2')
970 -- after pagedown the bottom line becomes the top
971 edit.run_after_keychord(Editor_state, 'pagedown')
972 check_eq(Editor_state.screen_top1.line, 2, 'F - test_pagedown/screen_top')
973 check_eq(Editor_state.cursor1.line, 2, 'F - test_pagedown/cursor')
974 y = Editor_state.top
975 App.screen.check(y, 'def', 'F - test_pagedown/screen:1')
976 y = y + Editor_state.line_height
977 App.screen.check(y, 'ghi', 'F - test_pagedown/screen:2')
980 function test_pagedown_skips_drawings()
981 io.write('\ntest_pagedown_skips_drawings')
982 -- some lines of text with a drawing intermixed
983 local drawing_width = 50
984 App.screen.init{width=Editor_state.left+drawing_width, height=80}
985 Editor_state = edit.initialize_test_state()
986 Editor_state.lines = load_array{'abc', -- height 15
987 '```lines', '```', -- height 25
988 'def', -- height 15
989 'ghi'} -- height 15
990 Text.redraw_all(Editor_state)
991 check_eq(Editor_state.lines[2].mode, 'drawing', 'F - test_pagedown_skips_drawings/baseline/lines')
992 Editor_state.cursor1 = {line=1, pos=1}
993 Editor_state.screen_top1 = {line=1, pos=1}
994 Editor_state.screen_bottom1 = {}
995 local drawing_height = Drawing_padding_height + drawing_width/2 -- default
996 -- initially the screen displays the first line and the drawing
997 -- 15px margin + 15px line1 + 10px margin + 25px drawing + 10px margin = 75px < screen height 80px
998 edit.draw(Editor_state)
999 local y = Editor_state.top
1000 App.screen.check(y, 'abc', 'F - test_pagedown_skips_drawings/baseline/screen:1')
1001 -- after pagedown the screen draws the drawing up top
1002 -- 15px margin + 10px margin + 25px drawing + 10px margin + 15px line3 = 75px < screen height 80px
1003 edit.run_after_keychord(Editor_state, 'pagedown')
1004 check_eq(Editor_state.screen_top1.line, 2, 'F - test_pagedown_skips_drawings/screen_top')
1005 check_eq(Editor_state.cursor1.line, 3, 'F - test_pagedown_skips_drawings/cursor')
1006 y = Editor_state.top + drawing_height
1007 App.screen.check(y, 'def', 'F - test_pagedown_skips_drawings/screen:1')
1010 function test_pagedown_can_start_from_middle_of_long_wrapping_line()
1011 io.write('\ntest_pagedown_can_start_from_middle_of_long_wrapping_line')
1012 -- draw a few lines starting from a very long wrapping line
1013 App.screen.init{width=Editor_state.left+30, height=60}
1014 Editor_state = edit.initialize_test_state()
1015 Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu vwx yza bcd efg hij', 'XYZ'}
1016 Text.redraw_all(Editor_state)
1017 Editor_state.cursor1 = {line=1, pos=2}
1018 Editor_state.screen_top1 = {line=1, pos=1}
1019 Editor_state.screen_bottom1 = {}
1020 edit.draw(Editor_state)
1021 local y = Editor_state.top
1022 App.screen.check(y, 'abc ', 'F - test_pagedown_can_start_from_middle_of_long_wrapping_line/baseline/screen:1')
1023 y = y + Editor_state.line_height
1024 App.screen.check(y, 'def ', 'F - test_pagedown_can_start_from_middle_of_long_wrapping_line/baseline/screen:2')
1025 y = y + Editor_state.line_height
1026 App.screen.check(y, 'ghi ', 'F - test_pagedown_can_start_from_middle_of_long_wrapping_line/baseline/screen:3')
1027 -- after pagedown we scroll down the very long wrapping line
1028 edit.run_after_keychord(Editor_state, 'pagedown')
1029 check_eq(Editor_state.screen_top1.line, 1, 'F - test_pagedown_can_start_from_middle_of_long_wrapping_line/screen_top:line')
1030 check_eq(Editor_state.screen_top1.pos, 9, 'F - test_pagedown_can_start_from_middle_of_long_wrapping_line/screen_top:pos')
1031 y = Editor_state.top
1032 App.screen.check(y, 'ghi ', 'F - test_pagedown_can_start_from_middle_of_long_wrapping_line/screen:1')
1033 y = y + Editor_state.line_height
1034 App.screen.check(y, 'jkl ', 'F - test_pagedown_can_start_from_middle_of_long_wrapping_line/screen:2')
1035 y = y + Editor_state.line_height
1036 App.screen.check(y, 'mno ', 'F - test_pagedown_can_start_from_middle_of_long_wrapping_line/screen:3')
1039 function test_pagedown_never_moves_up()
1040 io.write('\ntest_pagedown_never_moves_up')
1041 -- draw the final screen line of a wrapping line
1042 App.screen.init{width=Editor_state.left+30, height=60}
1043 Editor_state = edit.initialize_test_state()
1044 Editor_state.lines = load_array{'abc def ghi'}
1045 Text.redraw_all(Editor_state)
1046 Editor_state.cursor1 = {line=1, pos=9}
1047 Editor_state.screen_top1 = {line=1, pos=9}
1048 Editor_state.screen_bottom1 = {}
1049 edit.draw(Editor_state)
1050 -- pagedown makes no change
1051 edit.run_after_keychord(Editor_state, 'pagedown')
1052 check_eq(Editor_state.screen_top1.line, 1, 'F - test_pagedown_never_moves_up/screen_top:line')
1053 check_eq(Editor_state.screen_top1.pos, 9, 'F - test_pagedown_never_moves_up/screen_top:pos')
1056 function test_down_arrow_moves_cursor()
1057 io.write('\ntest_down_arrow_moves_cursor')
1058 App.screen.init{width=120, height=60}
1059 Editor_state = edit.initialize_test_state()
1060 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1061 Text.redraw_all(Editor_state)
1062 Editor_state.cursor1 = {line=1, pos=1}
1063 Editor_state.screen_top1 = {line=1, pos=1}
1064 Editor_state.screen_bottom1 = {}
1065 -- initially the first three lines are displayed
1066 edit.draw(Editor_state)
1067 local y = Editor_state.top
1068 App.screen.check(y, 'abc', 'F - test_down_arrow_moves_cursor/baseline/screen:1')
1069 y = y + Editor_state.line_height
1070 App.screen.check(y, 'def', 'F - test_down_arrow_moves_cursor/baseline/screen:2')
1071 y = y + Editor_state.line_height
1072 App.screen.check(y, 'ghi', 'F - test_down_arrow_moves_cursor/baseline/screen:3')
1073 -- after hitting the down arrow, the cursor moves down by 1 line
1074 edit.run_after_keychord(Editor_state, 'down')
1075 check_eq(Editor_state.screen_top1.line, 1, 'F - test_down_arrow_moves_cursor/screen_top')
1076 check_eq(Editor_state.cursor1.line, 2, 'F - test_down_arrow_moves_cursor/cursor')
1077 -- the screen is unchanged
1078 y = Editor_state.top
1079 App.screen.check(y, 'abc', 'F - test_down_arrow_moves_cursor/screen:1')
1080 y = y + Editor_state.line_height
1081 App.screen.check(y, 'def', 'F - test_down_arrow_moves_cursor/screen:2')
1082 y = y + Editor_state.line_height
1083 App.screen.check(y, 'ghi', 'F - test_down_arrow_moves_cursor/screen:3')
1086 function test_down_arrow_scrolls_down_by_one_line()
1087 io.write('\ntest_down_arrow_scrolls_down_by_one_line')
1088 -- display the first three lines with the cursor on the bottom line
1089 App.screen.init{width=120, height=60}
1090 Editor_state = edit.initialize_test_state()
1091 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1092 Text.redraw_all(Editor_state)
1093 Editor_state.cursor1 = {line=3, pos=1}
1094 Editor_state.screen_top1 = {line=1, pos=1}
1095 Editor_state.screen_bottom1 = {}
1096 edit.draw(Editor_state)
1097 local y = Editor_state.top
1098 App.screen.check(y, 'abc', 'F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:1')
1099 y = y + Editor_state.line_height
1100 App.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:2')
1101 y = y + Editor_state.line_height
1102 App.screen.check(y, 'ghi', 'F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:3')
1103 -- after hitting the down arrow the screen scrolls down by one line
1104 edit.run_after_keychord(Editor_state, 'down')
1105 check_eq(Editor_state.screen_top1.line, 2, 'F - test_down_arrow_scrolls_down_by_one_line/screen_top')
1106 check_eq(Editor_state.cursor1.line, 4, 'F - test_down_arrow_scrolls_down_by_one_line/cursor')
1107 y = Editor_state.top
1108 App.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_line/screen:1')
1109 y = y + Editor_state.line_height
1110 App.screen.check(y, 'ghi', 'F - test_down_arrow_scrolls_down_by_one_line/screen:2')
1111 y = y + Editor_state.line_height
1112 App.screen.check(y, 'jkl', 'F - test_down_arrow_scrolls_down_by_one_line/screen:3')
1115 function test_down_arrow_scrolls_down_by_one_screen_line()
1116 io.write('\ntest_down_arrow_scrolls_down_by_one_screen_line')
1117 -- display the first three lines with the cursor on the bottom line
1118 App.screen.init{width=Editor_state.left+30, height=60}
1119 Editor_state = edit.initialize_test_state()
1120 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1121 Text.redraw_all(Editor_state)
1122 Editor_state.cursor1 = {line=3, pos=1}
1123 Editor_state.screen_top1 = {line=1, pos=1}
1124 Editor_state.screen_bottom1 = {}
1125 edit.draw(Editor_state)
1126 local y = Editor_state.top
1127 App.screen.check(y, 'abc', 'F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:1')
1128 y = y + Editor_state.line_height
1129 App.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:2')
1130 y = y + Editor_state.line_height
1131 App.screen.check(y, 'ghi ', 'F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:3') -- line wrapping includes trailing whitespace
1132 -- after hitting the down arrow the screen scrolls down by one line
1133 edit.run_after_keychord(Editor_state, 'down')
1134 check_eq(Editor_state.screen_top1.line, 2, 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen_top')
1135 check_eq(Editor_state.cursor1.line, 3, 'F - test_down_arrow_scrolls_down_by_one_screen_line/cursor:line')
1136 check_eq(Editor_state.cursor1.pos, 5, 'F - test_down_arrow_scrolls_down_by_one_screen_line/cursor:pos')
1137 y = Editor_state.top
1138 App.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen:1')
1139 y = y + Editor_state.line_height
1140 App.screen.check(y, 'ghi ', 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen:2')
1141 y = y + Editor_state.line_height
1142 App.screen.check(y, 'jkl', 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen:3')
1145 function test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word()
1146 io.write('\ntest_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word')
1147 -- display the first three lines with the cursor on the bottom line
1148 App.screen.init{width=Editor_state.left+30, height=60}
1149 Editor_state = edit.initialize_test_state()
1150 Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}
1151 Text.redraw_all(Editor_state)
1152 Editor_state.cursor1 = {line=3, pos=1}
1153 Editor_state.screen_top1 = {line=1, pos=1}
1154 Editor_state.screen_bottom1 = {}
1155 edit.draw(Editor_state)
1156 local y = Editor_state.top
1157 App.screen.check(y, 'abc', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:1')
1158 y = y + Editor_state.line_height
1159 App.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:2')
1160 y = y + Editor_state.line_height
1161 App.screen.check(y, 'ghij', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:3')
1162 -- after hitting the down arrow the screen scrolls down by one line
1163 edit.run_after_keychord(Editor_state, 'down')
1164 check_eq(Editor_state.screen_top1.line, 2, 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen_top')
1165 check_eq(Editor_state.cursor1.line, 3, 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/cursor:line')
1166 check_eq(Editor_state.cursor1.pos, 5, 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/cursor:pos')
1167 y = Editor_state.top
1168 App.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:1')
1169 y = y + Editor_state.line_height
1170 App.screen.check(y, 'ghij', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:2')
1171 y = y + Editor_state.line_height
1172 App.screen.check(y, 'kl', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:3')
1175 function test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up()
1176 io.write('\ntest_pagedown_followed_by_down_arrow_does_not_scroll_screen_up')
1177 App.screen.init{width=Editor_state.left+30, height=60}
1178 Editor_state = edit.initialize_test_state()
1179 Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}
1180 Text.redraw_all(Editor_state)
1181 Editor_state.cursor1 = {line=3, pos=1}
1182 Editor_state.screen_top1 = {line=1, pos=1}
1183 Editor_state.screen_bottom1 = {}
1184 edit.draw(Editor_state)
1185 local y = Editor_state.top
1186 App.screen.check(y, 'abc', 'F - test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:1')
1187 y = y + Editor_state.line_height
1188 App.screen.check(y, 'def', 'F - test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:2')
1189 y = y + Editor_state.line_height
1190 App.screen.check(y, 'ghij', 'F - test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:3')
1191 -- after hitting pagedown the screen scrolls down to start of a long line
1192 edit.run_after_keychord(Editor_state, 'pagedown')
1193 check_eq(Editor_state.screen_top1.line, 3, 'F - test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up/baseline2/screen_top')
1194 check_eq(Editor_state.cursor1.line, 3, 'F - test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up/baseline2/cursor:line')
1195 check_eq(Editor_state.cursor1.pos, 1, 'F - test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up/baseline2/cursor:pos')
1196 -- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll up
1197 edit.run_after_keychord(Editor_state, 'down')
1198 check_eq(Editor_state.screen_top1.line, 3, 'F - test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up/screen_top')
1199 check_eq(Editor_state.cursor1.line, 3, 'F - test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up/cursor:line')
1200 check_eq(Editor_state.cursor1.pos, 5, 'F - test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up/cursor:pos')
1201 y = Editor_state.top
1202 App.screen.check(y, 'ghij', 'F - test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up/screen:1')
1203 y = y + Editor_state.line_height
1204 App.screen.check(y, 'kl', 'F - test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up/screen:2')
1205 y = y + Editor_state.line_height
1206 App.screen.check(y, 'mno', 'F - test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up/screen:3')
1209 function test_up_arrow_moves_cursor()
1210 io.write('\ntest_up_arrow_moves_cursor')
1211 -- display the first 3 lines with the cursor on the bottom line
1212 App.screen.init{width=120, height=60}
1213 Editor_state = edit.initialize_test_state()
1214 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1215 Text.redraw_all(Editor_state)
1216 Editor_state.cursor1 = {line=3, pos=1}
1217 Editor_state.screen_top1 = {line=1, pos=1}
1218 Editor_state.screen_bottom1 = {}
1219 edit.draw(Editor_state)
1220 local y = Editor_state.top
1221 App.screen.check(y, 'abc', 'F - test_up_arrow_moves_cursor/baseline/screen:1')
1222 y = y + Editor_state.line_height
1223 App.screen.check(y, 'def', 'F - test_up_arrow_moves_cursor/baseline/screen:2')
1224 y = y + Editor_state.line_height
1225 App.screen.check(y, 'ghi', 'F - test_up_arrow_moves_cursor/baseline/screen:3')
1226 -- after hitting the up arrow the cursor moves up by 1 line
1227 edit.run_after_keychord(Editor_state, 'up')
1228 check_eq(Editor_state.screen_top1.line, 1, 'F - test_up_arrow_moves_cursor/screen_top')
1229 check_eq(Editor_state.cursor1.line, 2, 'F - test_up_arrow_moves_cursor/cursor')
1230 -- the screen is unchanged
1231 y = Editor_state.top
1232 App.screen.check(y, 'abc', 'F - test_up_arrow_moves_cursor/screen:1')
1233 y = y + Editor_state.line_height
1234 App.screen.check(y, 'def', 'F - test_up_arrow_moves_cursor/screen:2')
1235 y = y + Editor_state.line_height
1236 App.screen.check(y, 'ghi', 'F - test_up_arrow_moves_cursor/screen:3')
1239 function test_up_arrow_scrolls_up_by_one_line()
1240 io.write('\ntest_up_arrow_scrolls_up_by_one_line')
1241 -- display the lines 2/3/4 with the cursor on line 2
1242 App.screen.init{width=120, height=60}
1243 Editor_state = edit.initialize_test_state()
1244 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1245 Text.redraw_all(Editor_state)
1246 Editor_state.cursor1 = {line=2, pos=1}
1247 Editor_state.screen_top1 = {line=2, pos=1}
1248 Editor_state.screen_bottom1 = {}
1249 edit.draw(Editor_state)
1250 local y = Editor_state.top
1251 App.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:1')
1252 y = y + Editor_state.line_height
1253 App.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:2')
1254 y = y + Editor_state.line_height
1255 App.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:3')
1256 -- after hitting the up arrow the screen scrolls up by one line
1257 edit.run_after_keychord(Editor_state, 'up')
1258 check_eq(Editor_state.screen_top1.line, 1, 'F - test_up_arrow_scrolls_up_by_one_line/screen_top')
1259 check_eq(Editor_state.cursor1.line, 1, 'F - test_up_arrow_scrolls_up_by_one_line/cursor')
1260 y = Editor_state.top
1261 App.screen.check(y, 'abc', 'F - test_up_arrow_scrolls_up_by_one_line/screen:1')
1262 y = y + Editor_state.line_height
1263 App.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_by_one_line/screen:2')
1264 y = y + Editor_state.line_height
1265 App.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_by_one_line/screen:3')
1268 function test_up_arrow_scrolls_up_by_one_screen_line()
1269 io.write('\ntest_up_arrow_scrolls_up_by_one_screen_line')
1270 -- display lines starting from second screen line of a line
1271 App.screen.init{width=Editor_state.left+30, height=60}
1272 Editor_state = edit.initialize_test_state()
1273 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1274 Text.redraw_all(Editor_state)
1275 Editor_state.cursor1 = {line=3, pos=6}
1276 Editor_state.screen_top1 = {line=3, pos=5}
1277 Editor_state.screen_bottom1 = {}
1278 edit.draw(Editor_state)
1279 local y = Editor_state.top
1280 App.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_by_one_screen_line/baseline/screen:1')
1281 y = y + Editor_state.line_height
1282 App.screen.check(y, 'mno', 'F - test_up_arrow_scrolls_up_by_one_screen_line/baseline/screen:2')
1283 -- after hitting the up arrow the screen scrolls up to first screen line
1284 edit.run_after_keychord(Editor_state, 'up')
1285 y = Editor_state.top
1286 App.screen.check(y, 'ghi ', 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen:1')
1287 y = y + Editor_state.line_height
1288 App.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen:2')
1289 y = y + Editor_state.line_height
1290 App.screen.check(y, 'mno', 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen:3')
1291 check_eq(Editor_state.screen_top1.line, 3, 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen_top')
1292 check_eq(Editor_state.screen_top1.pos, 1, 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen_top')
1293 check_eq(Editor_state.cursor1.line, 3, 'F - test_up_arrow_scrolls_up_by_one_screen_line/cursor:line')
1294 check_eq(Editor_state.cursor1.pos, 1, 'F - test_up_arrow_scrolls_up_by_one_screen_line/cursor:pos')
1297 function test_up_arrow_scrolls_up_to_final_screen_line()
1298 io.write('\ntest_up_arrow_scrolls_up_to_final_screen_line')
1299 -- display lines starting just after a long line
1300 App.screen.init{width=Editor_state.left+30, height=60}
1301 Editor_state = edit.initialize_test_state()
1302 Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}
1303 Text.redraw_all(Editor_state)
1304 Editor_state.cursor1 = {line=2, pos=1}
1305 Editor_state.screen_top1 = {line=2, pos=1}
1306 Editor_state.screen_bottom1 = {}
1307 edit.draw(Editor_state)
1308 local y = Editor_state.top
1309 App.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:1')
1310 y = y + Editor_state.line_height
1311 App.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:2')
1312 y = y + Editor_state.line_height
1313 App.screen.check(y, 'mno', 'F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:3')
1314 -- after hitting the up arrow the screen scrolls up to final screen line of previous line
1315 edit.run_after_keychord(Editor_state, 'up')
1316 y = Editor_state.top
1317 App.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen:1')
1318 y = y + Editor_state.line_height
1319 App.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen:2')
1320 y = y + Editor_state.line_height
1321 App.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen:3')
1322 check_eq(Editor_state.screen_top1.line, 1, 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen_top')
1323 check_eq(Editor_state.screen_top1.pos, 5, 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen_top')
1324 check_eq(Editor_state.cursor1.line, 1, 'F - test_up_arrow_scrolls_up_to_final_screen_line/cursor:line')
1325 check_eq(Editor_state.cursor1.pos, 5, 'F - test_up_arrow_scrolls_up_to_final_screen_line/cursor:pos')
1328 function test_up_arrow_scrolls_up_to_empty_line()
1329 io.write('\ntest_up_arrow_scrolls_up_to_empty_line')
1330 -- display a screenful of text with an empty line just above it outside the screen
1331 App.screen.init{width=120, height=60}
1332 Editor_state = edit.initialize_test_state()
1333 Editor_state.lines = load_array{'', 'abc', 'def', 'ghi', 'jkl'}
1334 Text.redraw_all(Editor_state)
1335 Editor_state.cursor1 = {line=2, pos=1}
1336 Editor_state.screen_top1 = {line=2, pos=1}
1337 Editor_state.screen_bottom1 = {}
1338 edit.draw(Editor_state)
1339 local y = Editor_state.top
1340 App.screen.check(y, 'abc', 'F - test_up_arrow_scrolls_up_to_empty_line/baseline/screen:1')
1341 y = y + Editor_state.line_height
1342 App.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_to_empty_line/baseline/screen:2')
1343 y = y + Editor_state.line_height
1344 App.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_to_empty_line/baseline/screen:3')
1345 -- after hitting the up arrow the screen scrolls up by one line
1346 edit.run_after_keychord(Editor_state, 'up')
1347 check_eq(Editor_state.screen_top1.line, 1, 'F - test_up_arrow_scrolls_up_to_empty_line/screen_top')
1348 check_eq(Editor_state.cursor1.line, 1, 'F - test_up_arrow_scrolls_up_to_empty_line/cursor')
1349 y = Editor_state.top
1350 -- empty first line
1351 y = y + Editor_state.line_height
1352 App.screen.check(y, 'abc', 'F - test_up_arrow_scrolls_up_to_empty_line/screen:2')
1353 y = y + Editor_state.line_height
1354 App.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_to_empty_line/screen:3')
1357 function test_pageup()
1358 io.write('\ntest_pageup')
1359 App.screen.init{width=120, height=45}
1360 Editor_state = edit.initialize_test_state()
1361 Editor_state.lines = load_array{'abc', 'def', 'ghi'}
1362 Text.redraw_all(Editor_state)
1363 Editor_state.cursor1 = {line=2, pos=1}
1364 Editor_state.screen_top1 = {line=2, pos=1}
1365 Editor_state.screen_bottom1 = {}
1366 -- initially the last two lines are displayed
1367 edit.draw(Editor_state)
1368 local y = Editor_state.top
1369 App.screen.check(y, 'def', 'F - test_pageup/baseline/screen:1')
1370 y = y + Editor_state.line_height
1371 App.screen.check(y, 'ghi', 'F - test_pageup/baseline/screen:2')
1372 -- after pageup the cursor goes to first line
1373 edit.run_after_keychord(Editor_state, 'pageup')
1374 check_eq(Editor_state.screen_top1.line, 1, 'F - test_pageup/screen_top')
1375 check_eq(Editor_state.cursor1.line, 1, 'F - test_pageup/cursor')
1376 y = Editor_state.top
1377 App.screen.check(y, 'abc', 'F - test_pageup/screen:1')
1378 y = y + Editor_state.line_height
1379 App.screen.check(y, 'def', 'F - test_pageup/screen:2')
1382 function test_pageup_scrolls_up_by_screen_line()
1383 io.write('\ntest_pageup_scrolls_up_by_screen_line')
1384 -- display the first three lines with the cursor on the bottom line
1385 App.screen.init{width=Editor_state.left+30, height=60}
1386 Editor_state = edit.initialize_test_state()
1387 Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}
1388 Text.redraw_all(Editor_state)
1389 Editor_state.cursor1 = {line=2, pos=1}
1390 Editor_state.screen_top1 = {line=2, pos=1}
1391 Editor_state.screen_bottom1 = {}
1392 edit.draw(Editor_state)
1393 local y = Editor_state.top
1394 App.screen.check(y, 'ghi', 'F - test_pageup_scrolls_up_by_screen_line/baseline/screen:1')
1395 y = y + Editor_state.line_height
1396 App.screen.check(y, 'jkl', 'F - test_pageup_scrolls_up_by_screen_line/baseline/screen:2')
1397 y = y + Editor_state.line_height
1398 App.screen.check(y, 'mno', 'F - test_pageup_scrolls_up_by_screen_line/baseline/screen:3') -- line wrapping includes trailing whitespace
1399 -- after hitting the page-up key the screen scrolls up to top
1400 edit.run_after_keychord(Editor_state, 'pageup')
1401 check_eq(Editor_state.screen_top1.line, 1, 'F - test_pageup_scrolls_up_by_screen_line/screen_top')
1402 check_eq(Editor_state.cursor1.line, 1, 'F - test_pageup_scrolls_up_by_screen_line/cursor:line')
1403 check_eq(Editor_state.cursor1.pos, 1, 'F - test_pageup_scrolls_up_by_screen_line/cursor:pos')
1404 y = Editor_state.top
1405 App.screen.check(y, 'abc ', 'F - test_pageup_scrolls_up_by_screen_line/screen:1')
1406 y = y + Editor_state.line_height
1407 App.screen.check(y, 'def', 'F - test_pageup_scrolls_up_by_screen_line/screen:2')
1408 y = y + Editor_state.line_height
1409 App.screen.check(y, 'ghi', 'F - test_pageup_scrolls_up_by_screen_line/screen:3')
1412 function test_pageup_scrolls_up_from_middle_screen_line()
1413 io.write('\ntest_pageup_scrolls_up_from_middle_screen_line')
1414 -- display a few lines starting from the middle of a line (Editor_state.cursor1.pos > 1)
1415 App.screen.init{width=Editor_state.left+30, height=60}
1416 Editor_state = edit.initialize_test_state()
1417 Editor_state.lines = load_array{'abc def', 'ghi jkl', 'mno'}
1418 Text.redraw_all(Editor_state)
1419 Editor_state.cursor1 = {line=2, pos=5}
1420 Editor_state.screen_top1 = {line=2, pos=5}
1421 Editor_state.screen_bottom1 = {}
1422 edit.draw(Editor_state)
1423 local y = Editor_state.top
1424 App.screen.check(y, 'jkl', 'F - test_pageup_scrolls_up_from_middle_screen_line/baseline/screen:2')
1425 y = y + Editor_state.line_height
1426 App.screen.check(y, 'mno', 'F - test_pageup_scrolls_up_from_middle_screen_line/baseline/screen:3') -- line wrapping includes trailing whitespace
1427 -- after hitting the page-up key the screen scrolls up to top
1428 edit.run_after_keychord(Editor_state, 'pageup')
1429 check_eq(Editor_state.screen_top1.line, 1, 'F - test_pageup_scrolls_up_from_middle_screen_line/screen_top')
1430 check_eq(Editor_state.cursor1.line, 1, 'F - test_pageup_scrolls_up_from_middle_screen_line/cursor:line')
1431 check_eq(Editor_state.cursor1.pos, 1, 'F - test_pageup_scrolls_up_from_middle_screen_line/cursor:pos')
1432 y = Editor_state.top
1433 App.screen.check(y, 'abc ', 'F - test_pageup_scrolls_up_from_middle_screen_line/screen:1')
1434 y = y + Editor_state.line_height
1435 App.screen.check(y, 'def', 'F - test_pageup_scrolls_up_from_middle_screen_line/screen:2')
1436 y = y + Editor_state.line_height
1437 App.screen.check(y, 'ghi ', 'F - test_pageup_scrolls_up_from_middle_screen_line/screen:3')
1440 function test_enter_on_bottom_line_scrolls_down()
1441 io.write('\ntest_enter_on_bottom_line_scrolls_down')
1442 -- display a few lines with cursor on bottom line
1443 App.screen.init{width=Editor_state.left+30, height=60}
1444 Editor_state = edit.initialize_test_state()
1445 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1446 Text.redraw_all(Editor_state)
1447 Editor_state.cursor1 = {line=3, pos=2}
1448 Editor_state.screen_top1 = {line=1, pos=1}
1449 Editor_state.screen_bottom1 = {}
1450 edit.draw(Editor_state)
1451 local y = Editor_state.top
1452 App.screen.check(y, 'abc', 'F - test_enter_on_bottom_line_scrolls_down/baseline/screen:1')
1453 y = y + Editor_state.line_height
1454 App.screen.check(y, 'def', 'F - test_enter_on_bottom_line_scrolls_down/baseline/screen:2')
1455 y = y + Editor_state.line_height
1456 App.screen.check(y, 'ghi', 'F - test_enter_on_bottom_line_scrolls_down/baseline/screen:3')
1457 -- after hitting the enter key the screen scrolls down
1458 edit.run_after_keychord(Editor_state, 'return')
1459 check_eq(Editor_state.screen_top1.line, 2, 'F - test_enter_on_bottom_line_scrolls_down/screen_top')
1460 check_eq(Editor_state.cursor1.line, 4, 'F - test_enter_on_bottom_line_scrolls_down/cursor:line')
1461 check_eq(Editor_state.cursor1.pos, 1, 'F - test_enter_on_bottom_line_scrolls_down/cursor:pos')
1462 y = Editor_state.top
1463 App.screen.check(y, 'def', 'F - test_enter_on_bottom_line_scrolls_down/screen:1')
1464 y = y + Editor_state.line_height
1465 App.screen.check(y, 'g', 'F - test_enter_on_bottom_line_scrolls_down/screen:2')
1466 y = y + Editor_state.line_height
1467 App.screen.check(y, 'hi', 'F - test_enter_on_bottom_line_scrolls_down/screen:3')
1470 function test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1471 io.write('\ntest_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom')
1472 -- display just the bottom line on screen
1473 App.screen.init{width=Editor_state.left+30, height=60}
1474 Editor_state = edit.initialize_test_state()
1475 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1476 Text.redraw_all(Editor_state)
1477 Editor_state.cursor1 = {line=4, pos=2}
1478 Editor_state.screen_top1 = {line=4, pos=1}
1479 Editor_state.screen_bottom1 = {}
1480 edit.draw(Editor_state)
1481 local y = Editor_state.top
1482 App.screen.check(y, 'jkl', 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/baseline/screen:1')
1483 -- after hitting the enter key the screen does not scroll down
1484 edit.run_after_keychord(Editor_state, 'return')
1485 check_eq(Editor_state.screen_top1.line, 4, 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/screen_top')
1486 check_eq(Editor_state.cursor1.line, 5, 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/cursor:line')
1487 check_eq(Editor_state.cursor1.pos, 1, 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/cursor:pos')
1488 y = Editor_state.top
1489 App.screen.check(y, 'j', 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/screen:1')
1490 y = y + Editor_state.line_height
1491 App.screen.check(y, 'kl', 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/screen:2')
1494 function test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1495 io.write('\ntest_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom')
1496 -- display just an empty bottom line on screen
1497 App.screen.init{width=Editor_state.left+30, height=60}
1498 Editor_state = edit.initialize_test_state()
1499 Editor_state.lines = load_array{'abc', ''}
1500 Text.redraw_all(Editor_state)
1501 Editor_state.cursor1 = {line=2, pos=1}
1502 Editor_state.screen_top1 = {line=2, pos=1}
1503 Editor_state.screen_bottom1 = {}
1504 edit.draw(Editor_state)
1505 -- after hitting the inserting_text key the screen does not scroll down
1506 edit.run_after_text_input(Editor_state, 'a')
1507 check_eq(Editor_state.screen_top1.line, 2, 'F - test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom/screen_top')
1508 check_eq(Editor_state.cursor1.line, 2, 'F - test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom/cursor:line')
1509 check_eq(Editor_state.cursor1.pos, 2, 'F - test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom/cursor:pos')
1510 local y = Editor_state.top
1511 App.screen.check(y, 'a', 'F - test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom/screen:1')
1514 function test_typing_on_bottom_line_scrolls_down()
1515 io.write('\ntest_typing_on_bottom_line_scrolls_down')
1516 -- display a few lines with cursor on bottom line
1517 App.screen.init{width=Editor_state.left+30, height=60}
1518 Editor_state = edit.initialize_test_state()
1519 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1520 Text.redraw_all(Editor_state)
1521 Editor_state.cursor1 = {line=3, pos=4}
1522 Editor_state.screen_top1 = {line=1, pos=1}
1523 Editor_state.screen_bottom1 = {}
1524 edit.draw(Editor_state)
1525 local y = Editor_state.top
1526 App.screen.check(y, 'abc', 'F - test_typing_on_bottom_line_scrolls_down/baseline/screen:1')
1527 y = y + Editor_state.line_height
1528 App.screen.check(y, 'def', 'F - test_typing_on_bottom_line_scrolls_down/baseline/screen:2')
1529 y = y + Editor_state.line_height
1530 App.screen.check(y, 'ghi', 'F - test_typing_on_bottom_line_scrolls_down/baseline/screen:3')
1531 -- after typing something the line wraps and the screen scrolls down
1532 edit.run_after_text_input(Editor_state, 'j')
1533 edit.run_after_text_input(Editor_state, 'k')
1534 edit.run_after_text_input(Editor_state, 'l')
1535 check_eq(Editor_state.screen_top1.line, 2, 'F - test_typing_on_bottom_line_scrolls_down/screen_top')
1536 check_eq(Editor_state.cursor1.line, 3, 'F - test_typing_on_bottom_line_scrolls_down/cursor:line')
1537 check_eq(Editor_state.cursor1.pos, 7, 'F - test_typing_on_bottom_line_scrolls_down/cursor:pos')
1538 y = Editor_state.top
1539 App.screen.check(y, 'def', 'F - test_typing_on_bottom_line_scrolls_down/screen:1')
1540 y = y + Editor_state.line_height
1541 App.screen.check(y, 'ghij', 'F - test_typing_on_bottom_line_scrolls_down/screen:2')
1542 y = y + Editor_state.line_height
1543 App.screen.check(y, 'kl', 'F - test_typing_on_bottom_line_scrolls_down/screen:3')
1546 function test_left_arrow_scrolls_up_in_wrapped_line()
1547 io.write('\ntest_left_arrow_scrolls_up_in_wrapped_line')
1548 -- display lines starting from second screen line of a line
1549 App.screen.init{width=Editor_state.left+30, height=60}
1550 Editor_state = edit.initialize_test_state()
1551 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1552 Text.redraw_all(Editor_state)
1553 Editor_state.screen_top1 = {line=3, pos=5}
1554 Editor_state.screen_bottom1 = {}
1555 -- cursor is at top of screen
1556 Editor_state.cursor1 = {line=3, pos=5}
1557 edit.draw(Editor_state)
1558 local y = Editor_state.top
1559 App.screen.check(y, 'jkl', 'F - test_left_arrow_scrolls_up_in_wrapped_line/baseline/screen:1')
1560 y = y + Editor_state.line_height
1561 App.screen.check(y, 'mno', 'F - test_left_arrow_scrolls_up_in_wrapped_line/baseline/screen:2')
1562 -- after hitting the left arrow the screen scrolls up to first screen line
1563 edit.run_after_keychord(Editor_state, 'left')
1564 y = Editor_state.top
1565 App.screen.check(y, 'ghi ', 'F - test_left_arrow_scrolls_up_in_wrapped_line/screen:1')
1566 y = y + Editor_state.line_height
1567 App.screen.check(y, 'jkl', 'F - test_left_arrow_scrolls_up_in_wrapped_line/screen:2')
1568 y = y + Editor_state.line_height
1569 App.screen.check(y, 'mno', 'F - test_left_arrow_scrolls_up_in_wrapped_line/screen:3')
1570 check_eq(Editor_state.screen_top1.line, 3, 'F - test_left_arrow_scrolls_up_in_wrapped_line/screen_top')
1571 check_eq(Editor_state.screen_top1.pos, 1, 'F - test_left_arrow_scrolls_up_in_wrapped_line/screen_top')
1572 check_eq(Editor_state.cursor1.line, 3, 'F - test_left_arrow_scrolls_up_in_wrapped_line/cursor:line')
1573 check_eq(Editor_state.cursor1.pos, 4, 'F - test_left_arrow_scrolls_up_in_wrapped_line/cursor:pos')
1576 function test_right_arrow_scrolls_down_in_wrapped_line()
1577 io.write('\ntest_right_arrow_scrolls_down_in_wrapped_line')
1578 -- display the first three lines with the cursor on the bottom line
1579 App.screen.init{width=Editor_state.left+30, height=60}
1580 Editor_state = edit.initialize_test_state()
1581 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1582 Text.redraw_all(Editor_state)
1583 Editor_state.screen_top1 = {line=1, pos=1}
1584 Editor_state.screen_bottom1 = {}
1585 -- cursor is at bottom right of screen
1586 Editor_state.cursor1 = {line=3, pos=5}
1587 edit.draw(Editor_state)
1588 local y = Editor_state.top
1589 App.screen.check(y, 'abc', 'F - test_right_arrow_scrolls_down_in_wrapped_line/baseline/screen:1')
1590 y = y + Editor_state.line_height
1591 App.screen.check(y, 'def', 'F - test_right_arrow_scrolls_down_in_wrapped_line/baseline/screen:2')
1592 y = y + Editor_state.line_height
1593 App.screen.check(y, 'ghi ', 'F - test_right_arrow_scrolls_down_in_wrapped_line/baseline/screen:3') -- line wrapping includes trailing whitespace
1594 -- after hitting the right arrow the screen scrolls down by one line
1595 edit.run_after_keychord(Editor_state, 'right')
1596 check_eq(Editor_state.screen_top1.line, 2, 'F - test_right_arrow_scrolls_down_in_wrapped_line/screen_top')
1597 check_eq(Editor_state.cursor1.line, 3, 'F - test_right_arrow_scrolls_down_in_wrapped_line/cursor:line')
1598 check_eq(Editor_state.cursor1.pos, 6, 'F - test_right_arrow_scrolls_down_in_wrapped_line/cursor:pos')
1599 y = Editor_state.top
1600 App.screen.check(y, 'def', 'F - test_right_arrow_scrolls_down_in_wrapped_line/screen:1')
1601 y = y + Editor_state.line_height
1602 App.screen.check(y, 'ghi ', 'F - test_right_arrow_scrolls_down_in_wrapped_line/screen:2')
1603 y = y + Editor_state.line_height
1604 App.screen.check(y, 'jkl', 'F - test_right_arrow_scrolls_down_in_wrapped_line/screen:3')
1607 function test_home_scrolls_up_in_wrapped_line()
1608 io.write('\ntest_home_scrolls_up_in_wrapped_line')
1609 -- display lines starting from second screen line of a line
1610 App.screen.init{width=Editor_state.left+30, height=60}
1611 Editor_state = edit.initialize_test_state()
1612 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1613 Text.redraw_all(Editor_state)
1614 Editor_state.screen_top1 = {line=3, pos=5}
1615 Editor_state.screen_bottom1 = {}
1616 -- cursor is at top of screen
1617 Editor_state.cursor1 = {line=3, pos=5}
1618 edit.draw(Editor_state)
1619 local y = Editor_state.top
1620 App.screen.check(y, 'jkl', 'F - test_home_scrolls_up_in_wrapped_line/baseline/screen:1')
1621 y = y + Editor_state.line_height
1622 App.screen.check(y, 'mno', 'F - test_home_scrolls_up_in_wrapped_line/baseline/screen:2')
1623 -- after hitting home the screen scrolls up to first screen line
1624 edit.run_after_keychord(Editor_state, 'home')
1625 y = Editor_state.top
1626 App.screen.check(y, 'ghi ', 'F - test_home_scrolls_up_in_wrapped_line/screen:1')
1627 y = y + Editor_state.line_height
1628 App.screen.check(y, 'jkl', 'F - test_home_scrolls_up_in_wrapped_line/screen:2')
1629 y = y + Editor_state.line_height
1630 App.screen.check(y, 'mno', 'F - test_home_scrolls_up_in_wrapped_line/screen:3')
1631 check_eq(Editor_state.screen_top1.line, 3, 'F - test_home_scrolls_up_in_wrapped_line/screen_top')
1632 check_eq(Editor_state.screen_top1.pos, 1, 'F - test_home_scrolls_up_in_wrapped_line/screen_top')
1633 check_eq(Editor_state.cursor1.line, 3, 'F - test_home_scrolls_up_in_wrapped_line/cursor:line')
1634 check_eq(Editor_state.cursor1.pos, 1, 'F - test_home_scrolls_up_in_wrapped_line/cursor:pos')
1637 function test_end_scrolls_down_in_wrapped_line()
1638 io.write('\ntest_end_scrolls_down_in_wrapped_line')
1639 -- display the first three lines with the cursor on the bottom line
1640 App.screen.init{width=Editor_state.left+30, height=60}
1641 Editor_state = edit.initialize_test_state()
1642 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1643 Text.redraw_all(Editor_state)
1644 Editor_state.screen_top1 = {line=1, pos=1}
1645 Editor_state.screen_bottom1 = {}
1646 -- cursor is at bottom right of screen
1647 Editor_state.cursor1 = {line=3, pos=5}
1648 edit.draw(Editor_state)
1649 local y = Editor_state.top
1650 App.screen.check(y, 'abc', 'F - test_end_scrolls_down_in_wrapped_line/baseline/screen:1')
1651 y = y + Editor_state.line_height
1652 App.screen.check(y, 'def', 'F - test_end_scrolls_down_in_wrapped_line/baseline/screen:2')
1653 y = y + Editor_state.line_height
1654 App.screen.check(y, 'ghi ', 'F - test_end_scrolls_down_in_wrapped_line/baseline/screen:3') -- line wrapping includes trailing whitespace
1655 -- after hitting end the screen scrolls down by one line
1656 edit.run_after_keychord(Editor_state, 'end')
1657 check_eq(Editor_state.screen_top1.line, 2, 'F - test_end_scrolls_down_in_wrapped_line/screen_top')
1658 check_eq(Editor_state.cursor1.line, 3, 'F - test_end_scrolls_down_in_wrapped_line/cursor:line')
1659 check_eq(Editor_state.cursor1.pos, 8, 'F - test_end_scrolls_down_in_wrapped_line/cursor:pos')
1660 y = Editor_state.top
1661 App.screen.check(y, 'def', 'F - test_end_scrolls_down_in_wrapped_line/screen:1')
1662 y = y + Editor_state.line_height
1663 App.screen.check(y, 'ghi ', 'F - test_end_scrolls_down_in_wrapped_line/screen:2')
1664 y = y + Editor_state.line_height
1665 App.screen.check(y, 'jkl', 'F - test_end_scrolls_down_in_wrapped_line/screen:3')
1668 function test_position_cursor_on_recently_edited_wrapping_line()
1669 -- draw a line wrapping over 2 screen lines
1670 io.write('\ntest_position_cursor_on_recently_edited_wrapping_line')
1671 App.screen.init{width=100, height=200}
1672 Editor_state = edit.initialize_test_state()
1673 Editor_state.lines = load_array{'abc def ghi jkl mno pqr ', 'xyz'}
1674 Text.redraw_all(Editor_state)
1675 Editor_state.cursor1 = {line=1, pos=25}
1676 Editor_state.screen_top1 = {line=1, pos=1}
1677 Editor_state.screen_bottom1 = {}
1678 edit.draw(Editor_state)
1679 local y = Editor_state.top
1680 App.screen.check(y, 'abc def ghi ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline1/screen:1')
1681 y = y + Editor_state.line_height
1682 App.screen.check(y, 'jkl mno pqr ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline1/screen:2')
1683 y = y + Editor_state.line_height
1684 App.screen.check(y, 'xyz', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline1/screen:3')
1685 -- add to the line until it's wrapping over 3 screen lines
1686 edit.run_after_text_input(Editor_state, 's')
1687 edit.run_after_text_input(Editor_state, 't')
1688 edit.run_after_text_input(Editor_state, 'u')
1689 check_eq(Editor_state.cursor1.pos, 28, 'F - test_position_cursor_on_recently_edited_wrapping_line/cursor:pos')
1690 y = Editor_state.top
1691 App.screen.check(y, 'abc def ghi ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:1')
1692 y = y + Editor_state.line_height
1693 App.screen.check(y, 'jkl mno pqr ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:2')
1694 y = y + Editor_state.line_height
1695 App.screen.check(y, 'stu', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:3')
1696 -- try to move the cursor earlier in the third screen line by clicking the mouse
1697 edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+Editor_state.line_height*2+5, 1)
1698 -- cursor should move
1699 check_eq(Editor_state.cursor1.line, 1, 'F - test_position_cursor_on_recently_edited_wrapping_line/cursor:line')
1700 check_eq(Editor_state.cursor1.pos, 26, 'F - test_position_cursor_on_recently_edited_wrapping_line/cursor:pos')
1703 function test_backspace_can_scroll_up()
1704 io.write('\ntest_backspace_can_scroll_up')
1705 -- display the lines 2/3/4 with the cursor on line 2
1706 App.screen.init{width=120, height=60}
1707 Editor_state = edit.initialize_test_state()
1708 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
1709 Text.redraw_all(Editor_state)
1710 Editor_state.cursor1 = {line=2, pos=1}
1711 Editor_state.screen_top1 = {line=2, pos=1}
1712 Editor_state.screen_bottom1 = {}
1713 edit.draw(Editor_state)
1714 local y = Editor_state.top
1715 App.screen.check(y, 'def', 'F - test_backspace_can_scroll_up/baseline/screen:1')
1716 y = y + Editor_state.line_height
1717 App.screen.check(y, 'ghi', 'F - test_backspace_can_scroll_up/baseline/screen:2')
1718 y = y + Editor_state.line_height
1719 App.screen.check(y, 'jkl', 'F - test_backspace_can_scroll_up/baseline/screen:3')
1720 -- after hitting backspace the screen scrolls up by one line
1721 edit.run_after_keychord(Editor_state, 'backspace')
1722 check_eq(Editor_state.screen_top1.line, 1, 'F - test_backspace_can_scroll_up/screen_top')
1723 check_eq(Editor_state.cursor1.line, 1, 'F - test_backspace_can_scroll_up/cursor')
1724 y = Editor_state.top
1725 App.screen.check(y, 'abcdef', 'F - test_backspace_can_scroll_up/screen:1')
1726 y = y + Editor_state.line_height
1727 App.screen.check(y, 'ghi', 'F - test_backspace_can_scroll_up/screen:2')
1728 y = y + Editor_state.line_height
1729 App.screen.check(y, 'jkl', 'F - test_backspace_can_scroll_up/screen:3')
1732 function test_backspace_can_scroll_up_screen_line()
1733 io.write('\ntest_backspace_can_scroll_up_screen_line')
1734 -- display lines starting from second screen line of a line
1735 App.screen.init{width=Editor_state.left+30, height=60}
1736 Editor_state = edit.initialize_test_state()
1737 Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
1738 Text.redraw_all(Editor_state)
1739 Editor_state.cursor1 = {line=3, pos=5}
1740 Editor_state.screen_top1 = {line=3, pos=5}
1741 Editor_state.screen_bottom1 = {}
1742 edit.draw(Editor_state)
1743 local y = Editor_state.top
1744 App.screen.check(y, 'jkl', 'F - test_backspace_can_scroll_up_screen_line/baseline/screen:1')
1745 y = y + Editor_state.line_height
1746 App.screen.check(y, 'mno', 'F - test_backspace_can_scroll_up_screen_line/baseline/screen:2')
1747 -- after hitting backspace the screen scrolls up by one screen line
1748 edit.run_after_keychord(Editor_state, 'backspace')
1749 y = Editor_state.top
1750 App.screen.check(y, 'ghij', 'F - test_backspace_can_scroll_up_screen_line/screen:1')
1751 y = y + Editor_state.line_height
1752 App.screen.check(y, 'kl', 'F - test_backspace_can_scroll_up_screen_line/screen:2')
1753 y = y + Editor_state.line_height
1754 App.screen.check(y, 'mno', 'F - test_backspace_can_scroll_up_screen_line/screen:3')
1755 check_eq(Editor_state.screen_top1.line, 3, 'F - test_backspace_can_scroll_up_screen_line/screen_top')
1756 check_eq(Editor_state.screen_top1.pos, 1, 'F - test_backspace_can_scroll_up_screen_line/screen_top')
1757 check_eq(Editor_state.cursor1.line, 3, 'F - test_backspace_can_scroll_up_screen_line/cursor:line')
1758 check_eq(Editor_state.cursor1.pos, 4, 'F - test_backspace_can_scroll_up_screen_line/cursor:pos')
1761 function test_backspace_past_line_boundary()
1762 io.write('\ntest_backspace_past_line_boundary')
1763 -- position cursor at start of a (non-first) line
1764 App.screen.init{width=Editor_state.left+30, height=60}
1765 Editor_state = edit.initialize_test_state()
1766 Editor_state.lines = load_array{'abc', 'def'}
1767 Text.redraw_all(Editor_state)
1768 Editor_state.cursor1 = {line=2, pos=1}
1769 -- backspace joins with previous line
1770 edit.run_after_keychord(Editor_state, 'backspace')
1771 check_eq(Editor_state.lines[1].data, 'abcdef', "F - test_backspace_past_line_boundary")
1774 -- some tests for operating over selections created using Shift- chords
1775 -- we're just testing delete_selection, and it works the same for all keys
1777 function test_backspace_over_selection()
1778 io.write('\ntest_backspace_over_selection')
1779 -- select just one character within a line with cursor before selection
1780 App.screen.init{width=Editor_state.left+30, height=60}
1781 Editor_state = edit.initialize_test_state()
1782 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
1783 Text.redraw_all(Editor_state)
1784 Editor_state.cursor1 = {line=1, pos=1}
1785 Editor_state.selection1 = {line=1, pos=2}
1786 -- backspace deletes the selected character, even though it's after the cursor
1787 edit.run_after_keychord(Editor_state, 'backspace')
1788 check_eq(Editor_state.lines[1].data, 'bc', "F - test_backspace_over_selection/data")
1789 -- cursor (remains) at start of selection
1790 check_eq(Editor_state.cursor1.line, 1, "F - test_backspace_over_selection/cursor:line")
1791 check_eq(Editor_state.cursor1.pos, 1, "F - test_backspace_over_selection/cursor:pos")
1792 -- selection is cleared
1793 check_nil(Editor_state.selection1.line, "F - test_backspace_over_selection/selection")
1796 function test_backspace_over_selection_reverse()
1797 io.write('\ntest_backspace_over_selection_reverse')
1798 -- select just one character within a line with cursor after selection
1799 App.screen.init{width=Editor_state.left+30, height=60}
1800 Editor_state = edit.initialize_test_state()
1801 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
1802 Text.redraw_all(Editor_state)
1803 Editor_state.cursor1 = {line=1, pos=2}
1804 Editor_state.selection1 = {line=1, pos=1}
1805 -- backspace deletes the selected character
1806 edit.run_after_keychord(Editor_state, 'backspace')
1807 check_eq(Editor_state.lines[1].data, 'bc', "F - test_backspace_over_selection_reverse/data")
1808 -- cursor moves to start of selection
1809 check_eq(Editor_state.cursor1.line, 1, "F - test_backspace_over_selection_reverse/cursor:line")
1810 check_eq(Editor_state.cursor1.pos, 1, "F - test_backspace_over_selection_reverse/cursor:pos")
1811 -- selection is cleared
1812 check_nil(Editor_state.selection1.line, "F - test_backspace_over_selection_reverse/selection")
1815 function test_backspace_over_multiple_lines()
1816 io.write('\ntest_backspace_over_multiple_lines')
1817 -- select just one character within a line with cursor after selection
1818 App.screen.init{width=Editor_state.left+30, height=60}
1819 Editor_state = edit.initialize_test_state()
1820 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
1821 Text.redraw_all(Editor_state)
1822 Editor_state.cursor1 = {line=1, pos=2}
1823 Editor_state.selection1 = {line=4, pos=2}
1824 -- backspace deletes the region and joins the remaining portions of lines on either side
1825 edit.run_after_keychord(Editor_state, 'backspace')
1826 check_eq(Editor_state.lines[1].data, 'akl', "F - test_backspace_over_multiple_lines/data:1")
1827 check_eq(Editor_state.lines[2].data, 'mno', "F - test_backspace_over_multiple_lines/data:2")
1828 -- cursor remains at start of selection
1829 check_eq(Editor_state.cursor1.line, 1, "F - test_backspace_over_multiple_lines/cursor:line")
1830 check_eq(Editor_state.cursor1.pos, 2, "F - test_backspace_over_multiple_lines/cursor:pos")
1831 -- selection is cleared
1832 check_nil(Editor_state.selection1.line, "F - test_backspace_over_multiple_lines/selection")
1835 function test_backspace_to_end_of_line()
1836 io.write('\ntest_backspace_to_end_of_line')
1837 -- select region from cursor to end of line
1838 App.screen.init{width=Editor_state.left+30, height=60}
1839 Editor_state = edit.initialize_test_state()
1840 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
1841 Text.redraw_all(Editor_state)
1842 Editor_state.cursor1 = {line=1, pos=2}
1843 Editor_state.selection1 = {line=1, pos=4}
1844 -- backspace deletes rest of line without joining to any other line
1845 edit.run_after_keychord(Editor_state, 'backspace')
1846 check_eq(Editor_state.lines[1].data, 'a', "F - test_backspace_to_start_of_line/data:1")
1847 check_eq(Editor_state.lines[2].data, 'def', "F - test_backspace_to_start_of_line/data:2")
1848 -- cursor remains at start of selection
1849 check_eq(Editor_state.cursor1.line, 1, "F - test_backspace_to_start_of_line/cursor:line")
1850 check_eq(Editor_state.cursor1.pos, 2, "F - test_backspace_to_start_of_line/cursor:pos")
1851 -- selection is cleared
1852 check_nil(Editor_state.selection1.line, "F - test_backspace_to_start_of_line/selection")
1855 function test_backspace_to_start_of_line()
1856 io.write('\ntest_backspace_to_start_of_line')
1857 -- select region from cursor to start of line
1858 App.screen.init{width=Editor_state.left+30, height=60}
1859 Editor_state = edit.initialize_test_state()
1860 Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
1861 Text.redraw_all(Editor_state)
1862 Editor_state.cursor1 = {line=2, pos=1}
1863 Editor_state.selection1 = {line=2, pos=3}
1864 -- backspace deletes beginning of line without joining to any other line
1865 edit.run_after_keychord(Editor_state, 'backspace')
1866 check_eq(Editor_state.lines[1].data, 'abc', "F - test_backspace_to_start_of_line/data:1")
1867 check_eq(Editor_state.lines[2].data, 'f', "F - test_backspace_to_start_of_line/data:2")
1868 -- cursor remains at start of selection
1869 check_eq(Editor_state.cursor1.line, 2, "F - test_backspace_to_start_of_line/cursor:line")
1870 check_eq(Editor_state.cursor1.pos, 1, "F - test_backspace_to_start_of_line/cursor:pos")
1871 -- selection is cleared
1872 check_nil(Editor_state.selection1.line, "F - test_backspace_to_start_of_line/selection")
1875 function test_undo_insert_text()
1876 io.write('\ntest_undo_insert_text')
1877 App.screen.init{width=120, height=60}
1878 Editor_state = edit.initialize_test_state()
1879 Editor_state.lines = load_array{'abc', 'def', 'xyz'}
1880 Text.redraw_all(Editor_state)
1881 Editor_state.cursor1 = {line=2, pos=4}
1882 Editor_state.screen_top1 = {line=1, pos=1}
1883 Editor_state.screen_bottom1 = {}
1884 -- insert a character
1885 edit.draw(Editor_state)
1886 edit.run_after_text_input(Editor_state, 'g')
1887 check_eq(Editor_state.cursor1.line, 2, 'F - test_undo_insert_text/baseline/cursor:line')
1888 check_eq(Editor_state.cursor1.pos, 5, 'F - test_undo_insert_text/baseline/cursor:pos')
1889 check_nil(Editor_state.selection1.line, 'F - test_undo_insert_text/baseline/selection:line')
1890 check_nil(Editor_state.selection1.pos, 'F - test_undo_insert_text/baseline/selection:pos')
1891 local y = Editor_state.top
1892 App.screen.check(y, 'abc', 'F - test_undo_insert_text/baseline/screen:1')
1893 y = y + Editor_state.line_height
1894 App.screen.check(y, 'defg', 'F - test_undo_insert_text/baseline/screen:2')
1895 y = y + Editor_state.line_height
1896 App.screen.check(y, 'xyz', 'F - test_undo_insert_text/baseline/screen:3')
1897 -- undo
1898 edit.run_after_keychord(Editor_state, 'C-z')
1899 check_eq(Editor_state.cursor1.line, 2, 'F - test_undo_insert_text/cursor:line')
1900 check_eq(Editor_state.cursor1.pos, 4, 'F - test_undo_insert_text/cursor:pos')
1901 check_nil(Editor_state.selection1.line, 'F - test_undo_insert_text/selection:line')
1902 check_nil(Editor_state.selection1.pos, 'F - test_undo_insert_text/selection:pos')
1903 y = Editor_state.top
1904 App.screen.check(y, 'abc', 'F - test_undo_insert_text/screen:1')
1905 y = y + Editor_state.line_height
1906 App.screen.check(y, 'def', 'F - test_undo_insert_text/screen:2')
1907 y = y + Editor_state.line_height
1908 App.screen.check(y, 'xyz', 'F - test_undo_insert_text/screen:3')
1911 function test_undo_delete_text()
1912 io.write('\ntest_undo_delete_text')
1913 App.screen.init{width=120, height=60}
1914 Editor_state = edit.initialize_test_state()
1915 Editor_state.lines = load_array{'abc', 'defg', 'xyz'}
1916 Text.redraw_all(Editor_state)
1917 Editor_state.cursor1 = {line=2, pos=5}
1918 Editor_state.screen_top1 = {line=1, pos=1}
1919 Editor_state.screen_bottom1 = {}
1920 -- delete a character
1921 edit.run_after_keychord(Editor_state, 'backspace')
1922 check_eq(Editor_state.cursor1.line, 2, 'F - test_undo_delete_text/baseline/cursor:line')
1923 check_eq(Editor_state.cursor1.pos, 4, 'F - test_undo_delete_text/baseline/cursor:pos')
1924 check_nil(Editor_state.selection1.line, 'F - test_undo_delete_text/baseline/selection:line')
1925 check_nil(Editor_state.selection1.pos, 'F - test_undo_delete_text/baseline/selection:pos')
1926 local y = Editor_state.top
1927 App.screen.check(y, 'abc', 'F - test_undo_delete_text/baseline/screen:1')
1928 y = y + Editor_state.line_height
1929 App.screen.check(y, 'def', 'F - test_undo_delete_text/baseline/screen:2')
1930 y = y + Editor_state.line_height
1931 App.screen.check(y, 'xyz', 'F - test_undo_delete_text/baseline/screen:3')
1932 -- undo
1933 --? -- after undo, the backspaced key is selected
1934 edit.run_after_keychord(Editor_state, 'C-z')
1935 check_eq(Editor_state.cursor1.line, 2, 'F - test_undo_delete_text/cursor:line')
1936 check_eq(Editor_state.cursor1.pos, 5, 'F - test_undo_delete_text/cursor:pos')
1937 check_nil(Editor_state.selection1.line, 'F - test_undo_delete_text/selection:line')
1938 check_nil(Editor_state.selection1.pos, 'F - test_undo_delete_text/selection:pos')
1939 --? check_eq(Editor_state.selection1.line, 2, 'F - test_undo_delete_text/selection:line')
1940 --? check_eq(Editor_state.selection1.pos, 4, 'F - test_undo_delete_text/selection:pos')
1941 y = Editor_state.top
1942 App.screen.check(y, 'abc', 'F - test_undo_delete_text/screen:1')
1943 y = y + Editor_state.line_height
1944 App.screen.check(y, 'defg', 'F - test_undo_delete_text/screen:2')
1945 y = y + Editor_state.line_height
1946 App.screen.check(y, 'xyz', 'F - test_undo_delete_text/screen:3')
1949 function test_undo_restores_selection()
1950 io.write('\ntest_undo_restores_selection')
1951 -- display a line of text with some part selected
1952 App.screen.init{width=75, height=80}
1953 Editor_state = edit.initialize_test_state()
1954 Editor_state.lines = load_array{'abc'}
1955 Text.redraw_all(Editor_state)
1956 Editor_state.cursor1 = {line=1, pos=1}
1957 Editor_state.selection1 = {line=1, pos=2}
1958 Editor_state.screen_top1 = {line=1, pos=1}
1959 Editor_state.screen_bottom1 = {}
1960 edit.draw(Editor_state)
1961 -- delete selected text
1962 edit.run_after_text_input(Editor_state, 'x')
1963 check_eq(Editor_state.lines[1].data, 'xbc', 'F - test_undo_restores_selection/baseline')
1964 check_nil(Editor_state.selection1.line, 'F - test_undo_restores_selection/baseline:selection')
1965 -- undo
1966 edit.run_after_keychord(Editor_state, 'C-z')
1967 edit.run_after_keychord(Editor_state, 'C-z')
1968 -- selection is restored
1969 check_eq(Editor_state.selection1.line, 1, 'F - test_undo_restores_selection/line')
1970 check_eq(Editor_state.selection1.pos, 2, 'F - test_undo_restores_selection/pos')
1973 function test_search()
1974 io.write('\ntest_search')
1975 App.screen.init{width=120, height=60}
1976 Editor_state = edit.initialize_test_state()
1977 Editor_state.lines = load_array{'```lines', '```', 'def', 'ghi', 'deg'}
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, 'F - test_search/1/cursor:line')
1988 check_eq(Editor_state.cursor1.pos, 1, 'F - test_search/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, 'F - test_search/2/cursor:line')
1998 check_eq(Editor_state.cursor1.pos, 1, 'F - test_search/2/cursor:pos')
2001 function test_search_upwards()
2002 io.write('\ntest_search_upwards')
2003 App.screen.init{width=120, height=60}
2004 Editor_state = edit.initialize_test_state()
2005 Editor_state.lines = load_array{'abc abd'}
2006 Text.redraw_all(Editor_state)
2007 Editor_state.cursor1 = {line=1, pos=2}
2008 Editor_state.screen_top1 = {line=1, pos=1}
2009 Editor_state.screen_bottom1 = {}
2010 edit.draw(Editor_state)
2011 -- search for a string
2012 edit.run_after_keychord(Editor_state, 'C-f')
2013 edit.run_after_text_input(Editor_state, 'a')
2014 -- search for previous occurrence
2015 edit.run_after_keychord(Editor_state, 'up')
2016 check_eq(Editor_state.cursor1.line, 1, 'F - test_search_upwards/2/cursor:line')
2017 check_eq(Editor_state.cursor1.pos, 1, 'F - test_search_upwards/2/cursor:pos')
2020 function test_search_wrap()
2021 io.write('\ntest_search_wrap')
2022 App.screen.init{width=120, height=60}
2023 Editor_state = edit.initialize_test_state()
2024 Editor_state.lines = load_array{'abc'}
2025 Text.redraw_all(Editor_state)
2026 Editor_state.cursor1 = {line=1, pos=3}
2027 Editor_state.screen_top1 = {line=1, pos=1}
2028 Editor_state.screen_bottom1 = {}
2029 edit.draw(Editor_state)
2030 -- search for a string
2031 edit.run_after_keychord(Editor_state, 'C-f')
2032 edit.run_after_text_input(Editor_state, 'a')
2033 edit.run_after_keychord(Editor_state, 'return')
2034 -- cursor wraps
2035 check_eq(Editor_state.cursor1.line, 1, 'F - test_search_wrap/1/cursor:line')
2036 check_eq(Editor_state.cursor1.pos, 1, 'F - test_search_wrap/1/cursor:pos')
2039 function test_search_wrap_upwards()
2040 io.write('\ntest_search_wrap_upwards')
2041 App.screen.init{width=120, height=60}
2042 Editor_state = edit.initialize_test_state()
2043 Editor_state.lines = load_array{'abc abd'}
2044 Text.redraw_all(Editor_state)
2045 Editor_state.cursor1 = {line=1, pos=1}
2046 Editor_state.screen_top1 = {line=1, pos=1}
2047 Editor_state.screen_bottom1 = {}
2048 edit.draw(Editor_state)
2049 -- search upwards for a string
2050 edit.run_after_keychord(Editor_state, 'C-f')
2051 edit.run_after_text_input(Editor_state, 'a')
2052 edit.run_after_keychord(Editor_state, 'up')
2053 -- cursor wraps
2054 check_eq(Editor_state.cursor1.line, 1, 'F - test_search_wrap_upwards/1/cursor:line')
2055 check_eq(Editor_state.cursor1.pos, 5, 'F - test_search_wrap_upwards/1/cursor:pos')