1 -- major tests for text editing flows
2 -- Arguably this should be called edit_tests.lua,
3 -- but that would mess up the git blame at this point.
5 function test_initial_state()
6 App
.screen
.init
{width
=120, height
=60}
7 Editor_state
= edit
.initialize_test_state()
8 Editor_state
.lines
= load_array
{}
9 Text
.redraw_all(Editor_state
)
10 edit
.draw(Editor_state
)
11 check_eq(#Editor_state
.lines
, 1, '#lines')
12 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
13 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
14 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
15 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
18 function test_backspace_from_start_of_final_line()
19 -- display final line of text with cursor at start of it
20 App
.screen
.init
{width
=120, height
=60}
21 Editor_state
= edit
.initialize_test_state()
22 Editor_state
.lines
= load_array
{'abc', 'def'}
23 Editor_state
.screen_top1
= {line
=2, pos
=1}
24 Editor_state
.cursor1
= {line
=2, pos
=1}
25 Text
.redraw_all(Editor_state
)
26 -- backspace scrolls up
27 edit
.run_after_keychord(Editor_state
, 'backspace')
28 check_eq(#Editor_state
.lines
, 1, '#lines')
29 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
30 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
33 function test_insert_first_character()
34 App
.screen
.init
{width
=120, height
=60}
35 Editor_state
= edit
.initialize_test_state()
36 Editor_state
.lines
= load_array
{}
37 Text
.redraw_all(Editor_state
)
38 edit
.draw(Editor_state
)
39 edit
.run_after_text_input(Editor_state
, 'a')
40 local y
= Editor_state
.top
41 App
.screen
.check(y
, 'a', 'screen:1')
44 function test_press_ctrl()
45 -- press ctrl while the cursor is on text
46 App
.screen
.init
{width
=50, height
=80}
47 Editor_state
= edit
.initialize_test_state()
48 Editor_state
.lines
= load_array
{''}
49 Text
.redraw_all(Editor_state
)
50 Editor_state
.cursor1
= {line
=1, pos
=1}
51 Editor_state
.screen_top1
= {line
=1, pos
=1}
52 Editor_state
.screen_bottom1
= {}
53 edit
.run_after_keychord(Editor_state
, 'C-m')
56 function test_move_left()
57 App
.screen
.init
{width
=120, height
=60}
58 Editor_state
= edit
.initialize_test_state()
59 Editor_state
.lines
= load_array
{'a'}
60 Text
.redraw_all(Editor_state
)
61 Editor_state
.cursor1
= {line
=1, pos
=2}
62 edit
.draw(Editor_state
)
63 edit
.run_after_keychord(Editor_state
, 'left')
64 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
67 function test_move_right()
68 App
.screen
.init
{width
=120, height
=60}
69 Editor_state
= edit
.initialize_test_state()
70 Editor_state
.lines
= load_array
{'a'}
71 Text
.redraw_all(Editor_state
)
72 Editor_state
.cursor1
= {line
=1, pos
=1}
73 edit
.draw(Editor_state
)
74 edit
.run_after_keychord(Editor_state
, 'right')
75 check_eq(Editor_state
.cursor1
.pos
, 2, 'check')
78 function test_move_left_to_previous_line()
79 App
.screen
.init
{width
=120, height
=60}
80 Editor_state
= edit
.initialize_test_state()
81 Editor_state
.lines
= load_array
{'abc', 'def'}
82 Text
.redraw_all(Editor_state
)
83 Editor_state
.cursor1
= {line
=2, pos
=1}
84 edit
.draw(Editor_state
)
85 edit
.run_after_keychord(Editor_state
, 'left')
86 check_eq(Editor_state
.cursor1
.line
, 1, 'line')
87 check_eq(Editor_state
.cursor1
.pos
, 4, 'pos') -- past end of line
90 function test_move_right_to_next_line()
91 App
.screen
.init
{width
=120, height
=60}
92 Editor_state
= edit
.initialize_test_state()
93 Editor_state
.lines
= load_array
{'abc', 'def'}
94 Text
.redraw_all(Editor_state
)
95 Editor_state
.cursor1
= {line
=1, pos
=4} -- past end of line
96 edit
.draw(Editor_state
)
97 edit
.run_after_keychord(Editor_state
, 'right')
98 check_eq(Editor_state
.cursor1
.line
, 2, 'line')
99 check_eq(Editor_state
.cursor1
.pos
, 1, 'pos')
102 function test_move_to_start_of_word()
103 App
.screen
.init
{width
=120, height
=60}
104 Editor_state
= edit
.initialize_test_state()
105 Editor_state
.lines
= load_array
{'abc'}
106 Text
.redraw_all(Editor_state
)
107 Editor_state
.cursor1
= {line
=1, pos
=3}
108 edit
.draw(Editor_state
)
109 edit
.run_after_keychord(Editor_state
, 'M-left')
110 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
113 function test_move_to_start_of_previous_word()
114 App
.screen
.init
{width
=120, height
=60}
115 Editor_state
= edit
.initialize_test_state()
116 Editor_state
.lines
= load_array
{'abc def'}
117 Text
.redraw_all(Editor_state
)
118 Editor_state
.cursor1
= {line
=1, pos
=4} -- at the space between words
119 edit
.draw(Editor_state
)
120 edit
.run_after_keychord(Editor_state
, 'M-left')
121 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
124 function test_skip_to_previous_word()
125 App
.screen
.init
{width
=120, height
=60}
126 Editor_state
= edit
.initialize_test_state()
127 Editor_state
.lines
= load_array
{'abc def'}
128 Text
.redraw_all(Editor_state
)
129 Editor_state
.cursor1
= {line
=1, pos
=5} -- at the start of second word
130 edit
.draw(Editor_state
)
131 edit
.run_after_keychord(Editor_state
, 'M-left')
132 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
135 function test_skip_past_tab_to_previous_word()
136 App
.screen
.init
{width
=120, height
=60}
137 Editor_state
= edit
.initialize_test_state()
138 Editor_state
.lines
= load_array
{'abc def\tghi'}
139 Text
.redraw_all(Editor_state
)
140 Editor_state
.cursor1
= {line
=1, pos
=10} -- within third word
141 edit
.draw(Editor_state
)
142 edit
.run_after_keychord(Editor_state
, 'M-left')
143 check_eq(Editor_state
.cursor1
.pos
, 9, 'check')
146 function test_skip_multiple_spaces_to_previous_word()
147 App
.screen
.init
{width
=120, height
=60}
148 Editor_state
= edit
.initialize_test_state()
149 Editor_state
.lines
= load_array
{'abc def'}
150 Text
.redraw_all(Editor_state
)
151 Editor_state
.cursor1
= {line
=1, pos
=6} -- at the start of second word
152 edit
.draw(Editor_state
)
153 edit
.run_after_keychord(Editor_state
, 'M-left')
154 check_eq(Editor_state
.cursor1
.pos
, 1, 'check')
157 function test_move_to_start_of_word_on_previous_line()
158 App
.screen
.init
{width
=120, height
=60}
159 Editor_state
= edit
.initialize_test_state()
160 Editor_state
.lines
= load_array
{'abc def', 'ghi'}
161 Text
.redraw_all(Editor_state
)
162 Editor_state
.cursor1
= {line
=2, pos
=1}
163 edit
.draw(Editor_state
)
164 edit
.run_after_keychord(Editor_state
, 'M-left')
165 check_eq(Editor_state
.cursor1
.line
, 1, 'line')
166 check_eq(Editor_state
.cursor1
.pos
, 5, 'pos')
169 function test_move_past_end_of_word()
170 App
.screen
.init
{width
=120, height
=60}
171 Editor_state
= edit
.initialize_test_state()
172 Editor_state
.lines
= load_array
{'abc def'}
173 Text
.redraw_all(Editor_state
)
174 Editor_state
.cursor1
= {line
=1, pos
=1}
175 edit
.draw(Editor_state
)
176 edit
.run_after_keychord(Editor_state
, 'M-right')
177 check_eq(Editor_state
.cursor1
.pos
, 4, 'check')
180 function test_skip_to_next_word()
181 App
.screen
.init
{width
=120, height
=60}
182 Editor_state
= edit
.initialize_test_state()
183 Editor_state
.lines
= load_array
{'abc def'}
184 Text
.redraw_all(Editor_state
)
185 Editor_state
.cursor1
= {line
=1, pos
=4} -- at the space between words
186 edit
.draw(Editor_state
)
187 edit
.run_after_keychord(Editor_state
, 'M-right')
188 check_eq(Editor_state
.cursor1
.pos
, 8, 'check')
191 function test_skip_past_tab_to_next_word()
192 App
.screen
.init
{width
=120, height
=60}
193 Editor_state
= edit
.initialize_test_state()
194 Editor_state
.lines
= load_array
{'abc\tdef'}
195 Text
.redraw_all(Editor_state
)
196 Editor_state
.cursor1
= {line
=1, pos
=1} -- at the space between words
197 edit
.draw(Editor_state
)
198 edit
.run_after_keychord(Editor_state
, 'M-right')
199 check_eq(Editor_state
.cursor1
.pos
, 4, 'check')
202 function test_skip_multiple_spaces_to_next_word()
203 App
.screen
.init
{width
=120, height
=60}
204 Editor_state
= edit
.initialize_test_state()
205 Editor_state
.lines
= load_array
{'abc def'}
206 Text
.redraw_all(Editor_state
)
207 Editor_state
.cursor1
= {line
=1, pos
=4} -- at the start of second word
208 edit
.draw(Editor_state
)
209 edit
.run_after_keychord(Editor_state
, 'M-right')
210 check_eq(Editor_state
.cursor1
.pos
, 9, 'check')
213 function test_move_past_end_of_word_on_next_line()
214 App
.screen
.init
{width
=120, height
=60}
215 Editor_state
= edit
.initialize_test_state()
216 Editor_state
.lines
= load_array
{'abc def', 'ghi'}
217 Text
.redraw_all(Editor_state
)
218 Editor_state
.cursor1
= {line
=1, pos
=8}
219 edit
.draw(Editor_state
)
220 edit
.run_after_keychord(Editor_state
, 'M-right')
221 check_eq(Editor_state
.cursor1
.line
, 2, 'line')
222 check_eq(Editor_state
.cursor1
.pos
, 4, 'pos')
225 function test_click_moves_cursor()
226 App
.screen
.init
{width
=50, height
=60}
227 Editor_state
= edit
.initialize_test_state()
228 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
229 Text
.redraw_all(Editor_state
)
230 Editor_state
.cursor1
= {line
=1, pos
=1}
231 Editor_state
.screen_top1
= {line
=1, pos
=1}
232 Editor_state
.screen_bottom1
= {}
233 Editor_state
.selection1
= {}
234 edit
.draw(Editor_state
) -- populate line_cache.starty for each line Editor_state.line_cache
235 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
236 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
237 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
238 -- selection is empty to avoid perturbing future edits
239 check_nil(Editor_state
.selection1
.line
, 'selection:line')
240 check_nil(Editor_state
.selection1
.pos
, 'selection:pos')
243 function test_click_to_left_of_line()
244 -- display a line with the cursor in the middle
245 App
.screen
.init
{width
=50, height
=80}
246 Editor_state
= edit
.initialize_test_state()
247 Editor_state
.lines
= load_array
{'abc'}
248 Text
.redraw_all(Editor_state
)
249 Editor_state
.cursor1
= {line
=1, pos
=3}
250 Editor_state
.screen_top1
= {line
=1, pos
=1}
251 Editor_state
.screen_bottom1
= {}
252 Editor_state
.selection1
= {}
253 -- click to the left of the line
254 edit
.draw(Editor_state
)
255 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
-4,Editor_state
.top
+5, 1)
256 -- cursor moves to start of line
257 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
258 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
259 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
262 function test_click_takes_margins_into_account()
263 -- display two lines with cursor on one of them
264 App
.screen
.init
{width
=100, height
=80}
265 Editor_state
= edit
.initialize_test_state()
266 Editor_state
.left
= 50 -- occupy only right side of screen
267 Editor_state
.lines
= load_array
{'abc', 'def'}
268 Text
.redraw_all(Editor_state
)
269 Editor_state
.cursor1
= {line
=2, pos
=1}
270 Editor_state
.screen_top1
= {line
=1, pos
=1}
271 Editor_state
.screen_bottom1
= {}
272 Editor_state
.selection1
= {}
273 -- click on the other line
274 edit
.draw(Editor_state
)
275 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
277 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
278 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
279 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
282 function test_click_on_empty_line()
283 -- display two lines with the first one empty
284 App
.screen
.init
{width
=50, height
=80}
285 Editor_state
= edit
.initialize_test_state()
286 Editor_state
.lines
= load_array
{'', 'def'}
287 Text
.redraw_all(Editor_state
)
288 Editor_state
.cursor1
= {line
=2, pos
=1}
289 Editor_state
.screen_top1
= {line
=1, pos
=1}
290 Editor_state
.screen_bottom1
= {}
291 Editor_state
.selection1
= {}
292 -- click on the empty line
293 edit
.draw(Editor_state
)
294 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
296 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
297 -- selection remains empty
298 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
301 function test_click_below_all_lines()
303 App
.screen
.init
{width
=50, height
=80}
304 Editor_state
= edit
.initialize_test_state()
305 Editor_state
.lines
= load_array
{'abc'}
306 Text
.redraw_all(Editor_state
)
307 Editor_state
.cursor1
= {line
=1, pos
=1}
308 Editor_state
.screen_top1
= {line
=1, pos
=1}
309 Editor_state
.screen_bottom1
= {}
310 Editor_state
.selection1
= {}
311 -- click below first line
312 edit
.draw(Editor_state
)
313 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+50, 1)
314 -- cursor doesn't move
315 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
316 -- selection remains empty
317 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
320 function test_draw_text()
321 App
.screen
.init
{width
=120, height
=60}
322 Editor_state
= edit
.initialize_test_state()
323 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi'}
324 Text
.redraw_all(Editor_state
)
325 Editor_state
.cursor1
= {line
=1, pos
=1}
326 Editor_state
.screen_top1
= {line
=1, pos
=1}
327 Editor_state
.screen_bottom1
= {}
328 edit
.draw(Editor_state
)
329 local y
= Editor_state
.top
330 App
.screen
.check(y
, 'abc', 'screen:1')
331 y
= y
+ Editor_state
.line_height
332 App
.screen
.check(y
, 'def', 'screen:2')
333 y
= y
+ Editor_state
.line_height
334 App
.screen
.check(y
, 'ghi', 'screen:3')
337 function test_draw_wrapping_text()
338 App
.screen
.init
{width
=50, height
=60}
339 Editor_state
= edit
.initialize_test_state()
340 Editor_state
.lines
= load_array
{'abc', 'defgh', 'xyz'}
341 Text
.redraw_all(Editor_state
)
342 Editor_state
.cursor1
= {line
=1, pos
=1}
343 Editor_state
.screen_top1
= {line
=1, pos
=1}
344 Editor_state
.screen_bottom1
= {}
345 edit
.draw(Editor_state
)
346 local y
= Editor_state
.top
347 App
.screen
.check(y
, 'abc', 'screen:1')
348 y
= y
+ Editor_state
.line_height
349 App
.screen
.check(y
, 'de', 'screen:2')
350 y
= y
+ Editor_state
.line_height
351 App
.screen
.check(y
, 'fgh', 'screen:3')
354 function test_draw_word_wrapping_text()
355 App
.screen
.init
{width
=60, height
=60}
356 Editor_state
= edit
.initialize_test_state()
357 Editor_state
.lines
= load_array
{'abc def ghi', 'jkl'}
358 Text
.redraw_all(Editor_state
)
359 Editor_state
.cursor1
= {line
=1, pos
=1}
360 Editor_state
.screen_top1
= {line
=1, pos
=1}
361 Editor_state
.screen_bottom1
= {}
362 edit
.draw(Editor_state
)
363 local y
= Editor_state
.top
364 App
.screen
.check(y
, 'abc ', 'screen:1')
365 y
= y
+ Editor_state
.line_height
366 App
.screen
.check(y
, 'def ', 'screen:2')
367 y
= y
+ Editor_state
.line_height
368 App
.screen
.check(y
, 'ghi', 'screen:3')
371 function test_click_on_wrapping_line()
372 -- display two screen lines with cursor on one of them
373 App
.screen
.init
{width
=50, height
=80}
374 Editor_state
= edit
.initialize_test_state()
375 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr stu'}
376 Text
.redraw_all(Editor_state
)
377 Editor_state
.cursor1
= {line
=1, pos
=20}
378 Editor_state
.screen_top1
= {line
=1, pos
=1}
379 Editor_state
.screen_bottom1
= {}
380 -- click on the other line
381 edit
.draw(Editor_state
)
382 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
384 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
385 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
386 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
389 function test_click_on_wrapping_line_takes_margins_into_account()
390 -- display two screen lines with cursor on one of them
391 App
.screen
.init
{width
=100, height
=80}
392 Editor_state
= edit
.initialize_test_state()
393 Editor_state
.left
= 50 -- occupy only right side of screen
394 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr stu'}
395 Text
.redraw_all(Editor_state
)
396 Editor_state
.cursor1
= {line
=1, pos
=20}
397 Editor_state
.screen_top1
= {line
=1, pos
=1}
398 Editor_state
.screen_bottom1
= {}
399 -- click on the other line
400 edit
.draw(Editor_state
)
401 edit
.run_after_mouse_click(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
403 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
404 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
405 check_nil(Editor_state
.selection1
.line
, 'selection is empty to avoid perturbing future edits')
408 function test_draw_text_wrapping_within_word()
409 -- arrange a screen line that needs to be split within a word
410 App
.screen
.init
{width
=60, height
=60}
411 Editor_state
= edit
.initialize_test_state()
412 Editor_state
.lines
= load_array
{'abcd e fghijk', 'xyz'}
413 Text
.redraw_all(Editor_state
)
414 Editor_state
.cursor1
= {line
=1, pos
=1}
415 Editor_state
.screen_top1
= {line
=1, pos
=1}
416 Editor_state
.screen_bottom1
= {}
417 edit
.draw(Editor_state
)
418 local y
= Editor_state
.top
419 App
.screen
.check(y
, 'abcd ', 'screen:1')
420 y
= y
+ Editor_state
.line_height
421 App
.screen
.check(y
, 'e fgh', 'screen:2')
422 y
= y
+ Editor_state
.line_height
423 App
.screen
.check(y
, 'ijk', 'screen:3')
426 function test_draw_wrapping_text_containing_non_ascii()
427 -- draw a long line containing non-ASCII
428 App
.screen
.init
{width
=60, height
=60}
429 Editor_state
= edit
.initialize_test_state()
430 Editor_state
.lines
= load_array
{'madam I’m adam', 'xyz'} -- notice the non-ASCII apostrophe
431 Text
.redraw_all(Editor_state
)
432 Editor_state
.cursor1
= {line
=1, pos
=1}
433 Editor_state
.screen_top1
= {line
=1, pos
=1}
434 Editor_state
.screen_bottom1
= {}
435 edit
.draw(Editor_state
)
436 local y
= Editor_state
.top
437 App
.screen
.check(y
, 'mad', 'screen:1')
438 y
= y
+ Editor_state
.line_height
439 App
.screen
.check(y
, 'am I', 'screen:2')
440 y
= y
+ Editor_state
.line_height
441 App
.screen
.check(y
, '’m a', 'screen:3')
444 function test_click_past_end_of_screen_line()
445 -- display a wrapping line
446 App
.screen
.init
{width
=75, height
=80}
447 Editor_state
= edit
.initialize_test_state()
449 Editor_state
.lines
= load_array
{"madam I'm adam"}
450 Text
.redraw_all(Editor_state
)
451 Editor_state
.cursor1
= {line
=1, pos
=1}
452 Editor_state
.screen_top1
= {line
=1, pos
=1}
453 Editor_state
.screen_bottom1
= {}
454 edit
.draw(Editor_state
)
455 local y
= Editor_state
.top
456 App
.screen
.check(y
, 'madam ', 'baseline/screen:1')
457 y
= y
+ Editor_state
.line_height
458 App
.screen
.check(y
, "I'm ad", 'baseline/screen:2')
459 y
= y
+ Editor_state
.line_height
460 -- click past end of second screen line
461 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
462 -- cursor moves to end of screen line
463 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
464 check_eq(Editor_state
.cursor1
.pos
, 12, 'cursor:pos')
467 function test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen()
468 -- display a wrapping line from its second screen line
469 App
.screen
.init
{width
=75, height
=80}
470 Editor_state
= edit
.initialize_test_state()
472 Editor_state
.lines
= load_array
{"madam I'm adam"}
473 Text
.redraw_all(Editor_state
)
474 Editor_state
.cursor1
= {line
=1, pos
=8}
475 Editor_state
.screen_top1
= {line
=1, pos
=7}
476 Editor_state
.screen_bottom1
= {}
477 edit
.draw(Editor_state
)
478 local y
= Editor_state
.top
479 App
.screen
.check(y
, "I'm ad", 'baseline/screen:2')
480 y
= y
+ Editor_state
.line_height
481 -- click past end of second screen line
482 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
483 -- cursor moves to end of screen line
484 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
485 check_eq(Editor_state
.cursor1
.pos
, 12, 'cursor:pos')
488 function test_click_past_end_of_wrapping_line()
489 -- display a wrapping line
490 App
.screen
.init
{width
=75, height
=80}
491 Editor_state
= edit
.initialize_test_state()
493 Editor_state
.lines
= load_array
{"madam I'm adam"}
494 Text
.redraw_all(Editor_state
)
495 Editor_state
.cursor1
= {line
=1, pos
=1}
496 Editor_state
.screen_top1
= {line
=1, pos
=1}
497 Editor_state
.screen_bottom1
= {}
498 edit
.draw(Editor_state
)
499 local y
= Editor_state
.top
500 App
.screen
.check(y
, 'madam ', 'baseline/screen:1')
501 y
= y
+ Editor_state
.line_height
502 App
.screen
.check(y
, "I'm ad", 'baseline/screen:2')
503 y
= y
+ Editor_state
.line_height
504 App
.screen
.check(y
, 'am', 'baseline/screen:3')
505 y
= y
+ Editor_state
.line_height
506 -- click past the end of it
507 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
508 -- cursor moves to end of line
509 check_eq(Editor_state
.cursor1
.pos
, 15, 'cursor') -- one more than the number of UTF-8 code-points
512 function test_click_past_end_of_wrapping_line_containing_non_ascii()
513 -- display a wrapping line containing non-ASCII
514 App
.screen
.init
{width
=75, height
=80}
515 Editor_state
= edit
.initialize_test_state()
517 Editor_state
.lines
= load_array
{'madam I’m adam'} -- notice the non-ASCII apostrophe
518 Text
.redraw_all(Editor_state
)
519 Editor_state
.cursor1
= {line
=1, pos
=1}
520 Editor_state
.screen_top1
= {line
=1, pos
=1}
521 Editor_state
.screen_bottom1
= {}
522 edit
.draw(Editor_state
)
523 local y
= Editor_state
.top
524 App
.screen
.check(y
, 'madam ', 'baseline/screen:1')
525 y
= y
+ Editor_state
.line_height
526 App
.screen
.check(y
, 'I’m ad', 'baseline/screen:2')
527 y
= y
+ Editor_state
.line_height
528 App
.screen
.check(y
, 'am', 'baseline/screen:3')
529 y
= y
+ Editor_state
.line_height
530 -- click past the end of it
531 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
532 -- cursor moves to end of line
533 check_eq(Editor_state
.cursor1
.pos
, 15, 'cursor') -- one more than the number of UTF-8 code-points
536 function test_click_past_end_of_word_wrapping_line()
537 -- display a long line wrapping at a word boundary on a screen of more realistic length
538 App
.screen
.init
{width
=160, height
=80}
539 Editor_state
= edit
.initialize_test_state()
541 -- 123456789012345678901
542 Editor_state
.lines
= load_array
{'the quick brown fox jumped over the lazy dog'}
543 Text
.redraw_all(Editor_state
)
544 Editor_state
.cursor1
= {line
=1, pos
=1}
545 Editor_state
.screen_top1
= {line
=1, pos
=1}
546 Editor_state
.screen_bottom1
= {}
547 edit
.draw(Editor_state
)
548 local y
= Editor_state
.top
549 App
.screen
.check(y
, 'the quick brown fox ', 'baseline/screen:1')
550 y
= y
+ Editor_state
.line_height
551 -- click past the end of the screen line
552 edit
.run_after_mouse_click(Editor_state
, App
.screen
.width
-2,y
-2, 1)
553 -- cursor moves to end of screen line
554 check_eq(Editor_state
.cursor1
.pos
, 20, 'cursor')
557 function test_select_text()
558 -- display a line of text
559 App
.screen
.init
{width
=75, height
=80}
560 Editor_state
= edit
.initialize_test_state()
561 Editor_state
.lines
= load_array
{'abc def'}
562 Text
.redraw_all(Editor_state
)
563 Editor_state
.cursor1
= {line
=1, pos
=1}
564 Editor_state
.screen_top1
= {line
=1, pos
=1}
565 Editor_state
.screen_bottom1
= {}
566 edit
.draw(Editor_state
)
568 App
.fake_key_press('lshift')
569 edit
.run_after_keychord(Editor_state
, 'S-right')
570 App
.fake_key_release('lshift')
571 edit
.key_release(Editor_state
, 'lshift')
572 -- selection persists even after shift is released
573 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
574 check_eq(Editor_state
.selection1
.pos
, 1, 'selection:pos')
575 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
576 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
579 function test_cursor_movement_without_shift_resets_selection()
580 -- display a line of text with some part selected
581 App
.screen
.init
{width
=75, height
=80}
582 Editor_state
= edit
.initialize_test_state()
583 Editor_state
.lines
= load_array
{'abc'}
584 Text
.redraw_all(Editor_state
)
585 Editor_state
.cursor1
= {line
=1, pos
=1}
586 Editor_state
.selection1
= {line
=1, pos
=2}
587 Editor_state
.screen_top1
= {line
=1, pos
=1}
588 Editor_state
.screen_bottom1
= {}
589 edit
.draw(Editor_state
)
590 -- press an arrow key without shift
591 edit
.run_after_keychord(Editor_state
, 'right')
592 -- no change to data, selection is reset
593 check_nil(Editor_state
.selection1
.line
, 'check')
594 check_eq(Editor_state
.lines
[1].data
, 'abc', 'data')
597 function test_edit_deletes_selection()
598 -- display a line of text with some part selected
599 App
.screen
.init
{width
=75, height
=80}
600 Editor_state
= edit
.initialize_test_state()
601 Editor_state
.lines
= load_array
{'abc'}
602 Text
.redraw_all(Editor_state
)
603 Editor_state
.cursor1
= {line
=1, pos
=1}
604 Editor_state
.selection1
= {line
=1, pos
=2}
605 Editor_state
.screen_top1
= {line
=1, pos
=1}
606 Editor_state
.screen_bottom1
= {}
607 edit
.draw(Editor_state
)
609 edit
.run_after_text_input(Editor_state
, 'x')
610 -- selected text is deleted and replaced with the key
611 check_eq(Editor_state
.lines
[1].data
, 'xbc', 'check')
614 function test_edit_with_shift_key_deletes_selection()
615 -- display a line of text with some part selected
616 App
.screen
.init
{width
=75, height
=80}
617 Editor_state
= edit
.initialize_test_state()
618 Editor_state
.lines
= load_array
{'abc'}
619 Text
.redraw_all(Editor_state
)
620 Editor_state
.cursor1
= {line
=1, pos
=1}
621 Editor_state
.selection1
= {line
=1, pos
=2}
622 Editor_state
.screen_top1
= {line
=1, pos
=1}
623 Editor_state
.screen_bottom1
= {}
624 edit
.draw(Editor_state
)
625 -- mimic precise keypresses for a capital letter
626 App
.fake_key_press('lshift')
627 edit
.keychord_press(Editor_state
, 'd', 'd')
628 edit
.text_input(Editor_state
, 'D')
629 edit
.key_release(Editor_state
, 'd')
630 App
.fake_key_release('lshift')
631 -- selected text is deleted and replaced with the key
632 check_nil(Editor_state
.selection1
.line
, 'check')
633 check_eq(Editor_state
.lines
[1].data
, 'Dbc', 'data')
636 function test_copy_does_not_reset_selection()
637 -- display a line of text with a selection
638 App
.screen
.init
{width
=75, height
=80}
639 Editor_state
= edit
.initialize_test_state()
640 Editor_state
.lines
= load_array
{'abc'}
641 Text
.redraw_all(Editor_state
)
642 Editor_state
.cursor1
= {line
=1, pos
=1}
643 Editor_state
.selection1
= {line
=1, pos
=2}
644 Editor_state
.screen_top1
= {line
=1, pos
=1}
645 Editor_state
.screen_bottom1
= {}
646 edit
.draw(Editor_state
)
648 edit
.run_after_keychord(Editor_state
, 'C-c')
649 check_eq(App
.clipboard
, 'a', 'clipboard')
650 -- selection is reset since shift key is not pressed
651 check(Editor_state
.selection1
.line
, 'check')
655 -- display a line of text with some part selected
656 App
.screen
.init
{width
=75, height
=80}
657 Editor_state
= edit
.initialize_test_state()
658 Editor_state
.lines
= load_array
{'abc'}
659 Text
.redraw_all(Editor_state
)
660 Editor_state
.cursor1
= {line
=1, pos
=1}
661 Editor_state
.selection1
= {line
=1, pos
=2}
662 Editor_state
.screen_top1
= {line
=1, pos
=1}
663 Editor_state
.screen_bottom1
= {}
664 edit
.draw(Editor_state
)
666 edit
.run_after_keychord(Editor_state
, 'C-x')
667 check_eq(App
.clipboard
, 'a', 'clipboard')
668 -- selected text is deleted
669 check_eq(Editor_state
.lines
[1].data
, 'bc', 'data')
672 function test_paste_replaces_selection()
673 -- display a line of text with a selection
674 App
.screen
.init
{width
=75, height
=80}
675 Editor_state
= edit
.initialize_test_state()
676 Editor_state
.lines
= load_array
{'abc', 'def'}
677 Text
.redraw_all(Editor_state
)
678 Editor_state
.cursor1
= {line
=2, pos
=1}
679 Editor_state
.selection1
= {line
=1, pos
=1}
680 Editor_state
.screen_top1
= {line
=1, pos
=1}
681 Editor_state
.screen_bottom1
= {}
682 edit
.draw(Editor_state
)
684 App
.clipboard
= 'xyz'
686 edit
.run_after_keychord(Editor_state
, 'C-v')
687 -- selection is reset since shift key is not pressed
688 -- selection includes the newline, so it's also deleted
689 check_eq(Editor_state
.lines
[1].data
, 'xyzdef', 'check')
692 function test_deleting_selection_may_scroll()
693 -- display lines 2/3/4
694 App
.screen
.init
{width
=120, height
=60}
695 Editor_state
= edit
.initialize_test_state()
696 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
697 Text
.redraw_all(Editor_state
)
698 Editor_state
.cursor1
= {line
=3, pos
=2}
699 Editor_state
.screen_top1
= {line
=2, pos
=1}
700 Editor_state
.screen_bottom1
= {}
701 edit
.draw(Editor_state
)
702 local y
= Editor_state
.top
703 App
.screen
.check(y
, 'def', 'baseline/screen:1')
704 y
= y
+ Editor_state
.line_height
705 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
706 y
= y
+ Editor_state
.line_height
707 App
.screen
.check(y
, 'jkl', 'baseline/screen:3')
708 -- set up a selection starting above the currently displayed page
709 Editor_state
.selection1
= {line
=1, pos
=2}
711 edit
.run_after_keychord(Editor_state
, 'backspace')
713 check_eq(Editor_state
.screen_top1
.line
, 1, 'check')
714 check_eq(Editor_state
.lines
[1].data
, 'ahi', 'data')
717 function test_edit_wrapping_text()
718 App
.screen
.init
{width
=50, height
=60}
719 Editor_state
= edit
.initialize_test_state()
720 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
721 Text
.redraw_all(Editor_state
)
722 Editor_state
.cursor1
= {line
=2, pos
=4}
723 Editor_state
.screen_top1
= {line
=1, pos
=1}
724 Editor_state
.screen_bottom1
= {}
725 edit
.draw(Editor_state
)
726 edit
.run_after_text_input(Editor_state
, 'g')
727 local y
= Editor_state
.top
728 App
.screen
.check(y
, 'abc', 'screen:1')
729 y
= y
+ Editor_state
.line_height
730 App
.screen
.check(y
, 'de', 'screen:2')
731 y
= y
+ Editor_state
.line_height
732 App
.screen
.check(y
, 'fg', 'screen:3')
735 function test_insert_newline()
736 -- display a few lines
737 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
738 Editor_state
= edit
.initialize_test_state()
739 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
740 Text
.redraw_all(Editor_state
)
741 Editor_state
.cursor1
= {line
=1, pos
=2}
742 Editor_state
.screen_top1
= {line
=1, pos
=1}
743 Editor_state
.screen_bottom1
= {}
744 edit
.draw(Editor_state
)
745 local y
= Editor_state
.top
746 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
747 y
= y
+ Editor_state
.line_height
748 App
.screen
.check(y
, 'def', 'baseline/screen:2')
749 y
= y
+ Editor_state
.line_height
750 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
751 -- hitting the enter key splits the line
752 edit
.run_after_keychord(Editor_state
, 'return')
753 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
754 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
755 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
757 App
.screen
.check(y
, 'a', 'screen:1')
758 y
= y
+ Editor_state
.line_height
759 App
.screen
.check(y
, 'bc', 'screen:2')
760 y
= y
+ Editor_state
.line_height
761 App
.screen
.check(y
, 'def', 'screen:3')
764 function test_insert_newline_at_start_of_line()
766 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
767 Editor_state
= edit
.initialize_test_state()
768 Editor_state
.lines
= load_array
{'abc'}
769 Text
.redraw_all(Editor_state
)
770 Editor_state
.cursor1
= {line
=1, pos
=1}
771 Editor_state
.screen_top1
= {line
=1, pos
=1}
772 Editor_state
.screen_bottom1
= {}
773 -- hitting the enter key splits the line
774 edit
.run_after_keychord(Editor_state
, 'return')
775 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
776 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
777 check_eq(Editor_state
.lines
[1].data
, '', 'data:1')
778 check_eq(Editor_state
.lines
[2].data
, 'abc', 'data:2')
781 function test_insert_from_clipboard()
782 -- display a few lines
783 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
784 Editor_state
= edit
.initialize_test_state()
785 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
786 Text
.redraw_all(Editor_state
)
787 Editor_state
.cursor1
= {line
=1, pos
=2}
788 Editor_state
.screen_top1
= {line
=1, pos
=1}
789 Editor_state
.screen_bottom1
= {}
790 edit
.draw(Editor_state
)
791 local y
= Editor_state
.top
792 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
793 y
= y
+ Editor_state
.line_height
794 App
.screen
.check(y
, 'def', 'baseline/screen:2')
795 y
= y
+ Editor_state
.line_height
796 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
797 -- paste some text including a newline, check that new line is created
798 App
.clipboard
= 'xy\nz'
799 edit
.run_after_keychord(Editor_state
, 'C-v')
800 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
801 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
802 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
804 App
.screen
.check(y
, 'axy', 'screen:1')
805 y
= y
+ Editor_state
.line_height
806 App
.screen
.check(y
, 'zbc', 'screen:2')
807 y
= y
+ Editor_state
.line_height
808 App
.screen
.check(y
, 'def', 'screen:3')
811 function test_select_text_using_mouse()
812 App
.screen
.init
{width
=50, height
=60}
813 Editor_state
= edit
.initialize_test_state()
814 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
815 Text
.redraw_all(Editor_state
)
816 Editor_state
.cursor1
= {line
=1, pos
=1}
817 Editor_state
.screen_top1
= {line
=1, pos
=1}
818 Editor_state
.screen_bottom1
= {}
819 Editor_state
.selection1
= {}
820 edit
.draw(Editor_state
) -- populate line_cache.starty for each line Editor_state.line_cache
821 -- press and hold on first location
822 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
823 -- drag and release somewhere else
824 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+Editor_state
.line_height
+5, 1)
825 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
826 check_eq(Editor_state
.selection1
.pos
, 2, 'selection:pos')
827 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
828 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
831 function test_select_text_using_mouse_starting_above_text()
832 App
.screen
.init
{width
=50, height
=60}
833 Editor_state
= edit
.initialize_test_state()
834 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
835 Text
.redraw_all(Editor_state
)
836 Editor_state
.cursor1
= {line
=1, pos
=1}
837 Editor_state
.screen_top1
= {line
=1, pos
=1}
838 Editor_state
.screen_bottom1
= {}
839 Editor_state
.selection1
= {}
840 edit
.draw(Editor_state
) -- populate line_cache.starty for each line Editor_state.line_cache
841 -- press mouse above first line of text
842 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+8,5, 1)
843 check(Editor_state
.selection1
.line
~= nil, 'selection:line-not-nil')
844 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
845 check_eq(Editor_state
.selection1
.pos
, 1, 'selection:pos')
848 function test_select_text_using_mouse_starting_above_text_wrapping_line()
849 -- first screen line starts in the middle of a line
850 App
.screen
.init
{width
=50, height
=60}
851 Editor_state
= edit
.initialize_test_state()
852 Editor_state
.lines
= load_array
{'abc', 'defgh', 'xyz'}
853 Text
.redraw_all(Editor_state
)
854 Editor_state
.cursor1
= {line
=2, pos
=5}
855 Editor_state
.screen_top1
= {line
=2, pos
=3}
856 Editor_state
.screen_bottom1
= {}
857 -- press mouse above first line of text
858 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+8,5, 1)
859 -- selection is at screen top
860 check(Editor_state
.selection1
.line
~= nil, 'selection:line-not-nil')
861 check_eq(Editor_state
.selection1
.line
, 2, 'selection:line')
862 check_eq(Editor_state
.selection1
.pos
, 3, 'selection:pos')
865 function test_select_text_using_mouse_starting_below_text()
866 -- I'd like to test what happens when a mouse click is below some page of
867 -- text, potentially even in the middle of a line.
868 -- However, it's brittle to set up a text line boundary just right.
869 -- So I'm going to just check things below the bottom of the final line of
870 -- text when it's in the middle of the screen.
871 -- final screen line ends in the middle of screen
872 App
.screen
.init
{width
=50, height
=60}
873 Editor_state
= edit
.initialize_test_state()
874 Editor_state
.lines
= load_array
{'abcde'}
875 Text
.redraw_all(Editor_state
)
876 Editor_state
.cursor1
= {line
=1, pos
=1}
877 Editor_state
.screen_top1
= {line
=1, pos
=1}
878 Editor_state
.screen_bottom1
= {}
879 edit
.draw(Editor_state
)
880 local y
= Editor_state
.top
881 App
.screen
.check(y
, 'ab', 'baseline:screen:1')
882 y
= y
+ Editor_state
.line_height
883 App
.screen
.check(y
, 'cde', 'baseline:screen:2')
884 -- press mouse above first line of text
885 edit
.run_after_mouse_press(Editor_state
, 5,App
.screen
.height
-5, 1)
886 -- selection is past bottom-most text in screen
887 check(Editor_state
.selection1
.line
~= nil, 'selection:line-not-nil')
888 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
889 check_eq(Editor_state
.selection1
.pos
, 6, 'selection:pos')
892 function test_select_text_using_mouse_and_shift()
893 App
.screen
.init
{width
=50, height
=60}
894 Editor_state
= edit
.initialize_test_state()
895 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
896 Text
.redraw_all(Editor_state
)
897 Editor_state
.cursor1
= {line
=1, pos
=1}
898 Editor_state
.screen_top1
= {line
=1, pos
=1}
899 Editor_state
.screen_bottom1
= {}
900 Editor_state
.selection1
= {}
901 edit
.draw(Editor_state
) -- populate line_cache.starty for each line Editor_state.line_cache
902 -- click on first location
903 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
904 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
905 -- hold down shift and click somewhere else
906 App
.fake_key_press('lshift')
907 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+5, 1)
908 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+Editor_state
.line_height
+5, 1)
909 App
.fake_key_release('lshift')
910 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
911 check_eq(Editor_state
.selection1
.pos
, 2, 'selection:pos')
912 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
913 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
916 function test_select_text_repeatedly_using_mouse_and_shift()
917 App
.screen
.init
{width
=50, height
=60}
918 Editor_state
= edit
.initialize_test_state()
919 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
920 Text
.redraw_all(Editor_state
)
921 Text
.redraw_all(Editor_state
)
922 Editor_state
.cursor1
= {line
=1, pos
=1}
923 Editor_state
.screen_top1
= {line
=1, pos
=1}
924 Editor_state
.screen_bottom1
= {}
925 Editor_state
.selection1
= {}
926 edit
.draw(Editor_state
) -- populate line_cache.starty for each line Editor_state.line_cache
927 -- click on first location
928 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
929 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+5, 1)
930 -- hold down shift and click on a second location
931 App
.fake_key_press('lshift')
932 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+5, 1)
933 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+Editor_state
.line_height
+5, 1)
934 -- hold down shift and click at a third location
935 App
.fake_key_press('lshift')
936 edit
.run_after_mouse_press(Editor_state
, Editor_state
.left
+20,Editor_state
.top
+5, 1)
937 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+8,Editor_state
.top
+Editor_state
.line_height
+5, 1)
938 App
.fake_key_release('lshift')
939 -- selection is between first and third location. forget the second location, not the first.
940 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
941 check_eq(Editor_state
.selection1
.pos
, 2, 'selection:pos')
942 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
943 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
946 function test_select_all_text()
947 -- display a single line of text
948 App
.screen
.init
{width
=75, height
=80}
949 Editor_state
= edit
.initialize_test_state()
950 Editor_state
.lines
= load_array
{'abc def'}
951 Text
.redraw_all(Editor_state
)
952 Editor_state
.cursor1
= {line
=1, pos
=1}
953 Editor_state
.screen_top1
= {line
=1, pos
=1}
954 Editor_state
.screen_bottom1
= {}
955 edit
.draw(Editor_state
)
957 App
.fake_key_press('lctrl')
958 edit
.run_after_keychord(Editor_state
, 'C-a')
959 App
.fake_key_release('lctrl')
960 edit
.key_release(Editor_state
, 'lctrl')
962 check_eq(Editor_state
.selection1
.line
, 1, 'selection:line')
963 check_eq(Editor_state
.selection1
.pos
, 1, 'selection:pos')
964 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
965 check_eq(Editor_state
.cursor1
.pos
, 8, 'cursor:pos')
968 function test_cut_without_selection()
969 -- display a few lines
970 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
971 Editor_state
= edit
.initialize_test_state()
972 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
973 Text
.redraw_all(Editor_state
)
974 Editor_state
.cursor1
= {line
=1, pos
=2}
975 Editor_state
.screen_top1
= {line
=1, pos
=1}
976 Editor_state
.screen_bottom1
= {}
977 Editor_state
.selection1
= {}
978 edit
.draw(Editor_state
)
979 -- try to cut without selecting text
980 edit
.run_after_keychord(Editor_state
, 'C-x')
982 check_nil(Editor_state
.selection1
.line
, 'check')
985 function test_pagedown()
986 App
.screen
.init
{width
=120, height
=45}
987 Editor_state
= edit
.initialize_test_state()
988 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi'}
989 Text
.redraw_all(Editor_state
)
990 Editor_state
.cursor1
= {line
=1, pos
=1}
991 Editor_state
.screen_top1
= {line
=1, pos
=1}
992 Editor_state
.screen_bottom1
= {}
993 -- initially the first two lines are displayed
994 edit
.draw(Editor_state
)
995 local y
= Editor_state
.top
996 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
997 y
= y
+ Editor_state
.line_height
998 App
.screen
.check(y
, 'def', 'baseline/screen:2')
999 -- after pagedown the bottom line becomes the top
1000 edit
.run_after_keychord(Editor_state
, 'pagedown')
1001 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1002 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor')
1003 y
= Editor_state
.top
1004 App
.screen
.check(y
, 'def', 'screen:1')
1005 y
= y
+ Editor_state
.line_height
1006 App
.screen
.check(y
, 'ghi', 'screen:2')
1009 function test_pagedown_often_shows_start_of_wrapping_line()
1010 -- draw a few lines ending in part of a wrapping line
1011 App
.screen
.init
{width
=50, height
=60}
1012 Editor_state
= edit
.initialize_test_state()
1013 Editor_state
.lines
= load_array
{'abc', 'def ghi jkl', 'mno'}
1014 Text
.redraw_all(Editor_state
)
1015 Editor_state
.cursor1
= {line
=1, pos
=1}
1016 Editor_state
.screen_top1
= {line
=1, pos
=1}
1017 Editor_state
.screen_bottom1
= {}
1018 edit
.draw(Editor_state
)
1019 local y
= Editor_state
.top
1020 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1021 y
= y
+ Editor_state
.line_height
1022 App
.screen
.check(y
, 'def ', 'baseline/screen:2')
1023 y
= y
+ Editor_state
.line_height
1024 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3')
1025 -- after pagedown we start drawing from the bottom _line_ (multiple screen lines)
1026 edit
.run_after_keychord(Editor_state
, 'pagedown')
1027 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top:line')
1028 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1029 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1030 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1031 y
= Editor_state
.top
1032 App
.screen
.check(y
, 'def ', 'screen:1')
1033 y
= y
+ Editor_state
.line_height
1034 App
.screen
.check(y
, 'ghi ', 'screen:2')
1035 y
= y
+ Editor_state
.line_height
1036 App
.screen
.check(y
, 'jkl', 'screen:3')
1039 function test_pagedown_can_start_from_middle_of_long_wrapping_line()
1040 -- draw a few lines starting from a very long wrapping line
1041 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1042 Editor_state
= edit
.initialize_test_state()
1043 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr stu vwx yza bcd efg hij', 'XYZ'}
1044 Text
.redraw_all(Editor_state
)
1045 Editor_state
.cursor1
= {line
=1, pos
=2}
1046 Editor_state
.screen_top1
= {line
=1, pos
=1}
1047 Editor_state
.screen_bottom1
= {}
1048 edit
.draw(Editor_state
)
1049 local y
= Editor_state
.top
1050 App
.screen
.check(y
, 'abc ', 'baseline/screen:1')
1051 y
= y
+ Editor_state
.line_height
1052 App
.screen
.check(y
, 'def ', 'baseline/screen:2')
1053 y
= y
+ Editor_state
.line_height
1054 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3')
1055 -- after pagedown we scroll down the very long wrapping line
1056 edit
.run_after_keychord(Editor_state
, 'pagedown')
1057 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
1058 check_eq(Editor_state
.screen_top1
.pos
, 9, 'screen_top:pos')
1059 y
= Editor_state
.top
1060 App
.screen
.check(y
, 'ghi ', 'screen:1')
1061 y
= y
+ Editor_state
.line_height
1062 App
.screen
.check(y
, 'jkl ', 'screen:2')
1063 y
= y
+ Editor_state
.line_height
1064 if Version
== '12.0' then
1065 -- HACK: Maybe v12.0 uses a different font? Strange that it only causes
1066 -- issues in a couple of places.
1067 -- We'll need to rethink our tests if issues like this start to multiply.
1068 App
.screen
.check(y
, 'mno ', 'screen:3')
1070 App
.screen
.check(y
, 'mn', 'screen:3')
1074 function test_pagedown_never_moves_up()
1075 -- draw the final screen line of a wrapping line
1076 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1077 Editor_state
= edit
.initialize_test_state()
1078 Editor_state
.lines
= load_array
{'abc def ghi'}
1079 Text
.redraw_all(Editor_state
)
1080 Editor_state
.cursor1
= {line
=1, pos
=9}
1081 Editor_state
.screen_top1
= {line
=1, pos
=9}
1082 Editor_state
.screen_bottom1
= {}
1083 edit
.draw(Editor_state
)
1084 -- pagedown makes no change
1085 edit
.run_after_keychord(Editor_state
, 'pagedown')
1086 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
1087 check_eq(Editor_state
.screen_top1
.pos
, 9, 'screen_top:pos')
1090 function test_down_arrow_moves_cursor()
1091 App
.screen
.init
{width
=120, height
=60}
1092 Editor_state
= edit
.initialize_test_state()
1093 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1094 Text
.redraw_all(Editor_state
)
1095 Editor_state
.cursor1
= {line
=1, pos
=1}
1096 Editor_state
.screen_top1
= {line
=1, pos
=1}
1097 Editor_state
.screen_bottom1
= {}
1098 -- initially the first three lines are displayed
1099 edit
.draw(Editor_state
)
1100 local y
= Editor_state
.top
1101 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1102 y
= y
+ Editor_state
.line_height
1103 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1104 y
= y
+ Editor_state
.line_height
1105 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1106 -- after hitting the down arrow, the cursor moves down by 1 line
1107 edit
.run_after_keychord(Editor_state
, 'down')
1108 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1109 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor')
1110 -- the screen is unchanged
1111 y
= Editor_state
.top
1112 App
.screen
.check(y
, 'abc', 'screen:1')
1113 y
= y
+ Editor_state
.line_height
1114 App
.screen
.check(y
, 'def', 'screen:2')
1115 y
= y
+ Editor_state
.line_height
1116 App
.screen
.check(y
, 'ghi', 'screen:3')
1119 function test_down_arrow_scrolls_down_by_one_line()
1120 -- display the first three lines with the cursor on the bottom line
1121 App
.screen
.init
{width
=120, height
=60}
1122 Editor_state
= edit
.initialize_test_state()
1123 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1124 Text
.redraw_all(Editor_state
)
1125 Editor_state
.cursor1
= {line
=3, pos
=1}
1126 Editor_state
.screen_top1
= {line
=1, pos
=1}
1127 Editor_state
.screen_bottom1
= {}
1128 edit
.draw(Editor_state
)
1129 local y
= Editor_state
.top
1130 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1131 y
= y
+ Editor_state
.line_height
1132 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1133 y
= y
+ Editor_state
.line_height
1134 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1135 -- after hitting the down arrow the screen scrolls down by one line
1136 edit
.run_after_keychord(Editor_state
, 'down')
1137 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1138 check_eq(Editor_state
.cursor1
.line
, 4, 'cursor')
1139 y
= Editor_state
.top
1140 App
.screen
.check(y
, 'def', 'screen:1')
1141 y
= y
+ Editor_state
.line_height
1142 App
.screen
.check(y
, 'ghi', 'screen:2')
1143 y
= y
+ Editor_state
.line_height
1144 App
.screen
.check(y
, 'jkl', 'screen:3')
1147 function test_down_arrow_scrolls_down_by_one_screen_line()
1148 -- display the first three lines with the cursor on the bottom line
1149 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1150 Editor_state
= edit
.initialize_test_state()
1151 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1152 Text
.redraw_all(Editor_state
)
1153 Editor_state
.cursor1
= {line
=3, pos
=1}
1154 Editor_state
.screen_top1
= {line
=1, pos
=1}
1155 Editor_state
.screen_bottom1
= {}
1156 edit
.draw(Editor_state
)
1157 local y
= Editor_state
.top
1158 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1159 y
= y
+ Editor_state
.line_height
1160 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1161 y
= y
+ Editor_state
.line_height
1162 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1163 -- after hitting the down arrow the screen scrolls down by one line
1164 edit
.run_after_keychord(Editor_state
, 'down')
1165 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1166 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1167 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1168 y
= Editor_state
.top
1169 App
.screen
.check(y
, 'def', 'screen:1')
1170 y
= y
+ Editor_state
.line_height
1171 App
.screen
.check(y
, 'ghi ', 'screen:2')
1172 y
= y
+ Editor_state
.line_height
1173 App
.screen
.check(y
, 'jkl', 'screen:3')
1176 function test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word()
1177 -- display the first three lines with the cursor on the bottom line
1178 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1179 Editor_state
= edit
.initialize_test_state()
1180 Editor_state
.lines
= load_array
{'abc', 'def', 'ghijkl', 'mno'}
1181 Text
.redraw_all(Editor_state
)
1182 Editor_state
.cursor1
= {line
=3, pos
=1}
1183 Editor_state
.screen_top1
= {line
=1, pos
=1}
1184 Editor_state
.screen_bottom1
= {}
1185 edit
.draw(Editor_state
)
1186 local y
= Editor_state
.top
1187 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1188 y
= y
+ Editor_state
.line_height
1189 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1190 y
= y
+ Editor_state
.line_height
1191 App
.screen
.check(y
, 'ghij', 'baseline/screen:3')
1192 -- after hitting the down arrow the screen scrolls down by one line
1193 edit
.run_after_keychord(Editor_state
, 'down')
1194 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1195 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1196 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1197 y
= Editor_state
.top
1198 App
.screen
.check(y
, 'def', 'screen:1')
1199 y
= y
+ Editor_state
.line_height
1200 App
.screen
.check(y
, 'ghij', 'screen:2')
1201 y
= y
+ Editor_state
.line_height
1202 App
.screen
.check(y
, 'kl', 'screen:3')
1205 function test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up()
1206 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1207 Editor_state
= edit
.initialize_test_state()
1208 Editor_state
.lines
= load_array
{'abc', 'def', 'ghijkl', 'mno'}
1209 Text
.redraw_all(Editor_state
)
1210 Editor_state
.cursor1
= {line
=3, pos
=1}
1211 Editor_state
.screen_top1
= {line
=1, pos
=1}
1212 Editor_state
.screen_bottom1
= {}
1213 edit
.draw(Editor_state
)
1214 local y
= Editor_state
.top
1215 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1216 y
= y
+ Editor_state
.line_height
1217 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1218 y
= y
+ Editor_state
.line_height
1219 App
.screen
.check(y
, 'ghij', 'baseline/screen:3')
1220 -- after hitting pagedown the screen scrolls down to start of a long line
1221 edit
.run_after_keychord(Editor_state
, 'pagedown')
1222 check_eq(Editor_state
.screen_top1
.line
, 3, 'baseline2/screen_top')
1223 check_eq(Editor_state
.cursor1
.line
, 3, 'baseline2/cursor:line')
1224 check_eq(Editor_state
.cursor1
.pos
, 1, 'baseline2/cursor:pos')
1225 -- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll up
1226 edit
.run_after_keychord(Editor_state
, 'down')
1227 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top')
1228 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1229 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1230 y
= Editor_state
.top
1231 App
.screen
.check(y
, 'ghij', 'screen:1')
1232 y
= y
+ Editor_state
.line_height
1233 App
.screen
.check(y
, 'kl', 'screen:2')
1234 y
= y
+ Editor_state
.line_height
1235 App
.screen
.check(y
, 'mno', 'screen:3')
1238 function test_up_arrow_moves_cursor()
1239 -- display the first 3 lines with the cursor on the bottom line
1240 App
.screen
.init
{width
=120, height
=60}
1241 Editor_state
= edit
.initialize_test_state()
1242 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1243 Text
.redraw_all(Editor_state
)
1244 Editor_state
.cursor1
= {line
=3, pos
=1}
1245 Editor_state
.screen_top1
= {line
=1, pos
=1}
1246 Editor_state
.screen_bottom1
= {}
1247 edit
.draw(Editor_state
)
1248 local y
= Editor_state
.top
1249 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1250 y
= y
+ Editor_state
.line_height
1251 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1252 y
= y
+ Editor_state
.line_height
1253 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1254 -- after hitting the up arrow the cursor moves up by 1 line
1255 edit
.run_after_keychord(Editor_state
, 'up')
1256 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1257 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor')
1258 -- the screen is unchanged
1259 y
= Editor_state
.top
1260 App
.screen
.check(y
, 'abc', 'screen:1')
1261 y
= y
+ Editor_state
.line_height
1262 App
.screen
.check(y
, 'def', 'screen:2')
1263 y
= y
+ Editor_state
.line_height
1264 App
.screen
.check(y
, 'ghi', 'screen:3')
1267 function test_up_arrow_scrolls_up_by_one_line()
1268 -- display the lines 2/3/4 with the cursor on line 2
1269 App
.screen
.init
{width
=120, height
=60}
1270 Editor_state
= edit
.initialize_test_state()
1271 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1272 Text
.redraw_all(Editor_state
)
1273 Editor_state
.cursor1
= {line
=2, pos
=1}
1274 Editor_state
.screen_top1
= {line
=2, pos
=1}
1275 Editor_state
.screen_bottom1
= {}
1276 edit
.draw(Editor_state
)
1277 local y
= Editor_state
.top
1278 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1279 y
= y
+ Editor_state
.line_height
1280 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1281 y
= y
+ Editor_state
.line_height
1282 App
.screen
.check(y
, 'jkl', 'baseline/screen:3')
1283 -- after hitting the up arrow the screen scrolls up by one line
1284 edit
.run_after_keychord(Editor_state
, 'up')
1285 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1286 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1287 y
= Editor_state
.top
1288 App
.screen
.check(y
, 'abc', 'screen:1')
1289 y
= y
+ Editor_state
.line_height
1290 App
.screen
.check(y
, 'def', 'screen:2')
1291 y
= y
+ Editor_state
.line_height
1292 App
.screen
.check(y
, 'ghi', 'screen:3')
1295 function test_up_arrow_scrolls_up_by_one_screen_line()
1296 -- display lines starting from second screen line of a line
1297 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1298 Editor_state
= edit
.initialize_test_state()
1299 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1300 Text
.redraw_all(Editor_state
)
1301 Editor_state
.cursor1
= {line
=3, pos
=6}
1302 Editor_state
.screen_top1
= {line
=3, pos
=5}
1303 Editor_state
.screen_bottom1
= {}
1304 edit
.draw(Editor_state
)
1305 local y
= Editor_state
.top
1306 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1307 y
= y
+ Editor_state
.line_height
1308 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1309 -- after hitting the up arrow the screen scrolls up to first screen line
1310 edit
.run_after_keychord(Editor_state
, 'up')
1311 y
= Editor_state
.top
1312 App
.screen
.check(y
, 'ghi ', 'screen:1')
1313 y
= y
+ Editor_state
.line_height
1314 App
.screen
.check(y
, 'jkl', 'screen:2')
1315 y
= y
+ Editor_state
.line_height
1316 App
.screen
.check(y
, 'mno', 'screen:3')
1317 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1318 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1319 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1320 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1323 function test_up_arrow_scrolls_up_to_final_screen_line()
1324 -- display lines starting just after a long line
1325 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1326 Editor_state
= edit
.initialize_test_state()
1327 Editor_state
.lines
= load_array
{'abc def', 'ghi', 'jkl', 'mno'}
1328 Text
.redraw_all(Editor_state
)
1329 Editor_state
.cursor1
= {line
=2, pos
=1}
1330 Editor_state
.screen_top1
= {line
=2, pos
=1}
1331 Editor_state
.screen_bottom1
= {}
1332 edit
.draw(Editor_state
)
1333 local y
= Editor_state
.top
1334 App
.screen
.check(y
, 'ghi', 'baseline/screen:1')
1335 y
= y
+ Editor_state
.line_height
1336 App
.screen
.check(y
, 'jkl', 'baseline/screen:2')
1337 y
= y
+ Editor_state
.line_height
1338 App
.screen
.check(y
, 'mno', 'baseline/screen:3')
1339 -- after hitting the up arrow the screen scrolls up to final screen line of previous line
1340 edit
.run_after_keychord(Editor_state
, 'up')
1341 y
= Editor_state
.top
1342 App
.screen
.check(y
, 'def', 'screen:1')
1343 y
= y
+ Editor_state
.line_height
1344 App
.screen
.check(y
, 'ghi', 'screen:2')
1345 y
= y
+ Editor_state
.line_height
1346 App
.screen
.check(y
, 'jkl', 'screen:3')
1347 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top:line')
1348 check_eq(Editor_state
.screen_top1
.pos
, 5, 'screen_top:pos')
1349 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1350 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1353 function test_up_arrow_scrolls_up_to_empty_line()
1354 -- display a screenful of text with an empty line just above it outside the screen
1355 App
.screen
.init
{width
=120, height
=60}
1356 Editor_state
= edit
.initialize_test_state()
1357 Editor_state
.lines
= load_array
{'', 'abc', 'def', 'ghi', 'jkl'}
1358 Text
.redraw_all(Editor_state
)
1359 Editor_state
.cursor1
= {line
=2, pos
=1}
1360 Editor_state
.screen_top1
= {line
=2, pos
=1}
1361 Editor_state
.screen_bottom1
= {}
1362 edit
.draw(Editor_state
)
1363 local y
= Editor_state
.top
1364 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1365 y
= y
+ Editor_state
.line_height
1366 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1367 y
= y
+ Editor_state
.line_height
1368 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1369 -- after hitting the up arrow the screen scrolls up by one line
1370 edit
.run_after_keychord(Editor_state
, 'up')
1371 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1372 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1373 y
= Editor_state
.top
1375 y
= y
+ Editor_state
.line_height
1376 App
.screen
.check(y
, 'abc', 'screen:2')
1377 y
= y
+ Editor_state
.line_height
1378 App
.screen
.check(y
, 'def', 'screen:3')
1381 function test_pageup()
1382 App
.screen
.init
{width
=120, height
=45}
1383 Editor_state
= edit
.initialize_test_state()
1384 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi'}
1385 Text
.redraw_all(Editor_state
)
1386 Editor_state
.cursor1
= {line
=2, pos
=1}
1387 Editor_state
.screen_top1
= {line
=2, pos
=1}
1388 Editor_state
.screen_bottom1
= {}
1389 -- initially the last two lines are displayed
1390 edit
.draw(Editor_state
)
1391 local y
= Editor_state
.top
1392 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1393 y
= y
+ Editor_state
.line_height
1394 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1395 -- after pageup the cursor goes to first line
1396 edit
.run_after_keychord(Editor_state
, 'pageup')
1397 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1398 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1399 y
= Editor_state
.top
1400 App
.screen
.check(y
, 'abc', 'screen:1')
1401 y
= y
+ Editor_state
.line_height
1402 App
.screen
.check(y
, 'def', 'screen:2')
1405 function test_pageup_scrolls_up_by_screen_line()
1406 -- display the first three lines with the cursor on the bottom line
1407 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1408 Editor_state
= edit
.initialize_test_state()
1409 Editor_state
.lines
= load_array
{'abc def', 'ghi', 'jkl', 'mno'}
1410 Text
.redraw_all(Editor_state
)
1411 Editor_state
.cursor1
= {line
=2, pos
=1}
1412 Editor_state
.screen_top1
= {line
=2, pos
=1}
1413 Editor_state
.screen_bottom1
= {}
1414 edit
.draw(Editor_state
)
1415 local y
= Editor_state
.top
1416 App
.screen
.check(y
, 'ghi', 'baseline/screen:1')
1417 y
= y
+ Editor_state
.line_height
1418 App
.screen
.check(y
, 'jkl', 'baseline/screen:2')
1419 y
= y
+ Editor_state
.line_height
1420 App
.screen
.check(y
, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1421 -- after hitting the page-up key the screen scrolls up to top
1422 edit
.run_after_keychord(Editor_state
, 'pageup')
1423 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1424 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1425 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1426 y
= Editor_state
.top
1427 App
.screen
.check(y
, 'abc ', 'screen:1')
1428 y
= y
+ Editor_state
.line_height
1429 App
.screen
.check(y
, 'def', 'screen:2')
1430 y
= y
+ Editor_state
.line_height
1431 App
.screen
.check(y
, 'ghi', 'screen:3')
1434 function test_pageup_scrolls_up_from_middle_screen_line()
1435 -- display a few lines starting from the middle of a line (Editor_state.cursor1.pos > 1)
1436 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1437 Editor_state
= edit
.initialize_test_state()
1438 Editor_state
.lines
= load_array
{'abc def', 'ghi jkl', 'mno'}
1439 Text
.redraw_all(Editor_state
)
1440 Editor_state
.cursor1
= {line
=2, pos
=5}
1441 Editor_state
.screen_top1
= {line
=2, pos
=5}
1442 Editor_state
.screen_bottom1
= {}
1443 edit
.draw(Editor_state
)
1444 local y
= Editor_state
.top
1445 App
.screen
.check(y
, 'jkl', 'baseline/screen:2')
1446 y
= y
+ Editor_state
.line_height
1447 App
.screen
.check(y
, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1448 -- after hitting the page-up key the screen scrolls up to top
1449 edit
.run_after_keychord(Editor_state
, 'pageup')
1450 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1451 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1452 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1453 y
= Editor_state
.top
1454 App
.screen
.check(y
, 'abc ', 'screen:1')
1455 y
= y
+ Editor_state
.line_height
1456 App
.screen
.check(y
, 'def', 'screen:2')
1457 y
= y
+ Editor_state
.line_height
1458 App
.screen
.check(y
, 'ghi ', 'screen:3')
1461 function test_enter_on_bottom_line_scrolls_down()
1462 -- display a few lines with cursor on bottom line
1463 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1464 Editor_state
= edit
.initialize_test_state()
1465 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1466 Text
.redraw_all(Editor_state
)
1467 Editor_state
.cursor1
= {line
=3, pos
=2}
1468 Editor_state
.screen_top1
= {line
=1, pos
=1}
1469 Editor_state
.screen_bottom1
= {}
1470 edit
.draw(Editor_state
)
1471 local y
= Editor_state
.top
1472 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1473 y
= y
+ Editor_state
.line_height
1474 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1475 y
= y
+ Editor_state
.line_height
1476 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1477 -- after hitting the enter key the screen scrolls down
1478 edit
.run_after_keychord(Editor_state
, 'return')
1479 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1480 check_eq(Editor_state
.cursor1
.line
, 4, 'cursor:line')
1481 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1482 y
= Editor_state
.top
1483 App
.screen
.check(y
, 'def', 'screen:1')
1484 y
= y
+ Editor_state
.line_height
1485 App
.screen
.check(y
, 'g', 'screen:2')
1486 y
= y
+ Editor_state
.line_height
1487 App
.screen
.check(y
, 'hi', 'screen:3')
1490 function test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1491 -- display just the bottom line on screen
1492 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1493 Editor_state
= edit
.initialize_test_state()
1494 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1495 Text
.redraw_all(Editor_state
)
1496 Editor_state
.cursor1
= {line
=4, pos
=2}
1497 Editor_state
.screen_top1
= {line
=4, pos
=1}
1498 Editor_state
.screen_bottom1
= {}
1499 edit
.draw(Editor_state
)
1500 local y
= Editor_state
.top
1501 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1502 -- after hitting the enter key the screen does not scroll down
1503 edit
.run_after_keychord(Editor_state
, 'return')
1504 check_eq(Editor_state
.screen_top1
.line
, 4, 'screen_top')
1505 check_eq(Editor_state
.cursor1
.line
, 5, 'cursor:line')
1506 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1507 y
= Editor_state
.top
1508 App
.screen
.check(y
, 'j', 'screen:1')
1509 y
= y
+ Editor_state
.line_height
1510 App
.screen
.check(y
, 'kl', 'screen:2')
1513 function test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom()
1514 -- display just an empty bottom line on screen
1515 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1516 Editor_state
= edit
.initialize_test_state()
1517 Editor_state
.lines
= load_array
{'abc', ''}
1518 Text
.redraw_all(Editor_state
)
1519 Editor_state
.cursor1
= {line
=2, pos
=1}
1520 Editor_state
.screen_top1
= {line
=2, pos
=1}
1521 Editor_state
.screen_bottom1
= {}
1522 edit
.draw(Editor_state
)
1523 -- after hitting the inserting_text key the screen does not scroll down
1524 edit
.run_after_text_input(Editor_state
, 'a')
1525 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1526 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1527 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
1528 local y
= Editor_state
.top
1529 App
.screen
.check(y
, 'a', 'screen:1')
1532 function test_typing_on_bottom_line_scrolls_down()
1533 -- display a few lines with cursor on bottom line
1534 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1535 Editor_state
= edit
.initialize_test_state()
1536 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1537 Text
.redraw_all(Editor_state
)
1538 Editor_state
.cursor1
= {line
=3, pos
=4}
1539 Editor_state
.screen_top1
= {line
=1, pos
=1}
1540 Editor_state
.screen_bottom1
= {}
1541 edit
.draw(Editor_state
)
1542 local y
= Editor_state
.top
1543 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1544 y
= y
+ Editor_state
.line_height
1545 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1546 y
= y
+ Editor_state
.line_height
1547 App
.screen
.check(y
, 'ghi', 'baseline/screen:3')
1548 -- after typing something the line wraps and the screen scrolls down
1549 edit
.run_after_text_input(Editor_state
, 'j')
1550 edit
.run_after_text_input(Editor_state
, 'k')
1551 edit
.run_after_text_input(Editor_state
, 'l')
1552 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1553 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1554 check_eq(Editor_state
.cursor1
.pos
, 7, 'cursor:pos')
1555 y
= Editor_state
.top
1556 App
.screen
.check(y
, 'def', 'screen:1')
1557 y
= y
+ Editor_state
.line_height
1558 App
.screen
.check(y
, 'ghij', 'screen:2')
1559 y
= y
+ Editor_state
.line_height
1560 App
.screen
.check(y
, 'kl', 'screen:3')
1563 function test_left_arrow_scrolls_up_in_wrapped_line()
1564 -- display lines starting from second screen line of a line
1565 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1566 Editor_state
= edit
.initialize_test_state()
1567 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1568 Text
.redraw_all(Editor_state
)
1569 Editor_state
.screen_top1
= {line
=3, pos
=5}
1570 Editor_state
.screen_bottom1
= {}
1571 -- cursor is at top of screen
1572 Editor_state
.cursor1
= {line
=3, pos
=5}
1573 edit
.draw(Editor_state
)
1574 local y
= Editor_state
.top
1575 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1576 y
= y
+ Editor_state
.line_height
1577 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1578 -- after hitting the left arrow the screen scrolls up to first screen line
1579 edit
.run_after_keychord(Editor_state
, 'left')
1580 y
= Editor_state
.top
1581 App
.screen
.check(y
, 'ghi ', 'screen:1')
1582 y
= y
+ Editor_state
.line_height
1583 App
.screen
.check(y
, 'jkl', 'screen:2')
1584 y
= y
+ Editor_state
.line_height
1585 App
.screen
.check(y
, 'mno', 'screen:3')
1586 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1587 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1588 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1589 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
1592 function test_right_arrow_scrolls_down_in_wrapped_line()
1593 -- display the first three lines with the cursor on the bottom line
1594 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1595 Editor_state
= edit
.initialize_test_state()
1596 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1597 Text
.redraw_all(Editor_state
)
1598 Editor_state
.screen_top1
= {line
=1, pos
=1}
1599 Editor_state
.screen_bottom1
= {}
1600 -- cursor is at bottom right of screen
1601 Editor_state
.cursor1
= {line
=3, pos
=5}
1602 edit
.draw(Editor_state
)
1603 local y
= Editor_state
.top
1604 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1605 y
= y
+ Editor_state
.line_height
1606 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1607 y
= y
+ Editor_state
.line_height
1608 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1609 -- after hitting the right arrow the screen scrolls down by one line
1610 edit
.run_after_keychord(Editor_state
, 'right')
1611 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1612 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1613 check_eq(Editor_state
.cursor1
.pos
, 6, 'cursor:pos')
1614 y
= Editor_state
.top
1615 App
.screen
.check(y
, 'def', 'screen:1')
1616 y
= y
+ Editor_state
.line_height
1617 App
.screen
.check(y
, 'ghi ', 'screen:2')
1618 y
= y
+ Editor_state
.line_height
1619 App
.screen
.check(y
, 'jkl', 'screen:3')
1622 function test_home_scrolls_up_in_wrapped_line()
1623 -- display lines starting from second screen line of a line
1624 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1625 Editor_state
= edit
.initialize_test_state()
1626 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1627 Text
.redraw_all(Editor_state
)
1628 Editor_state
.screen_top1
= {line
=3, pos
=5}
1629 Editor_state
.screen_bottom1
= {}
1630 -- cursor is at top of screen
1631 Editor_state
.cursor1
= {line
=3, pos
=5}
1632 edit
.draw(Editor_state
)
1633 local y
= Editor_state
.top
1634 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1635 y
= y
+ Editor_state
.line_height
1636 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1637 -- after hitting home the screen scrolls up to first screen line
1638 edit
.run_after_keychord(Editor_state
, 'home')
1639 y
= Editor_state
.top
1640 App
.screen
.check(y
, 'ghi ', 'screen:1')
1641 y
= y
+ Editor_state
.line_height
1642 App
.screen
.check(y
, 'jkl', 'screen:2')
1643 y
= y
+ Editor_state
.line_height
1644 App
.screen
.check(y
, 'mno', 'screen:3')
1645 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1646 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1647 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1648 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1651 function test_end_scrolls_down_in_wrapped_line()
1652 -- display the first three lines with the cursor on the bottom line
1653 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1654 Editor_state
= edit
.initialize_test_state()
1655 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1656 Text
.redraw_all(Editor_state
)
1657 Editor_state
.screen_top1
= {line
=1, pos
=1}
1658 Editor_state
.screen_bottom1
= {}
1659 -- cursor is at bottom right of screen
1660 Editor_state
.cursor1
= {line
=3, pos
=5}
1661 edit
.draw(Editor_state
)
1662 local y
= Editor_state
.top
1663 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1664 y
= y
+ Editor_state
.line_height
1665 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1666 y
= y
+ Editor_state
.line_height
1667 App
.screen
.check(y
, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
1668 -- after hitting end the screen scrolls down by one line
1669 edit
.run_after_keychord(Editor_state
, 'end')
1670 check_eq(Editor_state
.screen_top1
.line
, 2, 'screen_top')
1671 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1672 check_eq(Editor_state
.cursor1
.pos
, 8, 'cursor:pos')
1673 y
= Editor_state
.top
1674 App
.screen
.check(y
, 'def', 'screen:1')
1675 y
= y
+ Editor_state
.line_height
1676 App
.screen
.check(y
, 'ghi ', 'screen:2')
1677 y
= y
+ Editor_state
.line_height
1678 App
.screen
.check(y
, 'jkl', 'screen:3')
1681 function test_position_cursor_on_recently_edited_wrapping_line()
1682 -- draw a line wrapping over 2 screen lines
1683 App
.screen
.init
{width
=100, height
=200}
1684 Editor_state
= edit
.initialize_test_state()
1685 Editor_state
.lines
= load_array
{'abc def ghi jkl mno pqr ', 'xyz'}
1686 Text
.redraw_all(Editor_state
)
1687 Editor_state
.cursor1
= {line
=1, pos
=25}
1688 Editor_state
.screen_top1
= {line
=1, pos
=1}
1689 Editor_state
.screen_bottom1
= {}
1690 edit
.draw(Editor_state
)
1691 local y
= Editor_state
.top
1692 App
.screen
.check(y
, 'abc def ghi ', 'baseline1/screen:1')
1693 y
= y
+ Editor_state
.line_height
1694 App
.screen
.check(y
, 'jkl mno pqr ', 'baseline1/screen:2')
1695 y
= y
+ Editor_state
.line_height
1696 App
.screen
.check(y
, 'xyz', 'baseline1/screen:3')
1697 -- add to the line until it's wrapping over 3 screen lines
1698 edit
.run_after_text_input(Editor_state
, 's')
1699 edit
.run_after_text_input(Editor_state
, 't')
1700 edit
.run_after_text_input(Editor_state
, 'u')
1701 check_eq(Editor_state
.cursor1
.pos
, 28, 'cursor:pos')
1702 y
= Editor_state
.top
1703 App
.screen
.check(y
, 'abc def ghi ', 'baseline2/screen:1')
1704 y
= y
+ Editor_state
.line_height
1705 App
.screen
.check(y
, 'jkl mno pqr ', 'baseline2/screen:2')
1706 y
= y
+ Editor_state
.line_height
1707 App
.screen
.check(y
, 'stu', 'baseline2/screen:3')
1708 -- try to move the cursor earlier in the third screen line by clicking the mouse
1709 edit
.run_after_mouse_release(Editor_state
, Editor_state
.left
+2,Editor_state
.top
+Editor_state
.line_height
*2+5, 1)
1710 -- cursor should move
1711 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1712 check_eq(Editor_state
.cursor1
.pos
, 25, 'cursor:pos')
1715 function test_backspace_can_scroll_up()
1716 -- display the lines 2/3/4 with the cursor on line 2
1717 App
.screen
.init
{width
=120, height
=60}
1718 Editor_state
= edit
.initialize_test_state()
1719 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl'}
1720 Text
.redraw_all(Editor_state
)
1721 Editor_state
.cursor1
= {line
=2, pos
=1}
1722 Editor_state
.screen_top1
= {line
=2, pos
=1}
1723 Editor_state
.screen_bottom1
= {}
1724 edit
.draw(Editor_state
)
1725 local y
= Editor_state
.top
1726 App
.screen
.check(y
, 'def', 'baseline/screen:1')
1727 y
= y
+ Editor_state
.line_height
1728 App
.screen
.check(y
, 'ghi', 'baseline/screen:2')
1729 y
= y
+ Editor_state
.line_height
1730 App
.screen
.check(y
, 'jkl', 'baseline/screen:3')
1731 -- after hitting backspace the screen scrolls up by one line
1732 edit
.run_after_keychord(Editor_state
, 'backspace')
1733 check_eq(Editor_state
.screen_top1
.line
, 1, 'screen_top')
1734 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor')
1735 y
= Editor_state
.top
1736 App
.screen
.check(y
, 'abcdef', 'screen:1')
1737 y
= y
+ Editor_state
.line_height
1738 App
.screen
.check(y
, 'ghi', 'screen:2')
1739 y
= y
+ Editor_state
.line_height
1740 App
.screen
.check(y
, 'jkl', 'screen:3')
1743 function test_backspace_can_scroll_up_screen_line()
1744 -- display lines starting from second screen line of a line
1745 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1746 Editor_state
= edit
.initialize_test_state()
1747 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi jkl', 'mno'}
1748 Text
.redraw_all(Editor_state
)
1749 Editor_state
.cursor1
= {line
=3, pos
=5}
1750 Editor_state
.screen_top1
= {line
=3, pos
=5}
1751 Editor_state
.screen_bottom1
= {}
1752 edit
.draw(Editor_state
)
1753 local y
= Editor_state
.top
1754 App
.screen
.check(y
, 'jkl', 'baseline/screen:1')
1755 y
= y
+ Editor_state
.line_height
1756 App
.screen
.check(y
, 'mno', 'baseline/screen:2')
1757 -- after hitting backspace the screen scrolls up by one screen line
1758 edit
.run_after_keychord(Editor_state
, 'backspace')
1759 y
= Editor_state
.top
1760 App
.screen
.check(y
, 'ghij', 'screen:1')
1761 y
= y
+ Editor_state
.line_height
1762 App
.screen
.check(y
, 'kl', 'screen:2')
1763 y
= y
+ Editor_state
.line_height
1764 App
.screen
.check(y
, 'mno', 'screen:3')
1765 check_eq(Editor_state
.screen_top1
.line
, 3, 'screen_top:line')
1766 check_eq(Editor_state
.screen_top1
.pos
, 1, 'screen_top:pos')
1767 check_eq(Editor_state
.cursor1
.line
, 3, 'cursor:line')
1768 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
1771 function test_backspace_past_line_boundary()
1772 -- position cursor at start of a (non-first) line
1773 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1774 Editor_state
= edit
.initialize_test_state()
1775 Editor_state
.lines
= load_array
{'abc', 'def'}
1776 Text
.redraw_all(Editor_state
)
1777 Editor_state
.cursor1
= {line
=2, pos
=1}
1778 -- backspace joins with previous line
1779 edit
.run_after_keychord(Editor_state
, 'backspace')
1780 check_eq(Editor_state
.lines
[1].data
, 'abcdef', 'check')
1783 -- some tests for operating over selections created using Shift- chords
1784 -- we're just testing delete_selection, and it works the same for all keys
1786 function test_backspace_over_selection()
1787 -- select just one character within a line with cursor before selection
1788 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1789 Editor_state
= edit
.initialize_test_state()
1790 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1791 Text
.redraw_all(Editor_state
)
1792 Editor_state
.cursor1
= {line
=1, pos
=1}
1793 Editor_state
.selection1
= {line
=1, pos
=2}
1794 -- backspace deletes the selected character, even though it's after the cursor
1795 edit
.run_after_keychord(Editor_state
, 'backspace')
1796 check_eq(Editor_state
.lines
[1].data
, 'bc', 'data')
1797 -- cursor (remains) at start of selection
1798 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1799 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1800 -- selection is cleared
1801 check_nil(Editor_state
.selection1
.line
, 'selection')
1804 function test_backspace_over_selection_reverse()
1805 -- select just one character within a line with cursor after selection
1806 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1807 Editor_state
= edit
.initialize_test_state()
1808 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1809 Text
.redraw_all(Editor_state
)
1810 Editor_state
.cursor1
= {line
=1, pos
=2}
1811 Editor_state
.selection1
= {line
=1, pos
=1}
1812 -- backspace deletes the selected character
1813 edit
.run_after_keychord(Editor_state
, 'backspace')
1814 check_eq(Editor_state
.lines
[1].data
, 'bc', 'data')
1815 -- cursor moves to start of selection
1816 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1817 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1818 -- selection is cleared
1819 check_nil(Editor_state
.selection1
.line
, 'selection')
1822 function test_backspace_over_multiple_lines()
1823 -- select just one character within a line with cursor after selection
1824 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1825 Editor_state
= edit
.initialize_test_state()
1826 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1827 Text
.redraw_all(Editor_state
)
1828 Editor_state
.cursor1
= {line
=1, pos
=2}
1829 Editor_state
.selection1
= {line
=4, pos
=2}
1830 -- backspace deletes the region and joins the remaining portions of lines on either side
1831 edit
.run_after_keychord(Editor_state
, 'backspace')
1832 check_eq(Editor_state
.lines
[1].data
, 'akl', 'data:1')
1833 check_eq(Editor_state
.lines
[2].data
, 'mno', 'data:2')
1834 -- cursor remains at start of selection
1835 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1836 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
1837 -- selection is cleared
1838 check_nil(Editor_state
.selection1
.line
, 'selection')
1841 function test_backspace_to_end_of_line()
1842 -- select region from cursor to end of line
1843 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1844 Editor_state
= edit
.initialize_test_state()
1845 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1846 Text
.redraw_all(Editor_state
)
1847 Editor_state
.cursor1
= {line
=1, pos
=2}
1848 Editor_state
.selection1
= {line
=1, pos
=4}
1849 -- backspace deletes rest of line without joining to any other line
1850 edit
.run_after_keychord(Editor_state
, 'backspace')
1851 check_eq(Editor_state
.lines
[1].data
, 'a', 'data:1')
1852 check_eq(Editor_state
.lines
[2].data
, 'def', 'data:2')
1853 -- cursor remains at start of selection
1854 check_eq(Editor_state
.cursor1
.line
, 1, 'cursor:line')
1855 check_eq(Editor_state
.cursor1
.pos
, 2, 'cursor:pos')
1856 -- selection is cleared
1857 check_nil(Editor_state
.selection1
.line
, 'selection')
1860 function test_backspace_to_start_of_line()
1861 -- select region from cursor to start of line
1862 App
.screen
.init
{width
=Editor_state
.left
+30, height
=60}
1863 Editor_state
= edit
.initialize_test_state()
1864 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', 'jkl', 'mno'}
1865 Text
.redraw_all(Editor_state
)
1866 Editor_state
.cursor1
= {line
=2, pos
=1}
1867 Editor_state
.selection1
= {line
=2, pos
=3}
1868 -- backspace deletes beginning of line without joining to any other line
1869 edit
.run_after_keychord(Editor_state
, 'backspace')
1870 check_eq(Editor_state
.lines
[1].data
, 'abc', 'data:1')
1871 check_eq(Editor_state
.lines
[2].data
, 'f', 'data:2')
1872 -- cursor remains at start of selection
1873 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1874 check_eq(Editor_state
.cursor1
.pos
, 1, 'cursor:pos')
1875 -- selection is cleared
1876 check_nil(Editor_state
.selection1
.line
, 'selection')
1879 function test_undo_insert_text()
1880 App
.screen
.init
{width
=120, height
=60}
1881 Editor_state
= edit
.initialize_test_state()
1882 Editor_state
.lines
= load_array
{'abc', 'def', 'xyz'}
1883 Text
.redraw_all(Editor_state
)
1884 Editor_state
.cursor1
= {line
=2, pos
=4}
1885 Editor_state
.screen_top1
= {line
=1, pos
=1}
1886 Editor_state
.screen_bottom1
= {}
1887 -- insert a character
1888 edit
.draw(Editor_state
)
1889 edit
.run_after_text_input(Editor_state
, 'g')
1890 check_eq(Editor_state
.cursor1
.line
, 2, 'baseline/cursor:line')
1891 check_eq(Editor_state
.cursor1
.pos
, 5, 'baseline/cursor:pos')
1892 check_nil(Editor_state
.selection1
.line
, 'baseline/selection:line')
1893 check_nil(Editor_state
.selection1
.pos
, 'baseline/selection:pos')
1894 local y
= Editor_state
.top
1895 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1896 y
= y
+ Editor_state
.line_height
1897 App
.screen
.check(y
, 'defg', 'baseline/screen:2')
1898 y
= y
+ Editor_state
.line_height
1899 App
.screen
.check(y
, 'xyz', 'baseline/screen:3')
1901 edit
.run_after_keychord(Editor_state
, 'C-z')
1902 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1903 check_eq(Editor_state
.cursor1
.pos
, 4, 'cursor:pos')
1904 check_nil(Editor_state
.selection1
.line
, 'selection:line')
1905 check_nil(Editor_state
.selection1
.pos
, 'selection:pos')
1906 y
= Editor_state
.top
1907 App
.screen
.check(y
, 'abc', 'screen:1')
1908 y
= y
+ Editor_state
.line_height
1909 App
.screen
.check(y
, 'def', 'screen:2')
1910 y
= y
+ Editor_state
.line_height
1911 App
.screen
.check(y
, 'xyz', 'screen:3')
1914 function test_undo_delete_text()
1915 App
.screen
.init
{width
=120, height
=60}
1916 Editor_state
= edit
.initialize_test_state()
1917 Editor_state
.lines
= load_array
{'abc', 'defg', 'xyz'}
1918 Text
.redraw_all(Editor_state
)
1919 Editor_state
.cursor1
= {line
=2, pos
=5}
1920 Editor_state
.screen_top1
= {line
=1, pos
=1}
1921 Editor_state
.screen_bottom1
= {}
1922 -- delete a character
1923 edit
.run_after_keychord(Editor_state
, 'backspace')
1924 check_eq(Editor_state
.cursor1
.line
, 2, 'baseline/cursor:line')
1925 check_eq(Editor_state
.cursor1
.pos
, 4, 'baseline/cursor:pos')
1926 check_nil(Editor_state
.selection1
.line
, 'baseline/selection:line')
1927 check_nil(Editor_state
.selection1
.pos
, 'baseline/selection:pos')
1928 local y
= Editor_state
.top
1929 App
.screen
.check(y
, 'abc', 'baseline/screen:1')
1930 y
= y
+ Editor_state
.line_height
1931 App
.screen
.check(y
, 'def', 'baseline/screen:2')
1932 y
= y
+ Editor_state
.line_height
1933 App
.screen
.check(y
, 'xyz', 'baseline/screen:3')
1935 --? -- after undo, the backspaced key is selected
1936 edit
.run_after_keychord(Editor_state
, 'C-z')
1937 check_eq(Editor_state
.cursor1
.line
, 2, 'cursor:line')
1938 check_eq(Editor_state
.cursor1
.pos
, 5, 'cursor:pos')
1939 check_nil(Editor_state
.selection1
.line
, 'selection:line')
1940 check_nil(Editor_state
.selection1
.pos
, 'selection:pos')
1941 --? check_eq(Editor_state.selection1.line, 2, 'selection:line')
1942 --? check_eq(Editor_state.selection1.pos, 4, 'selection:pos')
1943 y
= Editor_state
.top
1944 App
.screen
.check(y
, 'abc', 'screen:1')
1945 y
= y
+ Editor_state
.line_height
1946 App
.screen
.check(y
, 'defg', 'screen:2')
1947 y
= y
+ Editor_state
.line_height
1948 App
.screen
.check(y
, 'xyz', 'screen:3')
1951 function test_undo_restores_selection()
1952 -- display a line of text with some part selected
1953 App
.screen
.init
{width
=75, height
=80}
1954 Editor_state
= edit
.initialize_test_state()
1955 Editor_state
.lines
= load_array
{'abc'}
1956 Text
.redraw_all(Editor_state
)
1957 Editor_state
.cursor1
= {line
=1, pos
=1}
1958 Editor_state
.selection1
= {line
=1, pos
=2}
1959 Editor_state
.screen_top1
= {line
=1, pos
=1}
1960 Editor_state
.screen_bottom1
= {}
1961 edit
.draw(Editor_state
)
1962 -- delete selected text
1963 edit
.run_after_text_input(Editor_state
, 'x')
1964 check_eq(Editor_state
.lines
[1].data
, 'xbc', 'baseline')
1965 check_nil(Editor_state
.selection1
.line
, 'baseline:selection')
1967 edit
.run_after_keychord(Editor_state
, 'C-z')
1968 edit
.run_after_keychord(Editor_state
, 'C-z')
1969 -- selection is restored
1970 check_eq(Editor_state
.selection1
.line
, 1, 'line')
1971 check_eq(Editor_state
.selection1
.pos
, 2, 'pos')
1974 function test_search()
1975 App
.screen
.init
{width
=120, height
=60}
1976 Editor_state
= edit
.initialize_test_state()
1977 Editor_state
.lines
= load_array
{'abc', 'def', 'ghi', '’deg'} -- contains unicode quote in final line
1978 Text
.redraw_all(Editor_state
)
1979 Editor_state
.cursor1
= {line
=1, pos
=1}
1980 Editor_state
.screen_top1
= {line
=1, pos
=1}
1981 Editor_state
.screen_bottom1
= {}
1982 edit
.draw(Editor_state
)
1983 -- search for a string
1984 edit
.run_after_keychord(Editor_state
, 'C-f')
1985 edit
.run_after_text_input(Editor_state
, 'd')
1986 edit
.run_after_keychord(Editor_state
, 'return')
1987 check_eq(Editor_state
.cursor1
.line
, 2, '1/cursor:line')
1988 check_eq(Editor_state
.cursor1
.pos
, 1, '1/cursor:pos')
1990 Editor_state
.cursor1
= {line
=1, pos
=1}
1991 Editor_state
.screen_top1
= {line
=1, pos
=1}
1992 -- search for second occurrence
1993 edit
.run_after_keychord(Editor_state
, 'C-f')
1994 edit
.run_after_text_input(Editor_state
, 'de')
1995 edit
.run_after_keychord(Editor_state
, 'down')
1996 edit
.run_after_keychord(Editor_state
, 'return')
1997 check_eq(Editor_state
.cursor1
.line
, 4, '2/cursor:line')
1998 check_eq(Editor_state
.cursor1
.pos
, 2, '2/cursor:pos')
2001 function test_search_upwards()
2002 App
.screen
.init
{width
=120, height
=60}
2003 Editor_state
= edit
.initialize_test_state()
2004 Editor_state
.lines
= load_array
{'’abc', 'abd'} -- contains unicode quote
2005 Text
.redraw_all(Editor_state
)
2006 Editor_state
.cursor1
= {line
=2, pos
=1}
2007 Editor_state
.screen_top1
= {line
=1, pos
=1}
2008 Editor_state
.screen_bottom1
= {}
2009 edit
.draw(Editor_state
)
2010 -- search for a string
2011 edit
.run_after_keychord(Editor_state
, 'C-f')
2012 edit
.run_after_text_input(Editor_state
, 'a')
2013 -- search for previous occurrence
2014 edit
.run_after_keychord(Editor_state
, 'up')
2015 check_eq(Editor_state
.cursor1
.line
, 1, '2/cursor:line')
2016 check_eq(Editor_state
.cursor1
.pos
, 2, '2/cursor:pos')
2019 function test_search_wrap()
2020 App
.screen
.init
{width
=120, height
=60}
2021 Editor_state
= edit
.initialize_test_state()
2022 Editor_state
.lines
= load_array
{'’abc', 'def'} -- contains unicode quote in first line
2023 Text
.redraw_all(Editor_state
)
2024 Editor_state
.cursor1
= {line
=2, pos
=1}
2025 Editor_state
.screen_top1
= {line
=1, pos
=1}
2026 Editor_state
.screen_bottom1
= {}
2027 edit
.draw(Editor_state
)
2028 -- search for a string
2029 edit
.run_after_keychord(Editor_state
, 'C-f')
2030 edit
.run_after_text_input(Editor_state
, 'a')
2031 edit
.run_after_keychord(Editor_state
, 'return')
2033 check_eq(Editor_state
.cursor1
.line
, 1, '1/cursor:line')
2034 check_eq(Editor_state
.cursor1
.pos
, 2, '1/cursor:pos')
2037 function test_search_wrap_upwards()
2038 App
.screen
.init
{width
=120, height
=60}
2039 Editor_state
= edit
.initialize_test_state()
2040 Editor_state
.lines
= load_array
{'abc ’abd'} -- contains unicode quote
2041 Text
.redraw_all(Editor_state
)
2042 Editor_state
.cursor1
= {line
=1, pos
=1}
2043 Editor_state
.screen_top1
= {line
=1, pos
=1}
2044 Editor_state
.screen_bottom1
= {}
2045 edit
.draw(Editor_state
)
2046 -- search upwards for a string
2047 edit
.run_after_keychord(Editor_state
, 'C-f')
2048 edit
.run_after_text_input(Editor_state
, 'a')
2049 edit
.run_after_keychord(Editor_state
, 'up')
2051 check_eq(Editor_state
.cursor1
.line
, 1, '1/cursor:line')
2052 check_eq(Editor_state
.cursor1
.pos
, 6, '1/cursor:pos')