split up editor tests between LÖVE 11 and LÖVE 12
Dependencies
- [2]
TUN6TDO4give a test a unique name - [3]
KVJTEXMSfix all tests in LÖVE v12 - [4]
GJ4LBCIEstreamline button.lua - [5]
J7A5ROM3bugfix in cursor positioning - [6]
2O4OXP5Xcomment - [7]
4FTOQOPZbugfix #2 in search UI - [8]
26V2B2QHclarify a misleading test - [9]
VG75U7IMbugfix: typing should delete highlighted text - [10]
QAMVLUK2fix a crash involving mouse and drawings - [11]
T3B4NLV3include a unit test - [12]
DSLD74DKlots more tests - [13]
3TCZ7ADHmove - [14]
4WAFGF4Zselection bugfix - [15]
LK4ZW4BBbugfix - [16]
7NQCCB34. - [17]
LXTTOB33extract a couple of files - [18]
JFFUF5ALoverride mouse state lookups in tests - [19]
5DOTWNVMright margin - [20]
AJP4OSTJnew test - [21]
YFW4MNNPhandle wrapping lines - [22]
MYC7XR5Qbugfix: lines that aren't drawn from the start - [23]
VSBSWTE4bugfix: where cursor is drawn - [24]
CIQN2MDEbugfix: typing a capital letter deletes selection - [25]
FYS7TCDWbugfix - [26]
GJLOKCYKbugfix: clicking past end of screen line - [27]
QKAMUWSBanother bugfix in scrolling while inserting text - [28]
UHB4GARJleft/right margin -> left/right coordinates - [29]
7XERS4UFmore decoupling editor tests from App - [30]
EKKFWP4Dbugfix: couple of margin-relative computations - [31]
M6TH7VSZrip out notion of Line_width - [32]
LF7BWEG4group all editor globals - [33]
AQQQNDTLyet another bugfix in selection management - [34]
XI5OALQXsome redundant calls - [35]
DGK5BPVIbugfix: UTF-8 in compute_fragments - [36]
APYPFFS3call edit rather than App callbacks in tests - [37]
EETIR4GXbugfix: skip over drawings when searching - [38]
5BMR5HRTclick to the left of a line - [39]
GDAWPFAVmore streamlined test names - [40]
CUIV2LE5some typos - [41]
KMRJOSLYbugfix: delete selection before pasting - [42]
GL4Q5WCVkeep text from overflowing right margin - [43]
LLAOOMULbugfix: search upwards - [44]
CVSRHMJ2experiment: slightly adaptive scrolling - [45]
RMKMPFT5fix a corner case when selecting text - [46]
WAR3HXHTtest both ways of selecting text with mouse - [47]
C45WCXJ2keep drawings within the line width slider as well - [48]
4J2L6JMRbugfix: deleting a selection spanning pages - [49]
2JLVAYHBstart decoupling editor tests from App - [50]
IFTYOERMline.y -> line_cache.starty in a few more places - [51]
62JEPVQ3bugfix: backspace from start of final line - [52]
HPVT467Winitialize contains test state - [53]
AMOPICKVbugfix: check after cursor on same line when searching upwards - [54]
4VKEE43Zbugfix - [55]
DFGPHG5Toverzealous search-and-replace - [56]
ORRSP7FVdeduce test names on failures - [57]
3OC7AIC7exclude left margin from my word-split heuristic - [58]
2CK5QI7Wmake love event names consistent - [59]
WPNRIC7Dmore decoupling editor tests from App - [60]
FZBXBUFFbugfix: search - [61]
SWZAQHGRbugfix: up arrow when line above is a drawing - [62]
LNUHQOGHstart passing in Editor_state explicitly - [63]
AOIRVVJArevert selection logic to before commit 3ffc2ed8f - [64]
Y4SPXCM3bugfix: pagedown was sometimes bouncing up - [65]
MSOQI3A5bugfix: check before cursor on same line - [66]
7EQLPB3Obugfix: don't delete selection when moving cursor - [67]
HTWAM4NZbugfix: scrolling in left/right movements - [68]
2LC3BM2Nsupport other whitespace chars in word movements - [69]
TGZAJUEFbring back a set of constants - [70]
ZLJGZYQGselect text with shift + mouseclick - [71]
IWYLK45Kclicking to the right of a line within line width - [72]
NUZFHX6Iflesh out some tests for word movements - [73]
CNCYMM6Amake test initializations a little more obvious - [74]
356GY7IQunify two similar functions - [75]
I64IPGJXavoid saving fragments in lines - [76]
6XCJX4DZbugfix: inscript's bug - [77]
7RKFA3VAfailing test now looks realistic - [78]
GNKUD23Iget rid of recent_mouse - [79]
FHNPQBLKmore carefully pass the 'key' arg around - [80]
LAW2O3NWextract variable Margin_left - [81]
656FM555bugfix: clear selection when clicking above or below lines - [82]
2TCIWW6Zstop caching starty - [83]
FQZ3U3YAstreamline one more test name - [84]
EMHRPJ3Rno, that's not right - [85]
V3EABA35skip multiple consecutive whitespace - [86]
EXGM6MIEdelete some duplicate initialization - [87]
2MPQO2ST. - [88]
ZZ2B5RPQextract variables for drawing padding - [89]
DRFE3B3Zmouse buttons are integers, not strings - [90]
P6SYWBLBremove a duplicate test - [91]
KOTI3MFGbugfix in previous commit - [92]
2H76FV5Sbugfix: searching files containing unicode - [93]
ILOA5BYFseparate data structure for each line's cache data - [94]
S2YQBEYCsnapshot: test for a new regression - [95]
NZKYPBSKcheck for scroll when just typing - [96]
6RYLD5ONchange how we handle clicks above top margin - [97]
ZS5IYZH5stop caching screen_bottom1 - [98]
H3ECRBXFbugfix: clicking on empty lines - [99]
JLU2RMC4allow Text.nearest_pos_less_than to return 0 - [100]
O7YTBRQYbugfix: restart search on backspace - [101]
Y2ZIPXEMnew test - [*]
R5QXEHUIsomebody stop me
Change contents
- file addition: text_tests_love12.lua[103.2]
function test_initial_state()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{}Text.redraw_all(Editor_state)edit.draw(Editor_state)check_eq(#Editor_state.lines, 1, '#lines')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')endfunction test_click_to_create_drawing()App.screen.init{width=800, height=600}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{}Text.redraw_all(Editor_state)edit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, 8,Editor_state.top+8, 1)-- cursor skips drawing to always remain on textcheck_eq(#Editor_state.lines, 2, '#lines')check_eq(Editor_state.cursor1.line, 2, 'cursor')endfunction test_backspace_to_delete_drawing()-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'```lines', '```', ''}Text.redraw_all(Editor_state)-- cursor is on text as always (outside tests this will get initialized correctly)Editor_state.cursor1.line = 2-- backspacing deletes the drawingedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(#Editor_state.lines, 1, '#lines')check_eq(Editor_state.cursor1.line, 1, 'cursor')endfunction test_backspace_from_start_of_final_line()-- display final line of text with cursor at start of itApp.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Editor_state.screen_top1 = {line=2, pos=1}Editor_state.cursor1 = {line=2, pos=1}Text.redraw_all(Editor_state)-- backspace scrolls upedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(#Editor_state.lines, 1, '#lines')check_eq(Editor_state.cursor1.line, 1, 'cursor')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')endfunction test_insert_first_character()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{}Text.redraw_all(Editor_state)edit.draw(Editor_state)edit.run_after_text_input(Editor_state, 'a')local y = Editor_state.topApp.screen.check(y, 'a', 'screen:1')endfunction test_press_ctrl()-- press ctrl while the cursor is on textApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{''}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.run_after_keychord(Editor_state, 'C-m', 'm')endfunction test_move_left()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'a'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_move_right()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'a'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'right', 'right')check_eq(Editor_state.cursor1.pos, 2, 'check')endfunction test_move_left_to_previous_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'left', 'left')check_eq(Editor_state.cursor1.line, 1, 'line')check_eq(Editor_state.cursor1.pos, 4, 'pos') -- past end of lineendfunction test_move_right_to_next_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4} -- past end of lineedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'right', 'right')check_eq(Editor_state.cursor1.line, 2, 'line')check_eq(Editor_state.cursor1.pos, 1, 'pos')endfunction test_move_to_start_of_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=3}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_move_to_start_of_previous_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4} -- at the space between wordsedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_skip_to_previous_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=5} -- at the start of second wordedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_skip_past_tab_to_previous_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def\tghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=10} -- within third wordedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 9, 'check')endfunction test_skip_multiple_spaces_to_previous_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=6} -- at the start of second wordedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_move_to_start_of_word_on_previous_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.line, 1, 'line')check_eq(Editor_state.cursor1.pos, 5, 'pos')endfunction test_move_past_end_of_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.pos, 4, 'check')endfunction test_skip_to_next_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4} -- at the space between wordsedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.pos, 8, 'check')endfunction test_skip_past_tab_to_next_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc\tdef'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1} -- at the space between wordsedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.pos, 4, 'check')endfunction test_skip_multiple_spaces_to_next_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4} -- at the start of second wordedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.pos, 9, 'check')endfunction test_move_past_end_of_word_on_next_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=8}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.line, 2, 'line')check_eq(Editor_state.cursor1.pos, 4, 'pos')endfunction test_click_moves_cursor()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each lineedit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')-- selection is empty to avoid perturbing future editscheck_nil(Editor_state.selection1.line, 'selection:line')check_nil(Editor_state.selection1.pos, 'selection:pos')endfunction test_click_to_left_of_line()-- display a line with the cursor in the middleApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=3}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}-- click to the left of the lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left-4,Editor_state.top+5, 1)-- cursor moves to start of linecheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_click_takes_margins_into_account()-- display two lines with cursor on one of themApp.screen.init{width=100, height=80}Editor_state = edit.initialize_test_state()Editor_state.left = 50 -- occupy only right side of screenEditor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}-- click on the other lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- cursor movescheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_click_on_empty_line()-- display two lines with the first one emptyApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}-- click on the empty lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- cursor movescheck_eq(Editor_state.cursor1.line, 1, 'cursor')-- selection remains emptycheck_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_click_below_final_line_of_file()-- display one lineApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}-- click below first lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+50, 1)-- cursor goes to bottomcheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')-- selection remains emptycheck_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_draw_text()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_draw_wrapping_text()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'defgh', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'de', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'fgh', 'screen:3')endfunction test_draw_word_wrapping_text()App.screen.init{width=60, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_click_on_wrapping_line()-- display two screen lines with cursor on one of themApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=20}Editor_state.screen_top1 = {line=1, pos=1}-- click on the other lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- cursor movescheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_click_on_wrapping_line_takes_margins_into_account()-- display two screen lines with cursor on one of themApp.screen.init{width=100, height=80}Editor_state = edit.initialize_test_state()Editor_state.left = 50 -- occupy only right side of screenEditor_state.lines = load_array{'abc def ghi jkl mno pqr stu'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=20}Editor_state.screen_top1 = {line=1, pos=1}-- click on the other lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- cursor movescheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_draw_text_wrapping_within_word()-- arrange a screen line that needs to be split within a wordApp.screen.init{width=60, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abcd e fghijk', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abcd ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'e fgh', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ijk', 'screen:3')endfunction test_draw_wrapping_text_containing_non_ascii()-- draw a long line containing non-ASCIIApp.screen.init{width=60, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'madam I’m adam', 'xyz'} -- notice the non-ASCII apostropheText.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'mad', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'am I', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, '’m a', 'screen:3')endfunction test_click_past_end_of_screen_line()-- display a wrapping lineApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()-- 12345678901234Editor_state.lines = load_array{"madam I'm adam"}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'madam ', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, "I'm ad", 'baseline/screen:2')y = y + Editor_state.line_height-- click past end of second screen lineedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of screen line (one more than final character shown)check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 13, 'cursor:pos')endfunction test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen()-- display a wrapping line from its second screen lineApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()-- 12345678901234Editor_state.lines = load_array{"madam I'm adam"}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=8}Editor_state.screen_top1 = {line=1, pos=7}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, "I'm ad", 'baseline/screen:2')y = y + Editor_state.line_height-- click past end of second screen lineedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of screen line (one more than final character shown)check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 13, 'cursor:pos')endfunction test_click_past_end_of_wrapping_line()-- display a wrapping lineApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()-- 12345678901234Editor_state.lines = load_array{"madam I'm adam"}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'madam ', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, "I'm ad", 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'am', 'baseline/screen:3')y = y + Editor_state.line_height-- click past the end of itedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of linecheck_eq(Editor_state.cursor1.pos, 15, 'cursor') -- one more than the number of UTF-8 code-pointsendfunction test_click_past_end_of_wrapping_line_containing_non_ascii()-- display a wrapping line containing non-ASCIIApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()-- 12345678901234Editor_state.lines = load_array{'madam I’m adam'} -- notice the non-ASCII apostropheText.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'madam ', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'I’m ad', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'am', 'baseline/screen:3')y = y + Editor_state.line_height-- click past the end of itedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of linecheck_eq(Editor_state.cursor1.pos, 15, 'cursor') -- one more than the number of UTF-8 code-pointsendfunction test_click_past_end_of_word_wrapping_line()-- display a long line wrapping at a word boundary on a screen of more realistic lengthApp.screen.init{width=160, height=80}Editor_state = edit.initialize_test_state()-- 0 1 2-- 123456789012345678901Editor_state.lines = load_array{'the quick brown fox jumped over the lazy dog'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'the quick brown fox ', 'baseline/screen:1')y = y + Editor_state.line_height-- click past the end of the screen lineedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of screen line (one more than final character shown)check_eq(Editor_state.cursor1.pos, 21, 'cursor')endfunction test_select_text()-- display a line of textApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- select a letterApp.fake_key_press('lshift')edit.run_after_keychord(Editor_state, 'S-right', 'right')App.fake_key_release('lshift')edit.key_release(Editor_state, 'lshift')-- selection persists even after shift is releasedcheck_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 1, 'selection:pos')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')endfunction test_cursor_movement_without_shift_resets_selection()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- press an arrow key without shiftedit.run_after_keychord(Editor_state, 'right', 'right')-- no change to data, selection is resetcheck_nil(Editor_state.selection1.line, 'check')check_eq(Editor_state.lines[1].data, 'abc', 'data')endfunction test_edit_deletes_selection()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- press a keyedit.run_after_text_input(Editor_state, 'x')-- selected text is deleted and replaced with the keycheck_eq(Editor_state.lines[1].data, 'xbc', 'check')endfunction test_edit_with_shift_key_deletes_selection()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- mimic precise keypresses for a capital letterApp.fake_key_press('lshift')edit.keychord_press(Editor_state, 'd', 'd')edit.text_input(Editor_state, 'D')edit.key_release(Editor_state, 'd')App.fake_key_release('lshift')-- selected text is deleted and replaced with the keycheck_nil(Editor_state.selection1.line, 'check')check_eq(Editor_state.lines[1].data, 'Dbc', 'data')endfunction test_copy_does_not_reset_selection()-- display a line of text with a selectionApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- copy selectionedit.run_after_keychord(Editor_state, 'C-c', 'c')check_eq(App.clipboard, 'a', 'clipboard')-- selection is reset since shift key is not pressedcheck(Editor_state.selection1.line, 'check')endfunction test_cut()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- press a keyedit.run_after_keychord(Editor_state, 'C-x', 'x')check_eq(App.clipboard, 'a', 'clipboard')-- selected text is deletedcheck_eq(Editor_state.lines[1].data, 'bc', 'data')endfunction test_paste_replaces_selection()-- display a line of text with a selectionApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.selection1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- set clipboardApp.clipboard = 'xyz'-- paste selectionedit.run_after_keychord(Editor_state, 'C-v', 'v')-- selection is reset since shift key is not pressed-- selection includes the newline, so it's also deletedcheck_eq(Editor_state.lines[1].data, 'xyzdef', 'check')endfunction test_deleting_selection_may_scroll()-- display lines 2/3/4App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=2}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:3')-- set up a selection starting above the currently displayed pageEditor_state.selection1 = {line=1, pos=2}-- delete selectionedit.run_after_keychord(Editor_state, 'backspace', 'backspace')-- page scrolls upcheck_eq(Editor_state.screen_top1.line, 1, 'check')check_eq(Editor_state.lines[1].data, 'ahi', 'data')endfunction test_edit_wrapping_text()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=4}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)edit.run_after_text_input(Editor_state, 'g')local y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'de', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'fg', 'screen:3')endfunction test_insert_newline()-- display a few linesApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- hitting the enter key splits the lineedit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'a', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'bc', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:3')endfunction test_insert_newline_at_start_of_line()-- display a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}-- hitting the enter key splits the lineedit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')check_eq(Editor_state.lines[1].data, '', 'data:1')check_eq(Editor_state.lines[2].data, 'abc', 'data:2')endfunction test_insert_from_clipboard()-- display a few linesApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- paste some text including a newline, check that new line is createdApp.clipboard = 'xy\nz'edit.run_after_keychord(Editor_state, 'C-v', 'v')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'axy', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'zbc', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:3')endfunction test_select_text_using_mouse()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each line-- press and hold on first locationedit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- drag and release somewhere elseedit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 2, 'selection:pos')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')endfunction test_select_text_using_mouse_starting_above_text()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each line-- press mouse above first line of textedit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 1, 'selection:pos')endfunction test_select_text_using_mouse_starting_above_text_wrapping_line()-- first screen line starts in the middle of a lineApp.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'defgh', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=5}Editor_state.screen_top1 = {line=2, pos=3}-- press mouse above first line of textedit.draw(Editor_state)edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)-- selection is at screen topcheck(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')check_eq(Editor_state.selection1.line, 2, 'selection:line')check_eq(Editor_state.selection1.pos, 3, 'selection:pos')endfunction test_select_text_using_mouse_starting_below_text()-- I'd like to test what happens when a mouse click is below some page of-- text, potentially even in the middle of a line.-- However, it's brittle to set up a text line boundary just right.-- So I'm going to just check things below the bottom of the final line of-- text when it's in the middle of the screen.-- final screen line ends in the middle of screenApp.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abcde'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'ab', 'baseline:screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'cde', 'baseline:screen:2')-- press mouse above first line of textedit.run_after_mouse_press(Editor_state, 5,App.screen.height-5, 1)-- selection is past bottom-most text in screencheck(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 6, 'selection:pos')endfunction test_select_text_using_mouse_and_shift()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each line-- click on first locationedit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- hold down shift and click somewhere elseApp.fake_key_press('lshift')edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)App.fake_key_release('lshift')check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 2, 'selection:pos')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')endfunction test_select_text_repeatedly_using_mouse_and_shift()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each line-- click on first locationedit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- hold down shift and click on a second locationApp.fake_key_press('lshift')edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)-- hold down shift and click at a third locationApp.fake_key_press('lshift')edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+Editor_state.line_height+5, 1)App.fake_key_release('lshift')-- selection is between first and third location. forget the second location, not the first.check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 2, 'selection:pos')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')endfunction test_select_all_text()-- display a single line of textApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- select allApp.fake_key_press('lctrl')edit.run_after_keychord(Editor_state, 'C-a', 'a')App.fake_key_release('lctrl')edit.key_release(Editor_state, 'lctrl')-- selectioncheck_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 1, 'selection:pos')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 8, 'cursor:pos')endfunction test_cut_without_selection()-- display a few linesApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state)-- try to cut without selecting textedit.run_after_keychord(Editor_state, 'C-x', 'x')-- no crashcheck_nil(Editor_state.selection1.line, 'check')endfunction test_pagedown()App.screen.init{width=120, height=45}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}-- initially the first two lines are displayededit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')-- after pagedown the bottom line becomes the topedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:2')endfunction test_pagedown_skips_drawings()-- some lines of text with a drawing intermixedlocal drawing_width = 50App.screen.init{width=Editor_state.left+drawing_width, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', -- height 15'```lines', '```', -- height 25'def', -- height 15'ghi'} -- height 15Text.redraw_all(Editor_state)check_eq(Editor_state.lines[2].mode, 'drawing', 'baseline/lines')Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}local drawing_height = Drawing_padding_height + drawing_width/2 -- default-- initially the screen displays the first line and the drawing-- 15px margin + 15px line1 + 10px margin + 25px drawing + 10px margin = 75px < screen height 80pxedit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')-- after pagedown the screen draws the drawing up top-- 15px margin + 10px margin + 25px drawing + 10px margin + 15px line3 = 75px < screen height 80pxedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor')y = Editor_state.top + drawing_heightApp.screen.check(y, 'def', 'screen:1')endfunction test_pagedown_can_start_from_middle_of_long_wrapping_line()-- draw a few lines starting from a very long wrapping lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu vwx yza bcd efg hij', 'XYZ'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc ', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def ', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'baseline/screen:3')-- after pagedown we scroll down the very long wrapping lineedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 9, 'screen_top:pos')y = Editor_state.topApp.screen.check(y, 'ghi ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno ', 'screen:3')endfunction test_pagedown_never_moves_up()-- draw the final screen line of a wrapping lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=9}Editor_state.screen_top1 = {line=1, pos=9}edit.draw(Editor_state)-- pagedown makes no changeedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 9, 'screen_top:pos')endfunction test_down_arrow_moves_cursor()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}-- initially the first three lines are displayededit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the down arrow, the cursor moves down by 1 lineedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor')-- the screen is unchangedy = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_down_arrow_skips_drawing()-- some lines of text with a drawing intermixedlocal drawing_width = 50App.screen.init{width=Editor_state.left+drawing_width, height=100}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', -- height 15'```lines', '```', -- height 25'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightlocal drawing_height = Drawing_padding_height + drawing_width/2 -- defaulty = y + drawing_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')check(Editor_state.cursor_x, 'baseline/cursor_x')-- after hitting the down arrow the cursor moves down by 2 lines, skipping the drawingedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.cursor1.line, 3, 'cursor')endfunction test_down_arrow_scrolls_down_by_one_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the down arrow the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 4, 'cursor')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_down_arrow_scrolls_down_by_one_screen_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the down arrow the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghij', 'baseline/screen:3')-- after hitting the down arrow the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghij', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:3')endfunction test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up()App.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghij', 'baseline/screen:3')-- after hitting pagedown the screen scrolls down to start of a long lineedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 3, 'baseline2/screen_top')check_eq(Editor_state.cursor1.line, 3, 'baseline2/cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'baseline2/cursor:pos')-- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll upedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 3, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'ghij', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')endfunction test_up_arrow_moves_cursor()-- display the first 3 lines with the cursor on the bottom lineApp.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the up arrow the cursor moves up by 1 lineedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor')-- the screen is unchangedy = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_up_arrow_skips_drawing()-- some lines of text with a drawing intermixedlocal drawing_width = 50App.screen.init{width=Editor_state.left+drawing_width, height=100}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', -- height 15'```lines', '```', -- height 25'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightlocal drawing_height = Drawing_padding_height + drawing_width/2 -- defaulty = y + drawing_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')check(Editor_state.cursor_x, 'baseline/cursor_x')-- after hitting the up arrow the cursor moves up by 2 lines, skipping the drawingedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.cursor1.line, 1, 'cursor')endfunction test_up_arrow_scrolls_up_by_one_line()-- display the lines 2/3/4 with the cursor on line 2App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:3')-- after hitting the up arrow the screen scrolls up by one lineedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_up_arrow_scrolls_up_by_one_line_skipping_drawing()-- display lines 3/4/5 with a drawing just off screen at line 2App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', '```lines', '```', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=3, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:3')-- after hitting the up arrow the screen scrolls up to previous text lineedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')endfunction test_up_arrow_scrolls_up_by_one_screen_line()-- display lines starting from second screen line of a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=6}Editor_state.screen_top1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:2')-- after hitting the up arrow the screen scrolls up to first screen lineedit.run_after_keychord(Editor_state, 'up', 'up')y = Editor_state.topApp.screen.check(y, 'ghi ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')endfunction test_up_arrow_scrolls_up_to_final_screen_line()-- display lines starting just after a long lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'ghi', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:3')-- after hitting the up arrow the screen scrolls up to final screen line of previous lineedit.run_after_keychord(Editor_state, 'up', 'up')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 5, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')endfunction test_up_arrow_scrolls_up_to_empty_line()-- display a screenful of text with an empty line just above it outside the screenApp.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'', 'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the up arrow the screen scrolls up by one lineedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')y = Editor_state.top-- empty first liney = y + Editor_state.line_heightApp.screen.check(y, 'abc', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:3')endfunction test_pageup()App.screen.init{width=120, height=45}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}-- initially the last two lines are displayededit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')-- after pageup the cursor goes to first lineedit.run_after_keychord(Editor_state, 'pageup', 'pageup')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')endfunction test_pageup_scrolls_up_by_screen_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'ghi', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the page-up key the screen scrolls up to topedit.run_after_keychord(Editor_state, 'pageup', 'pageup')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'abc ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_pageup_scrolls_up_from_middle_screen_line()-- display a few lines starting from the middle of a line (Editor_state.cursor1.pos > 1)App.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=5}Editor_state.screen_top1 = {line=2, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the page-up key the screen scrolls up to topedit.run_after_keychord(Editor_state, 'pageup', 'pageup')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'abc ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'screen:3')endfunction test_enter_on_bottom_line_scrolls_down()-- display a few lines with cursor on bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the enter key the screen scrolls downedit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 4, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'g', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'hi', 'screen:3')endfunction test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()-- display just the bottom line on screenApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=4, pos=2}Editor_state.screen_top1 = {line=4, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')-- after hitting the enter key the screen does not scroll downedit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.screen_top1.line, 4, 'screen_top')check_eq(Editor_state.cursor1.line, 5, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'j', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:2')endfunction test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom()-- display just an empty bottom line on screenApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', ''}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)-- after hitting the inserting_text key the screen does not scroll downedit.run_after_text_input(Editor_state, 'a')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')local y = Editor_state.topApp.screen.check(y, 'a', 'screen:1')endfunction test_typing_on_bottom_line_scrolls_down()-- display a few lines with cursor on bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=4}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after typing something the line wraps and the screen scrolls downedit.run_after_text_input(Editor_state, 'j')edit.run_after_text_input(Editor_state, 'k')edit.run_after_text_input(Editor_state, 'l')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 7, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghij', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:3')endfunction test_left_arrow_scrolls_up_in_wrapped_line()-- display lines starting from second screen line of a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.screen_top1 = {line=3, pos=5}-- cursor is at top of screenEditor_state.cursor1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:2')-- after hitting the left arrow the screen scrolls up to first screen lineedit.run_after_keychord(Editor_state, 'left', 'left')y = Editor_state.topApp.screen.check(y, 'ghi ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')endfunction test_right_arrow_scrolls_down_in_wrapped_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.screen_top1 = {line=1, pos=1}-- cursor is at bottom right of screenEditor_state.cursor1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the right arrow the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'right', 'right')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 6, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_home_scrolls_up_in_wrapped_line()-- display lines starting from second screen line of a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.screen_top1 = {line=3, pos=5}-- cursor is at top of screenEditor_state.cursor1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:2')-- after hitting home the screen scrolls up to first screen lineedit.run_after_keychord(Editor_state, 'home', 'home')y = Editor_state.topApp.screen.check(y, 'ghi ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')endfunction test_end_scrolls_down_in_wrapped_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.screen_top1 = {line=1, pos=1}-- cursor is at bottom right of screenEditor_state.cursor1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting end the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'end', 'end')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 8, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_position_cursor_on_recently_edited_wrapping_line()-- draw a line wrapping over 2 screen linesApp.screen.init{width=100, height=200}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi jkl mno pqr ', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=25}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc def ghi ', 'baseline1/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl mno pqr ', 'baseline1/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'baseline1/screen:3')-- add to the line until it's wrapping over 3 screen linesedit.run_after_text_input(Editor_state, 's')edit.run_after_text_input(Editor_state, 't')edit.run_after_text_input(Editor_state, 'u')check_eq(Editor_state.cursor1.pos, 28, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'abc def ghi ', 'baseline2/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl mno pqr ', 'baseline2/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'stu', 'baseline2/screen:3')-- try to move the cursor earlier in the third screen line by clicking the mouseedit.run_after_mouse_release(Editor_state, Editor_state.left+2,Editor_state.top+Editor_state.line_height*2+5, 1)-- cursor should movecheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 25, 'cursor:pos')endfunction test_backspace_can_scroll_up()-- display the lines 2/3/4 with the cursor on line 2App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:3')-- after hitting backspace the screen scrolls up by one lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')y = Editor_state.topApp.screen.check(y, 'abcdef', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_backspace_can_scroll_up_screen_line()-- display lines starting from second screen line of a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=5}Editor_state.screen_top1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:2')-- after hitting backspace the screen scrolls up by one screen lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')y = Editor_state.topApp.screen.check(y, 'ghij', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')endfunction test_backspace_past_line_boundary()-- position cursor at start of a (non-first) lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}-- backspace joins with previous lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'abcdef', 'check')end-- some tests for operating over selections created using Shift- chords-- we're just testing delete_selection, and it works the same for all keysfunction test_backspace_over_selection()-- select just one character within a line with cursor before selectionApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}-- backspace deletes the selected character, even though it's after the cursoredit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'bc', 'data')-- cursor (remains) at start of selectioncheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_backspace_over_selection_reverse()-- select just one character within a line with cursor after selectionApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.selection1 = {line=1, pos=1}-- backspace deletes the selected characteredit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'bc', 'data')-- cursor moves to start of selectioncheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_backspace_over_multiple_lines()-- select just one character within a line with cursor after selectionApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.selection1 = {line=4, pos=2}-- backspace deletes the region and joins the remaining portions of lines on either sideedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'akl', 'data:1')check_eq(Editor_state.lines[2].data, 'mno', 'data:2')-- cursor remains at start of selectioncheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_backspace_to_end_of_line()-- select region from cursor to end of lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.selection1 = {line=1, pos=4}-- backspace deletes rest of line without joining to any other lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'a', 'data:1')check_eq(Editor_state.lines[2].data, 'def', 'data:2')-- cursor remains at start of selectioncheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_backspace_to_start_of_line()-- select region from cursor to start of lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.selection1 = {line=2, pos=3}-- backspace deletes beginning of line without joining to any other lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'abc', 'data:1')check_eq(Editor_state.lines[2].data, 'f', 'data:2')-- cursor remains at start of selectioncheck_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_undo_insert_text()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=4}Editor_state.screen_top1 = {line=1, pos=1}-- insert a characteredit.draw(Editor_state)edit.run_after_text_input(Editor_state, 'g')check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'baseline/cursor:pos')check_nil(Editor_state.selection1.line, 'baseline/selection:line')check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'defg', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'baseline/screen:3')-- undoedit.run_after_keychord(Editor_state, 'C-z', 'z')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection:line')check_nil(Editor_state.selection1.pos, 'selection:pos')y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'screen:3')endfunction test_undo_delete_text()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'defg', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=5}Editor_state.screen_top1 = {line=1, pos=1}-- delete a characteredit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'baseline/cursor:pos')check_nil(Editor_state.selection1.line, 'baseline/selection:line')check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'baseline/screen:3')-- undo--? -- after undo, the backspaced key is selectededit.run_after_keychord(Editor_state, 'C-z', 'z')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection:line')check_nil(Editor_state.selection1.pos, 'selection:pos')--? check_eq(Editor_state.selection1.line, 2, 'selection:line')--? check_eq(Editor_state.selection1.pos, 4, 'selection:pos')y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'defg', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'screen:3')endfunction test_undo_restores_selection()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- delete selected textedit.run_after_text_input(Editor_state, 'x')check_eq(Editor_state.lines[1].data, 'xbc', 'baseline')check_nil(Editor_state.selection1.line, 'baseline:selection')-- undoedit.run_after_keychord(Editor_state, 'C-z', 'z')edit.run_after_keychord(Editor_state, 'C-z', 'z')-- selection is restoredcheck_eq(Editor_state.selection1.line, 1, 'line')check_eq(Editor_state.selection1.pos, 2, 'pos')endfunction test_search()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'```lines', '```', 'def', 'ghi', '’deg'} -- contains unicode quote in final lineText.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for a stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'd')edit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.cursor1.line, 2, '1/cursor:line')check_eq(Editor_state.cursor1.pos, 1, '1/cursor:pos')-- reset cursorEditor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}-- search for second occurrenceedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'de')edit.run_after_keychord(Editor_state, 'down', 'down')edit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.cursor1.line, 4, '2/cursor:line')check_eq(Editor_state.cursor1.pos, 2, '2/cursor:pos')endfunction test_search_upwards()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'’abc', 'abd'} -- contains unicode quoteText.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for a stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'a')-- search for previous occurrenceedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.cursor1.line, 1, '2/cursor:line')check_eq(Editor_state.cursor1.pos, 2, '2/cursor:pos')endfunction test_search_wrap()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'’abc', 'def'} -- contains unicode quote in first lineText.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for a stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'a')edit.run_after_keychord(Editor_state, 'return', 'return')-- cursor wrapscheck_eq(Editor_state.cursor1.line, 1, '1/cursor:line')check_eq(Editor_state.cursor1.pos, 2, '1/cursor:pos')endfunction test_search_wrap_upwards()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc ’abd'} -- contains unicode quoteText.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search upwards for a stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'a')edit.run_after_keychord(Editor_state, 'up', 'up')-- cursor wrapscheck_eq(Editor_state.cursor1.line, 1, '1/cursor:line')check_eq(Editor_state.cursor1.pos, 6, '1/cursor:pos')endfunction test_search_downwards_from_end_of_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for empty stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_keychord(Editor_state, 'down', 'down')-- no crashendfunction test_search_downwards_from_final_pos_of_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=3}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for empty stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_keychord(Editor_state, 'down', 'down')-- no crashend - file addition: text_tests_love11.lua[103.2]
function test_initial_state()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{}Text.redraw_all(Editor_state)edit.draw(Editor_state)check_eq(#Editor_state.lines, 1, '#lines')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')endfunction test_click_to_create_drawing()App.screen.init{width=800, height=600}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{}Text.redraw_all(Editor_state)edit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, 8,Editor_state.top+8, 1)-- cursor skips drawing to always remain on textcheck_eq(#Editor_state.lines, 2, '#lines')check_eq(Editor_state.cursor1.line, 2, 'cursor')endfunction test_backspace_to_delete_drawing()-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'```lines', '```', ''}Text.redraw_all(Editor_state)-- cursor is on text as always (outside tests this will get initialized correctly)Editor_state.cursor1.line = 2-- backspacing deletes the drawingedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(#Editor_state.lines, 1, '#lines')check_eq(Editor_state.cursor1.line, 1, 'cursor')endfunction test_backspace_from_start_of_final_line()-- display final line of text with cursor at start of itApp.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Editor_state.screen_top1 = {line=2, pos=1}Editor_state.cursor1 = {line=2, pos=1}Text.redraw_all(Editor_state)-- backspace scrolls upedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(#Editor_state.lines, 1, '#lines')check_eq(Editor_state.cursor1.line, 1, 'cursor')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')endfunction test_insert_first_character()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{}Text.redraw_all(Editor_state)edit.draw(Editor_state)edit.run_after_text_input(Editor_state, 'a')local y = Editor_state.topApp.screen.check(y, 'a', 'screen:1')endfunction test_press_ctrl()-- press ctrl while the cursor is on textApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{''}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.run_after_keychord(Editor_state, 'C-m', 'm')endfunction test_move_left()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'a'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_move_right()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'a'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'right', 'right')check_eq(Editor_state.cursor1.pos, 2, 'check')endfunction test_move_left_to_previous_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'left', 'left')check_eq(Editor_state.cursor1.line, 1, 'line')check_eq(Editor_state.cursor1.pos, 4, 'pos') -- past end of lineendfunction test_move_right_to_next_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4} -- past end of lineedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'right', 'right')check_eq(Editor_state.cursor1.line, 2, 'line')check_eq(Editor_state.cursor1.pos, 1, 'pos')endfunction test_move_to_start_of_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=3}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_move_to_start_of_previous_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4} -- at the space between wordsedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_skip_to_previous_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=5} -- at the start of second wordedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_skip_past_tab_to_previous_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def\tghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=10} -- within third wordedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 9, 'check')endfunction test_skip_multiple_spaces_to_previous_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=6} -- at the start of second wordedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_move_to_start_of_word_on_previous_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.line, 1, 'line')check_eq(Editor_state.cursor1.pos, 5, 'pos')endfunction test_move_past_end_of_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.pos, 4, 'check')endfunction test_skip_to_next_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4} -- at the space between wordsedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.pos, 8, 'check')endfunction test_skip_past_tab_to_next_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc\tdef'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1} -- at the space between wordsedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.pos, 4, 'check')endfunction test_skip_multiple_spaces_to_next_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4} -- at the start of second wordedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.pos, 9, 'check')endfunction test_move_past_end_of_word_on_next_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=8}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.line, 2, 'line')check_eq(Editor_state.cursor1.pos, 4, 'pos')endfunction test_click_moves_cursor()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each lineedit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')-- selection is empty to avoid perturbing future editscheck_nil(Editor_state.selection1.line, 'selection:line')check_nil(Editor_state.selection1.pos, 'selection:pos')endfunction test_click_to_left_of_line()-- display a line with the cursor in the middleApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=3}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}-- click to the left of the lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left-4,Editor_state.top+5, 1)-- cursor moves to start of linecheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_click_takes_margins_into_account()-- display two lines with cursor on one of themApp.screen.init{width=100, height=80}Editor_state = edit.initialize_test_state()Editor_state.left = 50 -- occupy only right side of screenEditor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}-- click on the other lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- cursor movescheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_click_on_empty_line()-- display two lines with the first one emptyApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}-- click on the empty lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- cursor movescheck_eq(Editor_state.cursor1.line, 1, 'cursor')-- selection remains emptycheck_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_click_below_final_line_of_file()-- display one lineApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}-- click below first lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+50, 1)-- cursor goes to bottomcheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')-- selection remains emptycheck_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_draw_text()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_draw_wrapping_text()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'defgh', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'de', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'fgh', 'screen:3')endfunction test_draw_word_wrapping_text()App.screen.init{width=60, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_click_on_wrapping_line()-- display two screen lines with cursor on one of themApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=20}Editor_state.screen_top1 = {line=1, pos=1}-- click on the other lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- cursor movescheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_click_on_wrapping_line_takes_margins_into_account()-- display two screen lines with cursor on one of themApp.screen.init{width=100, height=80}Editor_state = edit.initialize_test_state()Editor_state.left = 50 -- occupy only right side of screenEditor_state.lines = load_array{'abc def ghi jkl mno pqr stu'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=20}Editor_state.screen_top1 = {line=1, pos=1}-- click on the other lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- cursor movescheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_draw_text_wrapping_within_word()-- arrange a screen line that needs to be split within a wordApp.screen.init{width=60, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abcd e fghijk', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abcd ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'e fgh', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ijk', 'screen:3')endfunction test_draw_wrapping_text_containing_non_ascii()-- draw a long line containing non-ASCIIApp.screen.init{width=60, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'madam I’m adam', 'xyz'} -- notice the non-ASCII apostropheText.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'mad', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'am I', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, '’m a', 'screen:3')endfunction test_click_past_end_of_screen_line()-- display a wrapping lineApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()-- 12345678901234Editor_state.lines = load_array{"madam I'm adam"}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'madam ', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, "I'm ad", 'baseline/screen:2')y = y + Editor_state.line_height-- click past end of second screen lineedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of screen line (one more than final character shown)check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 13, 'cursor:pos')endfunction test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen()-- display a wrapping line from its second screen lineApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()-- 12345678901234Editor_state.lines = load_array{"madam I'm adam"}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=8}Editor_state.screen_top1 = {line=1, pos=7}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, "I'm ad", 'baseline/screen:2')y = y + Editor_state.line_height-- click past end of second screen lineedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of screen line (one more than final character shown)check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 13, 'cursor:pos')endfunction test_click_past_end_of_wrapping_line()-- display a wrapping lineApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()-- 12345678901234Editor_state.lines = load_array{"madam I'm adam"}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'madam ', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, "I'm ad", 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'am', 'baseline/screen:3')y = y + Editor_state.line_height-- click past the end of itedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of linecheck_eq(Editor_state.cursor1.pos, 15, 'cursor') -- one more than the number of UTF-8 code-pointsendfunction test_click_past_end_of_wrapping_line_containing_non_ascii()-- display a wrapping line containing non-ASCIIApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()-- 12345678901234Editor_state.lines = load_array{'madam I’m adam'} -- notice the non-ASCII apostropheText.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'madam ', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'I’m ad', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'am', 'baseline/screen:3')y = y + Editor_state.line_height-- click past the end of itedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of linecheck_eq(Editor_state.cursor1.pos, 15, 'cursor') -- one more than the number of UTF-8 code-pointsendfunction test_click_past_end_of_word_wrapping_line()-- display a long line wrapping at a word boundary on a screen of more realistic lengthApp.screen.init{width=160, height=80}Editor_state = edit.initialize_test_state()-- 0 1 2-- 123456789012345678901Editor_state.lines = load_array{'the quick brown fox jumped over the lazy dog'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'the quick brown fox ', 'baseline/screen:1')y = y + Editor_state.line_height-- click past the end of the screen lineedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of screen line (one more than final character shown)check_eq(Editor_state.cursor1.pos, 21, 'cursor')endfunction test_select_text()-- display a line of textApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- select a letterApp.fake_key_press('lshift')edit.run_after_keychord(Editor_state, 'S-right', 'right')App.fake_key_release('lshift')edit.key_release(Editor_state, 'lshift')-- selection persists even after shift is releasedcheck_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 1, 'selection:pos')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')endfunction test_cursor_movement_without_shift_resets_selection()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- press an arrow key without shiftedit.run_after_keychord(Editor_state, 'right', 'right')-- no change to data, selection is resetcheck_nil(Editor_state.selection1.line, 'check')check_eq(Editor_state.lines[1].data, 'abc', 'data')endfunction test_edit_deletes_selection()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- press a keyedit.run_after_text_input(Editor_state, 'x')-- selected text is deleted and replaced with the keycheck_eq(Editor_state.lines[1].data, 'xbc', 'check')endfunction test_edit_with_shift_key_deletes_selection()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- mimic precise keypresses for a capital letterApp.fake_key_press('lshift')edit.keychord_press(Editor_state, 'd', 'd')edit.text_input(Editor_state, 'D')edit.key_release(Editor_state, 'd')App.fake_key_release('lshift')-- selected text is deleted and replaced with the keycheck_nil(Editor_state.selection1.line, 'check')check_eq(Editor_state.lines[1].data, 'Dbc', 'data')endfunction test_copy_does_not_reset_selection()-- display a line of text with a selectionApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- copy selectionedit.run_after_keychord(Editor_state, 'C-c', 'c')check_eq(App.clipboard, 'a', 'clipboard')-- selection is reset since shift key is not pressedcheck(Editor_state.selection1.line, 'check')endfunction test_cut()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- press a keyedit.run_after_keychord(Editor_state, 'C-x', 'x')check_eq(App.clipboard, 'a', 'clipboard')-- selected text is deletedcheck_eq(Editor_state.lines[1].data, 'bc', 'data')endfunction test_paste_replaces_selection()-- display a line of text with a selectionApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.selection1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- set clipboardApp.clipboard = 'xyz'-- paste selectionedit.run_after_keychord(Editor_state, 'C-v', 'v')-- selection is reset since shift key is not pressed-- selection includes the newline, so it's also deletedcheck_eq(Editor_state.lines[1].data, 'xyzdef', 'check')endfunction test_deleting_selection_may_scroll()-- display lines 2/3/4App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=2}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:3')-- set up a selection starting above the currently displayed pageEditor_state.selection1 = {line=1, pos=2}-- delete selectionedit.run_after_keychord(Editor_state, 'backspace', 'backspace')-- page scrolls upcheck_eq(Editor_state.screen_top1.line, 1, 'check')check_eq(Editor_state.lines[1].data, 'ahi', 'data')endfunction test_edit_wrapping_text()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=4}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)edit.run_after_text_input(Editor_state, 'g')local y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'de', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'fg', 'screen:3')endfunction test_insert_newline()-- display a few linesApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- hitting the enter key splits the lineedit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'a', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'bc', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:3')endfunction test_insert_newline_at_start_of_line()-- display a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}-- hitting the enter key splits the lineedit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')check_eq(Editor_state.lines[1].data, '', 'data:1')check_eq(Editor_state.lines[2].data, 'abc', 'data:2')endfunction test_insert_from_clipboard()-- display a few linesApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- paste some text including a newline, check that new line is createdApp.clipboard = 'xy\nz'edit.run_after_keychord(Editor_state, 'C-v', 'v')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'axy', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'zbc', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:3')endfunction test_select_text_using_mouse()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each line-- press and hold on first locationedit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- drag and release somewhere elseedit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 2, 'selection:pos')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')endfunction test_select_text_using_mouse_starting_above_text()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each line-- press mouse above first line of textedit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 1, 'selection:pos')endfunction test_select_text_using_mouse_starting_above_text_wrapping_line()-- first screen line starts in the middle of a lineApp.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'defgh', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=5}Editor_state.screen_top1 = {line=2, pos=3}-- press mouse above first line of textedit.draw(Editor_state)edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)-- selection is at screen topcheck(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')check_eq(Editor_state.selection1.line, 2, 'selection:line')check_eq(Editor_state.selection1.pos, 3, 'selection:pos')endfunction test_select_text_using_mouse_starting_below_text()-- I'd like to test what happens when a mouse click is below some page of-- text, potentially even in the middle of a line.-- However, it's brittle to set up a text line boundary just right.-- So I'm going to just check things below the bottom of the final line of-- text when it's in the middle of the screen.-- final screen line ends in the middle of screenApp.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abcde'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'ab', 'baseline:screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'cde', 'baseline:screen:2')-- press mouse above first line of textedit.run_after_mouse_press(Editor_state, 5,App.screen.height-5, 1)-- selection is past bottom-most text in screencheck(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 6, 'selection:pos')endfunction test_select_text_using_mouse_and_shift()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each line-- click on first locationedit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- hold down shift and click somewhere elseApp.fake_key_press('lshift')edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)App.fake_key_release('lshift')check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 2, 'selection:pos')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')endfunction test_select_text_repeatedly_using_mouse_and_shift()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each line-- click on first locationedit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- hold down shift and click on a second locationApp.fake_key_press('lshift')edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)-- hold down shift and click at a third locationApp.fake_key_press('lshift')edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+Editor_state.line_height+5, 1)App.fake_key_release('lshift')-- selection is between first and third location. forget the second location, not the first.check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 2, 'selection:pos')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')endfunction test_select_all_text()-- display a single line of textApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- select allApp.fake_key_press('lctrl')edit.run_after_keychord(Editor_state, 'C-a', 'a')App.fake_key_release('lctrl')edit.key_release(Editor_state, 'lctrl')-- selectioncheck_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 1, 'selection:pos')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 8, 'cursor:pos')endfunction test_cut_without_selection()-- display a few linesApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state)-- try to cut without selecting textedit.run_after_keychord(Editor_state, 'C-x', 'x')-- no crashcheck_nil(Editor_state.selection1.line, 'check')endfunction test_pagedown()App.screen.init{width=120, height=45}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}-- initially the first two lines are displayededit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')-- after pagedown the bottom line becomes the topedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:2')endfunction test_pagedown_skips_drawings()-- some lines of text with a drawing intermixedlocal drawing_width = 50App.screen.init{width=Editor_state.left+drawing_width, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', -- height 15'```lines', '```', -- height 25'def', -- height 15'ghi'} -- height 15Text.redraw_all(Editor_state)check_eq(Editor_state.lines[2].mode, 'drawing', 'baseline/lines')Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}local drawing_height = Drawing_padding_height + drawing_width/2 -- default-- initially the screen displays the first line and the drawing-- 15px margin + 15px line1 + 10px margin + 25px drawing + 10px margin = 75px < screen height 80pxedit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')-- after pagedown the screen draws the drawing up top-- 15px margin + 10px margin + 25px drawing + 10px margin + 15px line3 = 75px < screen height 80pxedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor')y = Editor_state.top + drawing_heightApp.screen.check(y, 'def', 'screen:1')endfunction test_pagedown_can_start_from_middle_of_long_wrapping_line()-- draw a few lines starting from a very long wrapping lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu vwx yza bcd efg hij', 'XYZ'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc ', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def ', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'baseline/screen:3')-- after pagedown we scroll down the very long wrapping lineedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 9, 'screen_top:pos')y = Editor_state.topApp.screen.check(y, 'ghi ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mn', 'screen:3')endfunction test_pagedown_never_moves_up()-- draw the final screen line of a wrapping lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=9}Editor_state.screen_top1 = {line=1, pos=9}edit.draw(Editor_state)-- pagedown makes no changeedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 9, 'screen_top:pos')endfunction test_down_arrow_moves_cursor()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}-- initially the first three lines are displayededit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the down arrow, the cursor moves down by 1 lineedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor')-- the screen is unchangedy = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_down_arrow_skips_drawing()-- some lines of text with a drawing intermixedlocal drawing_width = 50App.screen.init{width=Editor_state.left+drawing_width, height=100}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', -- height 15'```lines', '```', -- height 25'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightlocal drawing_height = Drawing_padding_height + drawing_width/2 -- defaulty = y + drawing_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')check(Editor_state.cursor_x, 'baseline/cursor_x')-- after hitting the down arrow the cursor moves down by 2 lines, skipping the drawingedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.cursor1.line, 3, 'cursor')endfunction test_down_arrow_scrolls_down_by_one_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the down arrow the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 4, 'cursor')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_down_arrow_scrolls_down_by_one_screen_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the down arrow the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghij', 'baseline/screen:3')-- after hitting the down arrow the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghij', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:3')endfunction test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up()App.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghij', 'baseline/screen:3')-- after hitting pagedown the screen scrolls down to start of a long lineedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 3, 'baseline2/screen_top')check_eq(Editor_state.cursor1.line, 3, 'baseline2/cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'baseline2/cursor:pos')-- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll upedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 3, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'ghij', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')endfunction test_up_arrow_moves_cursor()-- display the first 3 lines with the cursor on the bottom lineApp.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the up arrow the cursor moves up by 1 lineedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor')-- the screen is unchangedy = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_up_arrow_skips_drawing()-- some lines of text with a drawing intermixedlocal drawing_width = 50App.screen.init{width=Editor_state.left+drawing_width, height=100}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', -- height 15'```lines', '```', -- height 25'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightlocal drawing_height = Drawing_padding_height + drawing_width/2 -- defaulty = y + drawing_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')check(Editor_state.cursor_x, 'baseline/cursor_x')-- after hitting the up arrow the cursor moves up by 2 lines, skipping the drawingedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.cursor1.line, 1, 'cursor')endfunction test_up_arrow_scrolls_up_by_one_line()-- display the lines 2/3/4 with the cursor on line 2App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:3')-- after hitting the up arrow the screen scrolls up by one lineedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_up_arrow_scrolls_up_by_one_line_skipping_drawing()-- display lines 3/4/5 with a drawing just off screen at line 2App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', '```lines', '```', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=3, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:3')-- after hitting the up arrow the screen scrolls up to previous text lineedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')endfunction test_up_arrow_scrolls_up_by_one_screen_line()-- display lines starting from second screen line of a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=6}Editor_state.screen_top1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:2')-- after hitting the up arrow the screen scrolls up to first screen lineedit.run_after_keychord(Editor_state, 'up', 'up')y = Editor_state.topApp.screen.check(y, 'ghi ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')endfunction test_up_arrow_scrolls_up_to_final_screen_line()-- display lines starting just after a long lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'ghi', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:3')-- after hitting the up arrow the screen scrolls up to final screen line of previous lineedit.run_after_keychord(Editor_state, 'up', 'up')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 5, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')endfunction test_up_arrow_scrolls_up_to_empty_line()-- display a screenful of text with an empty line just above it outside the screenApp.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'', 'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the up arrow the screen scrolls up by one lineedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')y = Editor_state.top-- empty first liney = y + Editor_state.line_heightApp.screen.check(y, 'abc', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:3')endfunction test_pageup()App.screen.init{width=120, height=45}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}-- initially the last two lines are displayededit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')-- after pageup the cursor goes to first lineedit.run_after_keychord(Editor_state, 'pageup', 'pageup')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')endfunction test_pageup_scrolls_up_by_screen_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'ghi', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the page-up key the screen scrolls up to topedit.run_after_keychord(Editor_state, 'pageup', 'pageup')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'abc ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_pageup_scrolls_up_from_middle_screen_line()-- display a few lines starting from the middle of a line (Editor_state.cursor1.pos > 1)App.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=5}Editor_state.screen_top1 = {line=2, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the page-up key the screen scrolls up to topedit.run_after_keychord(Editor_state, 'pageup', 'pageup')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'abc ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'screen:3')endfunction test_enter_on_bottom_line_scrolls_down()-- display a few lines with cursor on bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the enter key the screen scrolls downedit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 4, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'g', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'hi', 'screen:3')endfunction test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()-- display just the bottom line on screenApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=4, pos=2}Editor_state.screen_top1 = {line=4, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')-- after hitting the enter key the screen does not scroll downedit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.screen_top1.line, 4, 'screen_top')check_eq(Editor_state.cursor1.line, 5, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'j', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:2')endfunction test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom()-- display just an empty bottom line on screenApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', ''}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)-- after hitting the inserting_text key the screen does not scroll downedit.run_after_text_input(Editor_state, 'a')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')local y = Editor_state.topApp.screen.check(y, 'a', 'screen:1')endfunction test_typing_on_bottom_line_scrolls_down()-- display a few lines with cursor on bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=4}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after typing something the line wraps and the screen scrolls downedit.run_after_text_input(Editor_state, 'j')edit.run_after_text_input(Editor_state, 'k')edit.run_after_text_input(Editor_state, 'l')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 7, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghij', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:3')endfunction test_left_arrow_scrolls_up_in_wrapped_line()-- display lines starting from second screen line of a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.screen_top1 = {line=3, pos=5}-- cursor is at top of screenEditor_state.cursor1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:2')-- after hitting the left arrow the screen scrolls up to first screen lineedit.run_after_keychord(Editor_state, 'left', 'left')y = Editor_state.topApp.screen.check(y, 'ghi ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')endfunction test_right_arrow_scrolls_down_in_wrapped_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.screen_top1 = {line=1, pos=1}-- cursor is at bottom right of screenEditor_state.cursor1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the right arrow the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'right', 'right')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 6, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_home_scrolls_up_in_wrapped_line()-- display lines starting from second screen line of a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.screen_top1 = {line=3, pos=5}-- cursor is at top of screenEditor_state.cursor1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:2')-- after hitting home the screen scrolls up to first screen lineedit.run_after_keychord(Editor_state, 'home', 'home')y = Editor_state.topApp.screen.check(y, 'ghi ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')endfunction test_end_scrolls_down_in_wrapped_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.screen_top1 = {line=1, pos=1}-- cursor is at bottom right of screenEditor_state.cursor1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting end the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'end', 'end')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 8, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_position_cursor_on_recently_edited_wrapping_line()-- draw a line wrapping over 2 screen linesApp.screen.init{width=100, height=200}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi jkl mno pqr ', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=25}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc def ghi ', 'baseline1/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl mno pqr ', 'baseline1/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'baseline1/screen:3')-- add to the line until it's wrapping over 3 screen linesedit.run_after_text_input(Editor_state, 's')edit.run_after_text_input(Editor_state, 't')edit.run_after_text_input(Editor_state, 'u')check_eq(Editor_state.cursor1.pos, 28, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'abc def ghi ', 'baseline2/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl mno pqr ', 'baseline2/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'stu', 'baseline2/screen:3')-- try to move the cursor earlier in the third screen line by clicking the mouseedit.run_after_mouse_release(Editor_state, Editor_state.left+2,Editor_state.top+Editor_state.line_height*2+5, 1)-- cursor should movecheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 25, 'cursor:pos')endfunction test_backspace_can_scroll_up()-- display the lines 2/3/4 with the cursor on line 2App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:3')-- after hitting backspace the screen scrolls up by one lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')y = Editor_state.topApp.screen.check(y, 'abcdef', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_backspace_can_scroll_up_screen_line()-- display lines starting from second screen line of a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=5}Editor_state.screen_top1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:2')-- after hitting backspace the screen scrolls up by one screen lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')y = Editor_state.topApp.screen.check(y, 'ghij', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')endfunction test_backspace_past_line_boundary()-- position cursor at start of a (non-first) lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}-- backspace joins with previous lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'abcdef', 'check')end-- some tests for operating over selections created using Shift- chords-- we're just testing delete_selection, and it works the same for all keysfunction test_backspace_over_selection()-- select just one character within a line with cursor before selectionApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}-- backspace deletes the selected character, even though it's after the cursoredit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'bc', 'data')-- cursor (remains) at start of selectioncheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_backspace_over_selection_reverse()-- select just one character within a line with cursor after selectionApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.selection1 = {line=1, pos=1}-- backspace deletes the selected characteredit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'bc', 'data')-- cursor moves to start of selectioncheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_backspace_over_multiple_lines()-- select just one character within a line with cursor after selectionApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.selection1 = {line=4, pos=2}-- backspace deletes the region and joins the remaining portions of lines on either sideedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'akl', 'data:1')check_eq(Editor_state.lines[2].data, 'mno', 'data:2')-- cursor remains at start of selectioncheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_backspace_to_end_of_line()-- select region from cursor to end of lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.selection1 = {line=1, pos=4}-- backspace deletes rest of line without joining to any other lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'a', 'data:1')check_eq(Editor_state.lines[2].data, 'def', 'data:2')-- cursor remains at start of selectioncheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_backspace_to_start_of_line()-- select region from cursor to start of lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.selection1 = {line=2, pos=3}-- backspace deletes beginning of line without joining to any other lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'abc', 'data:1')check_eq(Editor_state.lines[2].data, 'f', 'data:2')-- cursor remains at start of selectioncheck_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_undo_insert_text()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=4}Editor_state.screen_top1 = {line=1, pos=1}-- insert a characteredit.draw(Editor_state)edit.run_after_text_input(Editor_state, 'g')check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'baseline/cursor:pos')check_nil(Editor_state.selection1.line, 'baseline/selection:line')check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'defg', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'baseline/screen:3')-- undoedit.run_after_keychord(Editor_state, 'C-z', 'z')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection:line')check_nil(Editor_state.selection1.pos, 'selection:pos')y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'screen:3')endfunction test_undo_delete_text()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'defg', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=5}Editor_state.screen_top1 = {line=1, pos=1}-- delete a characteredit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'baseline/cursor:pos')check_nil(Editor_state.selection1.line, 'baseline/selection:line')check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'baseline/screen:3')-- undo--? -- after undo, the backspaced key is selectededit.run_after_keychord(Editor_state, 'C-z', 'z')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection:line')check_nil(Editor_state.selection1.pos, 'selection:pos')--? check_eq(Editor_state.selection1.line, 2, 'selection:line')--? check_eq(Editor_state.selection1.pos, 4, 'selection:pos')y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'defg', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'screen:3')endfunction test_undo_restores_selection()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- delete selected textedit.run_after_text_input(Editor_state, 'x')check_eq(Editor_state.lines[1].data, 'xbc', 'baseline')check_nil(Editor_state.selection1.line, 'baseline:selection')-- undoedit.run_after_keychord(Editor_state, 'C-z', 'z')edit.run_after_keychord(Editor_state, 'C-z', 'z')-- selection is restoredcheck_eq(Editor_state.selection1.line, 1, 'line')check_eq(Editor_state.selection1.pos, 2, 'pos')endfunction test_search()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'```lines', '```', 'def', 'ghi', '’deg'} -- contains unicode quote in final lineText.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for a stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'd')edit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.cursor1.line, 2, '1/cursor:line')check_eq(Editor_state.cursor1.pos, 1, '1/cursor:pos')-- reset cursorEditor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}-- search for second occurrenceedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'de')edit.run_after_keychord(Editor_state, 'down', 'down')edit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.cursor1.line, 4, '2/cursor:line')check_eq(Editor_state.cursor1.pos, 2, '2/cursor:pos')endfunction test_search_upwards()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'’abc', 'abd'} -- contains unicode quoteText.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for a stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'a')-- search for previous occurrenceedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.cursor1.line, 1, '2/cursor:line')check_eq(Editor_state.cursor1.pos, 2, '2/cursor:pos')endfunction test_search_wrap()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'’abc', 'def'} -- contains unicode quote in first lineText.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for a stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'a')edit.run_after_keychord(Editor_state, 'return', 'return')-- cursor wrapscheck_eq(Editor_state.cursor1.line, 1, '1/cursor:line')check_eq(Editor_state.cursor1.pos, 2, '1/cursor:pos')endfunction test_search_wrap_upwards()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc ’abd'} -- contains unicode quoteText.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search upwards for a stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'a')edit.run_after_keychord(Editor_state, 'up', 'up')-- cursor wrapscheck_eq(Editor_state.cursor1.line, 1, '1/cursor:line')check_eq(Editor_state.cursor1.pos, 6, '1/cursor:pos')endfunction test_search_downwards_from_end_of_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for empty stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_keychord(Editor_state, 'down', 'down')-- no crashendfunction test_search_downwards_from_final_pos_of_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=3}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for empty stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_keychord(Editor_state, 'down', 'down')-- no crashend - replacement in text_tests.lua at line 2[9.41]→[9.2:107](∅→∅),[9.107]→[9.2:3](∅→∅),[9.92]→[9.2:3](∅→∅),[9.3]→[9.2:32](∅→∅),[9.67]→[9.67:107](∅→∅),[9.107]→[9.2:48](∅→∅),[9.48]→[9.1104:1140](∅→∅),[9.106]→[9.1104:1140](∅→∅),[9.107]→[9.1104:1140](∅→∅),[9.1140]→[9.315:347](∅→∅),[9.347]→[9.2:28](∅→∅),[9.1140]→[9.2:28](∅→∅),[9.28]→[9.2:283](∅→∅),[9.283]→[9.474:519](∅→∅),[9.1623]→[9.474:519](∅→∅),[9.474]→[9.474:519](∅→∅),[9.519]→[9.0:41](∅→∅),[9.41]→[9.49:95](∅→∅),[9.604]→[9.49:95](∅→∅),[9.95]→[9.1624:1660](∅→∅),[9.211]→[9.1624:1660](∅→∅),[9.604]→[9.1624:1660](∅→∅),[9.1660]→[9.348:380](∅→∅),[9.380]→[9.29:55](∅→∅),[9.1660]→[9.29:55](∅→∅),[9.55]→[9.212:280](∅→∅),[9.77]→[9.687:738](∅→∅),[9.280]→[9.687:738](∅→∅),[9.1807]→[9.687:738](∅→∅),[9.687]→[9.687:738](∅→∅),[9.738]→[9.284:380](∅→∅),[9.380]→[9.874:923](∅→∅),[9.1970]→[9.874:923](∅→∅),[9.874]→[9.874:923](∅→∅),[9.972]→[9.972:1115](∅→∅),[9.1115]→[9.96:142](∅→∅),[9.142]→[9.1971:2028](∅→∅),[9.385]→[9.1971:2028](∅→∅),[9.1115]→[9.1971:2028](∅→∅),[9.2028]→[9.381:413](∅→∅),[9.145]→[9.1159:1244](∅→∅),[9.413]→[9.1159:1244](∅→∅),[9.2114]→[9.1159:1244](∅→∅),[9.1159]→[9.1159:1244](∅→∅),[9.1244]→[9.2115:2147](∅→∅),[9.2147]→[9.1263:1300](∅→∅),[9.1263]→[9.1263:1300](∅→∅),[9.1300]→[9.2:68](∅→∅),[9.68]→[9.381:477](∅→∅),[9.131]→[9.381:477](∅→∅),[9.477]→[9.2:58](∅→∅),[9.2318]→[9.2:58](∅→∅),[9.114]→[9.114:451](∅→∅),[9.451]→[9.69:135](∅→∅),[9.135]→[9.478:633](∅→∅),[9.504]→[9.478:633](∅→∅),[9.633]→[9.1482:1487](∅→∅),[9.791]→[9.1482:1487](∅→∅),[9.2318]→[9.1482:1487](∅→∅),[9.1482]→[9.1482:1487](∅→∅),[9.1487]→[9.3:42](∅→∅),[9.3]→[9.3:42](∅→∅),[9.86]→[9.86:126](∅→∅),[9.126]→[9.143:189](∅→∅),[9.189]→[9.2319:2355](∅→∅),[9.490]→[9.2319:2355](∅→∅),[9.126]→[9.2319:2355](∅→∅),[9.2355]→[9.414:446](∅→∅),[9.446]→[9.56:82](∅→∅),[9.2355]→[9.56:82](∅→∅),[9.82]→[9.2:49](∅→∅),[9.49]→[9.491:520](∅→∅),[9.178]→[9.491:520](∅→∅),[9.520]→[9.634:673](∅→∅),[9.673]→[9.2:34](∅→∅),[9.287]→[9.2:34](∅→∅),[9.66]→[9.66:149](∅→∅),[9.149]→[9.190:236](∅→∅),[9.236]→[9.2479:2517](∅→∅),[9.625]→[9.2479:2517](∅→∅),[9.149]→[9.2479:2517](∅→∅),[9.2517]→[9.447:479](∅→∅),[9.479]→[9.2603:2689](∅→∅),[9.2603]→[9.2603:2689](∅→∅),[9.2724]→[9.136:188](∅→∅),[9.188]→[9.2:33](∅→∅),[9.226]→[9.2:33](∅→∅),[9.320]→[9.2:33](∅→∅),[9.64]→[9.83:123](∅→∅),[9.123]→[9.237:283](∅→∅),[9.123]→[9.2725:2764](∅→∅),[9.283]→[9.2725:2764](∅→∅),[9.730]→[9.2725:2764](∅→∅),[9.104]→[9.2725:2764](∅→∅),[9.2764]→[9.480:512](∅→∅),[9.512]→[9.2764:2805](∅→∅),[9.2764]→[9.2764:2805](∅→∅),[9.2805]→[9.124:150](∅→∅),[9.150]→[9.189:245](∅→∅),[9.245]→[9.674:723](∅→∅),[9.275]→[9.674:723](∅→∅),[9.723]→[9.300:332](∅→∅),[9.2954]→[9.300:332](∅→∅),[9.300]→[9.300:332](∅→∅),[9.364]→[9.364:404](∅→∅),[9.404]→[9.284:330](∅→∅),[9.330]→[9.2955:2994](∅→∅),[9.835]→[9.2955:2994](∅→∅),[9.404]→[9.2955:2994](∅→∅),[9.2994]→[9.513:545](∅→∅),[9.545]→[9.2994:3035](∅→∅),[9.2994]→[9.2994:3035](∅→∅),[9.3035]→[9.151:177](∅→∅),[9.177]→[9.246:304](∅→∅),[9.304]→[9.724:773](∅→∅),[9.325]→[9.724:773](∅→∅),[9.320]→[9.287:291](∅→∅),[9.602]→[9.287:291](∅→∅),[9.773]→[9.287:291](∅→∅),[9.3185]→[9.287:291](∅→∅),[9.287]→[9.287:291](∅→∅),[9.291]→[9.92:93](∅→∅),[9.92]→[9.92:93](∅→∅),[9.93]→[9.603:646](∅→∅),[9.694]→[9.694:734](∅→∅),[9.734]→[9.331:377](∅→∅),[9.377]→[9.3186:3234](∅→∅),[9.940]→[9.3186:3234](∅→∅),[9.734]→[9.3186:3234](∅→∅),[9.3234]→[9.546:578](∅→∅),[9.578]→[9.3234:3275](∅→∅),[9.3234]→[9.3234:3275](∅→∅),[9.3275]→[9.178:204](∅→∅),[9.204]→[9.305:361](∅→∅),[9.361]→[9.774:891](∅→∅),[9.374]→[9.774:891](∅→∅),[9.891]→[9.1053:1098](∅→∅),[9.3551]→[9.1053:1098](∅→∅),[9.1053]→[9.1053:1098](∅→∅),[9.1143]→[9.1143:1183](∅→∅),[9.1183]→[9.378:424](∅→∅),[9.424]→[9.3552:3600](∅→∅),[9.1045]→[9.3552:3600](∅→∅),[9.1183]→[9.3552:3600](∅→∅),[9.3600]→[9.579:611](∅→∅),[9.611]→[9.3600:3662](∅→∅),[9.3600]→[9.3600:3662](∅→∅),[9.3662]→[9.205:231](∅→∅),[9.231]→[9.362:420](∅→∅),[9.420]→[9.892:988](∅→∅),[9.424]→[9.892:988](∅→∅),[9.988]→[9.1497:1540](∅→∅),[9.3911]→[9.1497:1540](∅→∅),[9.1497]→[9.1497:1540](∅→∅),[9.1583]→[9.1583:1623](∅→∅),[9.1623]→[9.425:471](∅→∅),[9.471]→[9.3912:3953](∅→∅),[9.1150]→[9.3912:3953](∅→∅),[9.1623]→[9.3912:3953](∅→∅),[9.3953]→[9.612:644](∅→∅),[9.644]→[9.3953:3994](∅→∅),[9.3953]→[9.3953:3994](∅→∅),[9.3994]→[9.232:258](∅→∅),[9.258]→[9.421:479](∅→∅),[9.479]→[9.989:1038](∅→∅),[9.475]→[9.989:1038](∅→∅),[9.1038]→[9.1835:1887](∅→∅),[9.4155]→[9.1835:1887](∅→∅),[9.1835]→[9.1835:1887](∅→∅),[9.1939]→[9.1939:1979](∅→∅),[9.1979]→[9.472:518](∅→∅),[9.518]→[9.4156:4201](∅→∅),[9.1255]→[9.4156:4201](∅→∅),[9.1979]→[9.4156:4201](∅→∅),[9.4201]→[9.645:677](∅→∅),[9.677]→[9.4201:4273](∅→∅),[9.4201]→[9.4201:4273](∅→∅),[9.4273]→[9.259:285](∅→∅),[9.285]→[9.480:538](∅→∅),[9.538]→[9.1039:1088](∅→∅),[9.526]→[9.1039:1088](∅→∅),[9.1088]→[9.2235:2278](∅→∅),[9.4443]→[9.2235:2278](∅→∅),[9.2235]→[9.2235:2278](∅→∅),[9.2321]→[9.2321:2361](∅→∅),[9.2361]→[9.519:565](∅→∅),[9.565]→[9.4444:4489](∅→∅),[9.1360]→[9.4444:4489](∅→∅),[9.2361]→[9.4444:4489](∅→∅),[9.4489]→[9.678:710](∅→∅),[9.710]→[9.4489:4562](∅→∅),[9.4489]→[9.4489:4562](∅→∅),[9.4562]→[9.286:312](∅→∅),[9.312]→[9.539:597](∅→∅),[9.597]→[9.1089:1138](∅→∅),[9.577]→[9.1089:1138](∅→∅),[9.1138]→[9.2:54](∅→∅),[9.4723]→[9.2:54](∅→∅),[9.2609]→[9.2:54](∅→∅),[9.106]→[9.106:146](∅→∅),[9.146]→[9.566:612](∅→∅),[9.612]→[9.4724:4774](∅→∅),[9.1465]→[9.4724:4774](∅→∅),[9.146]→[9.4724:4774](∅→∅),[9.4774]→[9.711:743](∅→∅),[9.743]→[9.4774:4838](∅→∅),[9.4774]→[9.4774:4838](∅→∅),[9.4838]→[9.313:339](∅→∅),[9.339]→[9.598:656](∅→∅),[9.656]→[9.1139:1188](∅→∅),[9.628]→[9.1139:1188](∅→∅),[9.1188]→[9.2:61](∅→∅),[9.5008]→[9.2:61](∅→∅),[9.399]→[9.2:61](∅→∅),[9.120]→[9.120:160](∅→∅),[9.160]→[9.613:659](∅→∅),[9.659]→[9.5009:5055](∅→∅),[9.1570]→[9.5009:5055](∅→∅),[9.160]→[9.5009:5055](∅→∅),[9.5055]→[9.744:776](∅→∅),[9.776]→[9.5055:5128](∅→∅),[9.5055]→[9.5055:5128](∅→∅),[9.5128]→[9.340:366](∅→∅),[9.366]→[9.657:715](∅→∅),[9.715]→[9.1189:1238](∅→∅),[9.679]→[9.1189:1238](∅→∅),[9.399]→[9.2609:2669](∅→∅),[9.425]→[9.2609:2669](∅→∅),[9.1238]→[9.2609:2669](∅→∅),[9.5305]→[9.2609:2669](∅→∅),[9.2609]→[9.2609:2669](∅→∅),[9.2729]→[9.2729:2769](∅→∅),[9.2769]→[9.660:706](∅→∅),[9.706]→[9.5306:5358](∅→∅),[9.1675]→[9.5306:5358](∅→∅),[9.2769]→[9.5306:5358](∅→∅),[9.5358]→[9.777:809](∅→∅),[9.809]→[9.5358:5399](∅→∅),[9.5358]→[9.5358:5399](∅→∅),[9.5399]→[9.367:393](∅→∅),[9.393]→[9.716:774](∅→∅),[9.774]→[9.1239:1335](∅→∅),[9.730]→[9.1239:1335](∅→∅),[9.1335]→[9.3097:3140](∅→∅),[9.5678]→[9.3097:3140](∅→∅),[9.3097]→[9.3097:3140](∅→∅),[9.3183]→[9.3183:3223](∅→∅),[9.3223]→[9.707:753](∅→∅),[9.753]→[9.5679:5724](∅→∅),[9.1780]→[9.5679:5724](∅→∅),[9.3223]→[9.5679:5724](∅→∅),[9.5724]→[9.810:842](∅→∅),[9.842]→[9.5724:5765](∅→∅),[9.5724]→[9.5724:5765](∅→∅),[9.5765]→[9.394:420](∅→∅),[9.420]→[9.775:835](∅→∅),[9.835]→[9.1336:1385](∅→∅),[9.782]→[9.1336:1385](∅→∅),[9.1385]→[9.3440:3479](∅→∅),[9.5926]→[9.3440:3479](∅→∅),[9.3440]→[9.3440:3479](∅→∅),[9.3518]→[9.3518:3558](∅→∅),[9.3558]→[9.754:800](∅→∅),[9.800]→[9.5927:5972](∅→∅),[9.1885]→[9.5927:5972](∅→∅),[9.3558]→[9.5927:5972](∅→∅),[9.5972]→[9.843:875](∅→∅),[9.875]→[9.5972:6044](∅→∅),[9.5972]→[9.5972:6044](∅→∅),[9.6044]→[9.421:447](∅→∅),[9.447]→[9.836:896](∅→∅),[9.896]→[9.1386:1435](∅→∅),[9.834]→[9.1386:1435](∅→∅),[9.1435]→[9.3802:3807](∅→∅),[9.6201]→[9.3802:3807](∅→∅),[9.3802]→[9.3802:3807](∅→∅),[9.3807]→[9.400:443](∅→∅),[9.491]→[9.491:531](∅→∅),[9.531]→[9.801:847](∅→∅),[9.847]→[9.6202:6248](∅→∅),[9.1990]→[9.6202:6248](∅→∅),[9.531]→[9.6202:6248](∅→∅),[9.6248]→[9.876:908](∅→∅),[9.908]→[9.6248:6320](∅→∅),[9.6248]→[9.6248:6320](∅→∅),[9.6320]→[9.448:474](∅→∅),[9.474]→[9.897:957](∅→∅),[9.957]→[9.1436:1485](∅→∅),[9.886]→[9.1436:1485](∅→∅),[9.1485]→[9.785:790](∅→∅),[9.6486]→[9.785:790](∅→∅),[9.785]→[9.785:790](∅→∅),[9.790]→[9.426:476](∅→∅),[9.531]→[9.531:571](∅→∅),[9.571]→[9.848:894](∅→∅),[9.894]→[9.6487:6533](∅→∅),[9.2095]→[9.6487:6533](∅→∅),[9.571]→[9.6487:6533](∅→∅),[9.6533]→[9.909:941](∅→∅),[9.941]→[9.6533:6606](∅→∅),[9.6533]→[9.6533:6606](∅→∅),[9.6606]→[9.475:501](∅→∅),[9.501]→[9.958:1018](∅→∅),[9.1018]→[9.1486:1535](∅→∅),[9.938]→[9.1486:1535](∅→∅),[9.1535]→[9.833:838](∅→∅),[9.6779]→[9.833:838](∅→∅),[9.833]→[9.833:838](∅→∅),[9.790]→[9.3807:3858](∅→∅),[9.838]→[9.3807:3858](∅→∅),[9.3807]→[9.3807:3858](∅→∅),[9.3914]→[9.3914:3954](∅→∅),[9.3954]→[9.895:941](∅→∅),[9.941]→[9.6780:6832](∅→∅),[9.2200]→[9.6780:6832](∅→∅),[9.3954]→[9.6780:6832](∅→∅),[9.6832]→[9.942:974](∅→∅),[9.974]→[9.6832:6873](∅→∅),[9.6832]→[9.6832:6873](∅→∅),[9.6873]→[9.502:528](∅→∅),[9.528]→[9.1019:1079](∅→∅),[9.1079]→[9.1536:1632](∅→∅),[9.990]→[9.1536:1632](∅→∅),[9.1632]→[9.4275:4280](∅→∅),[9.7144]→[9.4275:4280](∅→∅),[9.4275]→[9.4275:4280](∅→∅),[9.4280]→[9.2:37](∅→∅),[9.77]→[9.2:41](∅→∅),[9.40]→[9.2:41](∅→∅),[9.41]→[9.942:988](∅→∅),[9.151]→[9.942:988](∅→∅),[9.988]→[9.42:97](∅→∅),[9.97]→[9.975:1007](∅→∅),[9.7193]→[9.975:1007](∅→∅),[9.1007]→[9.98:139](∅→∅),[9.139]→[9.7320:7365](∅→∅),[9.7320]→[9.7320:7365](∅→∅),[9.7400]→[9.140:171](∅→∅),[9.171]→[6.2:75](∅→∅),[6.75]→[9.266:354](∅→∅),[9.97]→[9.266:354](∅→∅),[9.266]→[9.266:354](∅→∅),[9.354]→[9.1633:1743](∅→∅),[9.244]→[9.435:492](∅→∅),[9.1743]→[9.435:492](∅→∅),[9.435]→[9.435:492](∅→∅),[9.492]→[9.1744:1862](∅→∅),[9.112]→[9.2:7](∅→∅),[9.419]→[9.2:7](∅→∅),[9.662]→[9.2:7](∅→∅),[9.1862]→[9.2:7](∅→∅),[9.7687]→[9.2:7](∅→∅),[9.105]→[9.2:7](∅→∅),[9.7]→[9.420:458](∅→∅),[9.501]→[9.105:399](∅→∅),[9.105]→[9.105:399](∅→∅),[9.434]→[9.2:33](∅→∅),[9.33]→[9.434:616](∅→∅),[9.434]→[9.434:616](∅→∅),[9.616]→[9.1863:2070](∅→∅),[9.802]→[9.949:954](∅→∅),[9.2070]→[9.949:954](∅→∅),[9.949]→[9.949:954](∅→∅),[9.954]→[9.803:852](∅→∅),[9.906]→[9.290:654](∅→∅),[9.290]→[9.290:654](∅→∅),[9.689]→[9.34:65](∅→∅),[9.65]→[9.689:848](∅→∅),[9.689]→[9.689:848](∅→∅),[9.848]→[9.2071:2278](∅→∅),[9.1240]→[9.1214:1219](∅→∅),[9.2278]→[9.1214:1219](∅→∅),[9.1214]→[9.1214:1219](∅→∅),[9.1219]→[9.1241:1277](∅→∅),[9.1318]→[9.106:193](∅→∅),[9.106]→[9.106:193](∅→∅),[9.193]→[9.989:1035](∅→∅),[9.1035]→[9.7688:7733](∅→∅),[9.2497]→[9.7688:7733](∅→∅),[9.193]→[9.7688:7733](∅→∅),[9.7733]→[9.1008:1040](∅→∅),[9.1040]→[9.7819:7905](∅→∅),[9.7819]→[9.7819:7905](∅→∅),[9.7940]→[9.66:97](∅→∅),[9.97]→[9.354:383](∅→∅),[9.7940]→[9.354:383](∅→∅),[9.354]→[9.354:383](∅→∅),[9.383]→[9.556:582](∅→∅),[9.582]→[9.2498:2584](∅→∅),[9.1192]→[9.455:473](∅→∅),[9.2584]→[9.455:473](∅→∅),[9.8026]→[9.455:473](∅→∅),[9.455]→[9.455:473](∅→∅),[9.473]→[9.98:280](∅→∅),[9.280]→[8.0:47](∅→∅),[8.47]→[9.318:584](∅→∅),[9.318]→[9.318:584](∅→∅),[9.619]→[9.619:791](∅→∅),[9.791]→[8.48:185](∅→∅),[8.185]→[9.817:943](∅→∅),[9.2330]→[9.817:943](∅→∅),[9.105]→[9.524:529](∅→∅),[9.551]→[9.524:529](∅→∅),[9.943]→[9.524:529](∅→∅),[9.1399]→[9.524:529](∅→∅),[9.2330]→[9.524:529](∅→∅),[9.8118]→[9.524:529](∅→∅),[9.524]→[9.524:529](∅→∅),[9.529]→[9.93:119](∅→∅),[9.93]→[9.93:119](∅→∅),[9.150]→[9.150:190](∅→∅),[9.190]→[9.1036:1082](∅→∅),[9.1082]→[9.8119:8174](∅→∅),[9.2689]→[9.8119:8174](∅→∅),[9.190]→[9.8119:8174](∅→∅),[9.8174]→[9.1041:1073](∅→∅),[9.1073]→[9.8260:8346](∅→∅),[9.8260]→[9.8260:8346](∅→∅),[9.8381]→[9.583:609](∅→∅),[9.609]→[9.2690:2719](∅→∅),[9.2719]→[9.2331:2372](∅→∅),[9.2372]→[9.8419:8454](∅→∅),[9.442]→[9.8419:8454](∅→∅),[9.8454]→[9.2373:2414](∅→∅),[9.2414]→[9.8455:8490](∅→∅),[9.524]→[9.8455:8490](∅→∅),[9.8490]→[9.2415:2456](∅→∅),[9.2456]→[9.606:646](∅→∅),[9.606]→[9.606:646](∅→∅),[9.686]→[9.686:725](∅→∅),[9.725]→[9.1083:1129](∅→∅),[9.1129]→[9.8491:8548](∅→∅),[9.2824]→[9.8491:8548](∅→∅),[9.725]→[9.8491:8548](∅→∅),[9.8548]→[9.1074:1106](∅→∅),[9.1106]→[9.8634:8720](∅→∅),[9.8634]→[9.8634:8720](∅→∅),[9.8755]→[9.610:636](∅→∅),[9.636]→[9.2825:2854](∅→∅),[9.2854]→[9.2457:2498](∅→∅),[9.2498]→[9.8793:8828](∅→∅),[9.988]→[9.8793:8828](∅→∅),[9.8828]→[9.2499:2539](∅→∅),[9.70]→[9.8829:8864](∅→∅),[9.2539]→[9.8829:8864](∅→∅),[9.1079]→[9.8829:8864](∅→∅),[9.8864]→[9.2540:2581](∅→∅),[9.140]→[9.1169:1214](∅→∅),[9.2581]→[9.1169:1214](∅→∅),[9.1169]→[9.1169:1214](∅→∅),[9.1259]→[9.1259:1298](∅→∅),[9.1298]→[9.1130:1176](∅→∅),[9.1176]→[9.8865:8921](∅→∅),[9.2959]→[9.8865:8921](∅→∅),[9.1298]→[9.8865:8921](∅→∅),[9.8921]→[9.1107:1139](∅→∅),[9.1139]→[9.9007:9093](∅→∅),[9.9007]→[9.9007:9093](∅→∅),[9.9128]→[9.637:663](∅→∅),[9.663]→[9.2960:2989](∅→∅),[9.2989]→[9.2582:2624](∅→∅),[9.2624]→[9.9166:9201](∅→∅),[9.1566]→[9.9166:9201](∅→∅),[9.9201]→[9.2625:2667](∅→∅),[9.2667]→[9.9202:9237](∅→∅),[9.1663]→[9.9202:9237](∅→∅),[9.9237]→[9.2668:2709](∅→∅),[9.2709]→[9.1220:1225](∅→∅),[9.1759]→[9.1220:1225](∅→∅),[9.1225]→[9.1400:1439](∅→∅),[9.1439]→[2.2:59](∅→∅),[2.59]→[9.1380:1649](∅→∅),[9.1380]→[9.1380:1649](∅→∅),[9.1684]→[9.1684:1843](∅→∅),[9.1843]→[9.2710:2917](∅→∅),[9.1787]→[9.1759:1764](∅→∅),[9.2179]→[9.1759:1764](∅→∅),[9.2917]→[9.1759:1764](∅→∅),[9.1759]→[9.1759:1764](∅→∅),[9.1764]→[9.1788:1854](∅→∅),[9.1854]→[2.60:117](∅→∅),[2.117]→[9.2389:2721](∅→∅),[9.2389]→[9.2389:2721](∅→∅),[9.2756]→[9.2756:2915](∅→∅),[9.2915]→[9.2918:3125](∅→∅),[9.2310]→[9.3332:3337](∅→∅),[9.3125]→[9.3332:3337](∅→∅),[9.3332]→[9.3332:3337](∅→∅),[9.3337]→[9.1764:1875](∅→∅),[9.1764]→[9.1764:1875](∅→∅),[9.1927]→[9.1927:1966](∅→∅),[9.1966]→[9.1177:1223](∅→∅),[9.1223]→[9.9238:9296](∅→∅),[9.3094]→[9.9238:9296](∅→∅),[9.1966]→[9.9238:9296](∅→∅),[9.9296]→[9.1140:1172](∅→∅),[9.1172]→[9.9382:9468](∅→∅),[9.9382]→[9.9382:9468](∅→∅),[9.9503]→[9.664:690](∅→∅),[9.690]→[9.3095:3124](∅→∅),[9.3124]→[9.3126:3169](∅→∅),[9.3169]→[9.9541:9576](∅→∅),[9.2244]→[9.9541:9576](∅→∅),[9.9576]→[9.3170:3213](∅→∅),[9.224]→[9.9577:9612](∅→∅),[9.3213]→[9.9577:9612](∅→∅),[9.2350]→[9.9577:9612](∅→∅),[9.9612]→[9.3214:3255](∅→∅),[9.306]→[9.2:106](∅→∅),[9.3255]→[9.2:106](∅→∅),[9.2452]→[9.2:106](∅→∅),[9.167]→[9.167:206](∅→∅),[9.206]→[9.1224:1270](∅→∅),[9.1270]→[9.9613:9710](∅→∅),[9.3229]→[9.9613:9710](∅→∅),[9.206]→[9.9613:9710](∅→∅),[9.9710]→[9.1173:1205](∅→∅),[9.1205]→[9.9796:9882](∅→∅),[9.9796]→[9.9796:9882](∅→∅),[9.9917]→[9.691:717](∅→∅),[9.717]→[9.3230:3259](∅→∅),[9.3259]→[9.3256:3297](∅→∅),[9.397]→[9.9955:9990](∅→∅),[9.3297]→[9.9955:9990](∅→∅),[9.531]→[9.9955:9990](∅→∅),[9.9990]→[9.3298:3340](∅→∅),[9.93]→[9.9991:10026](∅→∅),[9.488]→[9.9991:10026](∅→∅),[9.3340]→[9.9991:10026](∅→∅),[9.646]→[9.9991:10026](∅→∅),[9.10026]→[9.3341:3385](∅→∅),[9.187]→[9.2452:2457](∅→∅),[9.582]→[9.2452:2457](∅→∅),[9.759]→[9.2452:2457](∅→∅),[9.3385]→[9.2452:2457](∅→∅),[9.2452]→[9.2452:2457](∅→∅),[9.2457]→[2.118:164](∅→∅),[2.164]→[9.85:114](∅→∅),[9.85]→[9.85:114](∅→∅),[9.114]→[9.2:41](∅→∅),[9.41]→[9.1271:1317](∅→∅),[9.1317]→[9.1206:1256](∅→∅),[9.1256]→[9.10027:10079](∅→∅),[9.190]→[9.10027:10079](∅→∅),[9.10079]→[9.1257:1289](∅→∅),[9.1289]→[9.10165:10251](∅→∅),[9.10165]→[9.10165:10251](∅→∅),[9.10286]→[9.718:744](∅→∅),[9.744]→[9.3365:3394](∅→∅),[9.3394]→[9.3386:3439](∅→∅),[9.3439]→[9.10324:10359](∅→∅),[9.450]→[9.10324:10359](∅→∅),[9.10359]→[9.3440:3493](∅→∅),[9.668]→[9.10360:10395](∅→∅),[9.3493]→[9.10360:10395](∅→∅),[9.558]→[9.10360:10395](∅→∅),[9.10395]→[9.580:622](∅→∅),[9.580]→[9.580:622](∅→∅),[9.622]→[9.1193:1263](∅→∅),[9.1263]→[5.0:78](∅→∅),[5.78]→[9.3494:3550](∅→∅),[9.730]→[9.3494:3550](∅→∅),[9.3550]→[5.79:134](∅→∅),[5.134]→[9.879:884](∅→∅),[9.756]→[9.879:884](∅→∅),[9.3605]→[9.879:884](∅→∅),[9.10571]→[9.879:884](∅→∅),[9.879]→[9.879:884](∅→∅),[9.884]→[9.2:80](∅→∅),[9.163]→[9.163:220](∅→∅),[9.220]→[9.42:81](∅→∅),[9.81]→[9.1318:1364](∅→∅),[9.1364]→[9.1290:1340](∅→∅),[9.1340]→[9.10572:10624](∅→∅),[9.296]→[9.10572:10624](∅→∅),[9.10624]→[9.1341:1373](∅→∅),[9.1373]→[9.10710:10796](∅→∅),[9.10710]→[9.10710:10796](∅→∅),[9.10831]→[9.745:771](∅→∅),[9.771]→[9.3500:3529](∅→∅),[9.3529]→[9.3606:3659](∅→∅),[9.881]→[9.10869:10904](∅→∅),[9.3659]→[9.10869:10904](∅→∅),[9.596]→[9.10869:10904](∅→∅),[9.10904]→[9.618:660](∅→∅),[9.618]→[9.618:660](∅→∅),[9.660]→[9.1264:1334](∅→∅),[9.1334]→[5.135:213](∅→∅),[5.213]→[9.3660:3716](∅→∅),[9.755]→[9.3660:3716](∅→∅),[9.3716]→[5.214:269](∅→∅),[5.269]→[9.982:987](∅→∅),[9.1008]→[9.982:987](∅→∅),[9.3771]→[9.982:987](∅→∅),[9.11158]→[9.982:987](∅→∅),[9.982]→[9.982:987](∅→∅),[9.987]→[9.884:932](∅→∅),[9.884]→[9.884:932](∅→∅),[9.985]→[9.985:1014](∅→∅),[9.1014]→[9.82:121](∅→∅),[9.121]→[9.1365:1411](∅→∅),[9.1411]→[9.1374:1424](∅→∅),[9.1424]→[9.11159:11211](∅→∅),[9.1090]→[9.11159:11211](∅→∅),[9.11211]→[9.1425:1457](∅→∅),[9.1457]→[9.11297:11383](∅→∅),[9.11297]→[9.11297:11383](∅→∅),[9.11418]→[9.772:798](∅→∅),[9.798]→[9.3635:3664](∅→∅),[9.3664]→[9.3772:3825](∅→∅),[9.3825]→[9.11456:11491](∅→∅),[9.1359]→[9.11456:11491](∅→∅),[9.11491]→[9.3826:3879](∅→∅),[9.1103]→[9.11492:11527](∅→∅),[9.3879]→[9.11492:11527](∅→∅),[9.1476]→[9.11492:11527](∅→∅),[9.11527]→[9.3880:3929](∅→∅),[9.1194]→[9.11528:11563](∅→∅),[9.3929]→[9.11528:11563](∅→∅),[9.1587]→[9.11528:11563](∅→∅),[9.11563]→[9.1609:1639](∅→∅),[9.1609]→[9.1609:1639](∅→∅),[9.1639]→[9.1335:1405](∅→∅),[9.1405]→[9.1707:1740](∅→∅),[9.1707]→[9.1707:1740](∅→∅),[9.1740]→[9.3930:4031](∅→∅),[9.4031]→[9.1869:1874](∅→∅),[9.11706]→[9.1869:1874](∅→∅),[9.1869]→[9.1869:1874](∅→∅),[9.1874]→[9.1195:1264](∅→∅),[9.1338]→[9.127:177](∅→∅),[9.127]→[9.127:177](∅→∅),[9.177]→[9.122:161](∅→∅),[9.161]→[9.1412:1458](∅→∅),[9.1458]→[9.1458:1508](∅→∅),[9.1508]→[9.11707:11797](∅→∅),[9.253]→[9.11707:11797](∅→∅),[9.11797]→[9.1509:1541](∅→∅),[9.1541]→[9.11883:11969](∅→∅),[9.11883]→[9.11883:11969](∅→∅),[9.12004]→[9.799:825](∅→∅),[9.825]→[9.3770:3799](∅→∅),[9.3799]→[9.4032:4085](∅→∅),[9.1454]→[9.12042:12077](∅→∅),[9.4085]→[9.12042:12077](∅→∅),[9.572]→[9.12042:12077](∅→∅),[9.12077]→[9.4086:4141](∅→∅),[9.1572]→[9.12078:12113](∅→∅),[9.4141]→[9.12078:12113](∅→∅),[9.703]→[9.12078:12113](∅→∅),[9.12113]→[9.4142:4191](∅→∅),[9.1684]→[9.12114:12149](∅→∅),[9.4191]→[9.12114:12149](∅→∅),[9.826]→[9.12114:12149](∅→∅),[9.12149]→[9.848:878](∅→∅),[9.848]→[9.848:878](∅→∅),[9.878]→[9.1406:1476](∅→∅),[9.124]→[9.948:981](∅→∅),[9.1476]→[9.948:981](∅→∅),[9.948]→[9.948:981](∅→∅),[9.981]→[9.4192:4293](∅→∅),[9.1848]→[9.2:60](∅→∅),[9.4293]→[9.2:60](∅→∅),[9.12304]→[9.2:60](∅→∅),[9.1122]→[9.2:60](∅→∅),[9.118]→[9.118:208](∅→∅),[9.208]→[9.162:202](∅→∅),[9.202]→[9.1459:1505](∅→∅),[9.1505]→[9.1542:1655](∅→∅),[9.1655]→[9.12305:12387](∅→∅),[9.335]→[9.12305:12387](∅→∅),[9.12387]→[9.1656:1688](∅→∅),[9.1688]→[9.12473:12559](∅→∅),[9.12473]→[9.12473:12559](∅→∅),[9.12594]→[9.826:852](∅→∅),[9.852]→[9.3905:3934](∅→∅),[9.3934]→[9.4294:4361](∅→∅),[9.4361]→[9.12632:12667](∅→∅),[9.654]→[9.12632:12667](∅→∅),[9.12667]→[9.203:246](∅→∅),[9.676]→[9.203:246](∅→∅),[9.246]→[9.1477:1547](∅→∅),[9.1547]→[5.270:399](∅→∅),[5.399]→[9.2:35](∅→∅),[9.927]→[9.2:35](∅→∅),[9.4413]→[9.2:35](∅→∅),[9.12765]→[9.2:35](∅→∅),[9.1122]→[9.2:35](∅→∅),[9.68]→[9.68:96](∅→∅),[9.96]→[9.302:341](∅→∅),[9.341]→[9.1506:1552](∅→∅),[9.1552]→[9.12766:12811](∅→∅),[9.4039]→[9.12766:12811](∅→∅),[9.341]→[9.12766:12811](∅→∅),[9.12811]→[9.1689:1721](∅→∅),[9.1721]→[9.12897:12983](∅→∅),[9.12897]→[9.12897:12983](∅→∅),[9.13018]→[9.853:879](∅→∅),[9.466]→[9.280:332](∅→∅),[9.879]→[9.280:332](∅→∅),[9.280]→[9.280:332](∅→∅),[9.332]→[9.1080:1140](∅→∅),[9.1140]→[9.368:401](∅→∅),[9.1599]→[9.368:401](∅→∅),[9.368]→[9.368:401](∅→∅),[9.401]→[9.50:93](∅→∅),[9.93]→[9.2:55](∅→∅),[9.55]→[9.4414:4646](∅→∅),[9.4646]→[9.2:70](∅→∅),[9.13335]→[9.2:70](∅→∅),[9.746]→[9.2:70](∅→∅),[9.138]→[9.138:190](∅→∅),[9.190]→[9.342:381](∅→∅),[9.381]→[9.1553:1599](∅→∅),[9.1599]→[9.13336:13377](∅→∅),[9.4144]→[9.13336:13377](∅→∅),[9.381]→[9.13336:13377](∅→∅),[9.13377]→[9.1722:1754](∅→∅),[9.1754]→[9.13463:13593](∅→∅),[9.13463]→[9.13463:13593](∅→∅),[9.13628]→[9.880:906](∅→∅),[9.481]→[9.401:439](∅→∅),[9.906]→[9.401:439](∅→∅),[9.401]→[9.401:439](∅→∅),[9.439]→[9.1141:1199](∅→∅),[9.1199]→[9.473:516](∅→∅),[9.1649]→[9.473:516](∅→∅),[9.473]→[9.473:516](∅→∅),[9.516]→[9.4647:4752](∅→∅),[9.701]→[9.2:7](∅→∅),[9.746]→[9.2:7](∅→∅),[9.4752]→[9.2:7](∅→∅),[9.13840]→[9.2:7](∅→∅),[9.1122]→[9.2:7](∅→∅),[9.1127]→[9.2:41](∅→∅),[9.85]→[9.85:137](∅→∅),[9.137]→[9.422:461](∅→∅),[9.461]→[9.1600:1646](∅→∅),[9.1646]→[9.13841:13882](∅→∅),[9.4249]→[9.13841:13882](∅→∅),[9.461]→[9.13841:13882](∅→∅),[9.13882]→[9.1755:1787](∅→∅),[9.1787]→[9.13968:14098](∅→∅),[9.13968]→[9.13968:14098](∅→∅),[9.14133]→[9.907:933](∅→∅),[9.496]→[9.348:365](∅→∅),[9.933]→[9.348:365](∅→∅),[9.348]→[9.348:365](∅→∅),[9.365]→[9.146:193](∅→∅),[9.193]→[9.396:452](∅→∅),[9.1696]→[9.396:452](∅→∅),[9.396]→[9.396:452](∅→∅),[9.452]→[9.4753:4808](∅→∅),[9.4808]→[9.2:61](∅→∅),[9.14215]→[9.2:61](∅→∅),[9.520]→[9.2:61](∅→∅),[9.120]→[9.120:172](∅→∅),[9.172]→[9.462:501](∅→∅),[9.501]→[9.1647:1693](∅→∅),[9.1693]→[9.14216:14257](∅→∅),[9.4354]→[9.14216:14257](∅→∅),[9.501]→[9.14216:14257](∅→∅),[9.14257]→[9.1788:1820](∅→∅),[9.1820]→[9.14343:14473](∅→∅),[9.14343]→[9.14343:14473](∅→∅),[9.14508]→[9.934:960](∅→∅),[9.511]→[9.383:465](∅→∅),[9.960]→[9.383:465](∅→∅),[9.383]→[9.383:465](∅→∅),[9.465]→[9.194:315](∅→∅),[9.135]→[9.531:620](∅→∅),[9.315]→[9.531:620](∅→∅),[9.531]→[9.531:620](∅→∅),[9.620]→[9.4809:4914](∅→∅),[9.787]→[9.2:53](∅→∅),[9.4914]→[9.2:53](∅→∅),[9.14702]→[9.2:53](∅→∅),[9.520]→[9.2:53](∅→∅),[9.104]→[9.104:149](∅→∅),[9.149]→[9.502:541](∅→∅),[9.541]→[9.1694:1740](∅→∅),[9.1740]→[9.14703:14744](∅→∅),[9.4459]→[9.14703:14744](∅→∅),[9.541]→[9.14703:14744](∅→∅),[9.14744]→[9.1821:1853](∅→∅),[9.1853]→[9.14830:14960](∅→∅),[9.14830]→[9.14830:14960](∅→∅),[9.14995]→[9.961:987](∅→∅),[9.526]→[9.360:380](∅→∅),[9.987]→[9.360:380](∅→∅),[9.360]→[9.360:380](∅→∅),[9.380]→[9.1200:1252](∅→∅),[9.1252]→[9.4915:4959](∅→∅),[9.1744]→[9.4915:4959](∅→∅),[9.4959]→[9.495:550](∅→∅),[9.495]→[9.495:550](∅→∅),[9.550]→[9.4960:5007](∅→∅),[9.5007]→[9.2:27](∅→∅),[9.15076]→[9.2:27](∅→∅),[9.617]→[9.2:27](∅→∅),[9.52]→[9.52:104](∅→∅),[9.104]→[9.542:581](∅→∅),[9.581]→[9.1741:1787](∅→∅),[9.1787]→[9.15077:15118](∅→∅),[9.4564]→[9.15077:15118](∅→∅),[9.581]→[9.15077:15118](∅→∅),[9.15118]→[9.1854:1886](∅→∅),[9.1886]→[9.15204:15334](∅→∅),[9.15204]→[9.15204:15334](∅→∅),[9.15369]→[9.988:1014](∅→∅),[9.541]→[9.315:332](∅→∅),[9.1014]→[9.315:332](∅→∅),[9.315]→[9.315:332](∅→∅),[9.332]→[9.1253:1305](∅→∅),[9.1305]→[9.5008:5052](∅→∅),[9.1792]→[9.5008:5052](∅→∅),[9.5052]→[9.421:451](∅→∅),[9.421]→[9.421:451](∅→∅),[9.451]→[9.5053:5106](∅→∅),[9.5106]→[9.504:550](∅→∅),[9.15436]→[9.504:550](∅→∅),[9.504]→[9.504:550](∅→∅),[9.596]→[9.596:641](∅→∅),[9.641]→[9.582:621](∅→∅),[9.621]→[9.1788:1834](∅→∅),[9.1834]→[9.15437:15485](∅→∅),[9.4669]→[9.15437:15485](∅→∅),[9.621]→[9.15437:15485](∅→∅),[9.15485]→[9.1887:1919](∅→∅),[9.1919]→[9.15571:15701](∅→∅),[9.15571]→[9.15571:15701](∅→∅),[9.15736]→[9.1015:1041](∅→∅),[9.556]→[9.859:923](∅→∅),[9.1041]→[9.859:923](∅→∅),[9.859]→[9.859:923](∅→∅),[9.923]→[9.1306:1358](∅→∅),[9.1358]→[9.955:1068](∅→∅),[9.1840]→[9.955:1068](∅→∅),[9.955]→[9.955:1068](∅→∅),[9.1068]→[9.5107:5165](∅→∅),[9.617]→[9.520:525](∅→∅),[9.1141]→[9.520:525](∅→∅),[9.5165]→[9.520:525](∅→∅),[9.15823]→[9.520:525](∅→∅),[9.520]→[9.520:525](∅→∅),[9.525]→[9.2:48](∅→∅),[9.99]→[9.99:164](∅→∅),[9.164]→[9.1835:1881](∅→∅),[9.1881]→[9.15824:15886](∅→∅),[9.4774]→[9.15824:15886](∅→∅),[9.164]→[9.15824:15886](∅→∅),[9.15886]→[9.1920:1952](∅→∅),[9.1952]→[9.15972:16058](∅→∅),[9.15972]→[9.15972:16058](∅→∅),[9.16093]→[9.1042:1068](∅→∅),[9.1068]→[9.4775:4804](∅→∅),[9.4804]→[9.5166:5216](∅→∅),[9.5216]→[9.16131:16166](∅→∅),[9.452]→[9.16131:16166](∅→∅),[9.16166]→[9.5217:5267](∅→∅),[9.5267]→[9.16167:16202](∅→∅),[9.563]→[9.16167:16202](∅→∅),[9.16202]→[9.5268:5318](∅→∅),[9.5318]→[9.674:742](∅→∅),[9.674]→[9.674:742](∅→∅),[9.742]→[9.16203:16247](∅→∅),[9.16247]→[9.773:795](∅→∅),[9.773]→[9.773:795](∅→∅),[9.795]→[9.1359:1425](∅→∅),[9.1425]→[9.833:854](∅→∅),[9.1894]→[9.833:854](∅→∅),[9.833]→[9.833:854](∅→∅),[9.854]→[9.5319:5427](∅→∅),[9.5427]→[9.1008:1013](∅→∅),[9.16428]→[9.1008:1013](∅→∅),[9.1008]→[9.1008:1013](∅→∅),[9.525]→[9.2457:2492](∅→∅),[9.1013]→[9.2457:2492](∅→∅),[9.1127]→[9.2457:2492](∅→∅),[9.2457]→[9.2457:2492](∅→∅),[9.2532]→[9.2532:2571](∅→∅),[9.2571]→[9.1882:1928](∅→∅),[9.1928]→[9.16429:16484](∅→∅),[9.4909]→[9.16429:16484](∅→∅),[9.2571]→[9.16429:16484](∅→∅),[9.16484]→[9.1953:1985](∅→∅),[9.1985]→[9.16570:16656](∅→∅),[9.16570]→[9.16570:16656](∅→∅),[9.16691]→[9.1069:1095](∅→∅),[9.1095]→[9.316:363](∅→∅),[9.363]→[9.4910:4939](∅→∅),[9.2079]→[9.4910:4939](∅→∅),[9.4939]→[9.5428:5469](∅→∅),[9.5469]→[9.16729:16764](∅→∅),[9.3005]→[9.16729:16764](∅→∅),[9.16764]→[9.5470:5510](∅→∅),[9.1917]→[9.16765:16800](∅→∅),[9.5510]→[9.16765:16800](∅→∅),[9.3096]→[9.16765:16800](∅→∅),[9.16800]→[9.5511:5551](∅→∅),[9.1986]→[9.3188:3224](∅→∅),[9.5551]→[9.3188:3224](∅→∅),[9.3188]→[9.3188:3224](∅→∅),[9.3260]→[9.2:27](∅→∅),[9.27]→[9.4940:4997](∅→∅),[9.4997]→[9.1929:1975](∅→∅),[9.1975]→[9.16865:16927](∅→∅),[9.5101]→[9.16865:16927](∅→∅),[9.16865]→[9.16865:16927](∅→∅),[9.16927]→[9.1986:2018](∅→∅),[9.2018]→[9.17013:17099](∅→∅),[9.17013]→[9.17013:17099](∅→∅),[9.17134]→[9.1096:1122](∅→∅),[9.1122]→[9.5102:5131](∅→∅),[9.5131]→[9.5552:5602](∅→∅),[9.5602]→[9.17172:17207](∅→∅),[9.3627]→[9.17172:17207](∅→∅),[9.17207]→[9.5603:5653](∅→∅),[9.5653]→[9.17208:17243](∅→∅),[9.3723]→[9.17208:17243](∅→∅),[9.17243]→[9.5654:5704](∅→∅),[9.5704]→[9.28:71](∅→∅),[9.3819]→[9.28:71](∅→∅),[9.71]→[9.1426:1486](∅→∅),[9.1486]→[9.5705:5874](∅→∅),[9.2130]→[9.5705:5874](∅→∅),[9.5874]→[9.5132:5155](∅→∅),[9.17485]→[9.5132:5155](∅→∅),[9.5155]→[9.5875:5914](∅→∅),[9.5914]→[9.17516:17551](∅→∅),[9.4193]→[9.17516:17551](∅→∅),[9.17551]→[9.5915:5955](∅→∅),[9.5955]→[9.17552:17587](∅→∅),[9.4279]→[9.17552:17587](∅→∅),[9.17587]→[9.5956:5997](∅→∅),[9.5997]→[9.1488:1541](∅→∅),[9.4366]→[9.1488:1541](∅→∅),[9.1594]→[9.1594:1614](∅→∅),[9.1614]→[9.5156:5213](∅→∅),[9.5213]→[9.1976:2022](∅→∅),[9.2022]→[9.17652:17693](∅→∅),[9.5317]→[9.17652:17693](∅→∅),[9.17652]→[9.17652:17693](∅→∅),[9.17693]→[9.2019:2051](∅→∅),[9.2051]→[9.17779:17865](∅→∅),[9.17779]→[9.17779:17865](∅→∅),[9.17900]→[9.1798:1841](∅→∅),[9.1798]→[9.1798:1841](∅→∅),[9.1841]→[9.1487:1547](∅→∅),[9.1547]→[9.5998:6217](∅→∅),[9.2181]→[9.5998:6217](∅→∅),[9.2207]→[9.4366:4409](∅→∅),[9.6217]→[9.4366:4409](∅→∅),[9.18284]→[9.4366:4409](∅→∅),[9.4366]→[9.4366:4409](∅→∅),[9.4452]→[9.2:27](∅→∅),[9.27]→[9.5318:5375](∅→∅),[9.5375]→[9.2023:2069](∅→∅),[9.2069]→[9.18349:18411](∅→∅),[9.5479]→[9.18349:18411](∅→∅),[9.18349]→[9.18349:18411](∅→∅),[9.18411]→[9.2052:2084](∅→∅),[9.2084]→[9.18497:18583](∅→∅),[9.18497]→[9.18497:18583](∅→∅),[9.18618]→[9.1123:1149](∅→∅),[9.1149]→[9.5480:5509](∅→∅),[9.5509]→[9.6218:6268](∅→∅),[9.6268]→[9.18656:18691](∅→∅),[9.4826]→[9.18656:18691](∅→∅),[9.18691]→[9.6269:6319](∅→∅),[9.6319]→[9.18692:18727](∅→∅),[9.4929]→[9.18692:18727](∅→∅),[9.18727]→[9.6320:6370](∅→∅),[9.6370]→[9.28:101](∅→∅),[9.5032]→[9.28:101](∅→∅),[9.101]→[9.5089:5115](∅→∅),[9.5089]→[9.5089:5115](∅→∅),[9.5115]→[9.1548:1600](∅→∅),[9.1600]→[9.6371:6540](∅→∅),[9.2229]→[9.6371:6540](∅→∅),[9.6540]→[9.5510:5533](∅→∅),[9.18990]→[9.5510:5533](∅→∅),[9.5533]→[9.6541:6582](∅→∅),[9.6582]→[9.19021:19056](∅→∅),[9.5459]→[9.19021:19056](∅→∅),[9.19056]→[9.6583:6624](∅→∅),[9.6624]→[9.19057:19092](∅→∅),[9.5553]→[9.19057:19092](∅→∅),[9.19092]→[9.6625:6666](∅→∅),[9.6666]→[9.5647:5652](∅→∅),[9.5647]→[9.5647:5652](∅→∅),[9.184]→[9.184:224](∅→∅),[9.269]→[9.269:308](∅→∅),[9.308]→[9.2117:2163](∅→∅),[9.2163]→[9.19904:19959](∅→∅),[9.5832]→[9.19904:19959](∅→∅),[9.308]→[9.19904:19959](∅→∅),[9.19959]→[9.2118:2150](∅→∅),[9.2150]→[9.20045:20131](∅→∅),[9.20045]→[9.20045:20131](∅→∅),[9.20166]→[9.20166:20197](∅→∅),[9.20197]→[6.76:149](∅→∅),[6.149]→[9.2:40](∅→∅),[9.195]→[9.2:40](∅→∅),[9.432]→[9.2:40](∅→∅),[9.732]→[9.2:40](∅→∅),[9.1315]→[9.2:40](∅→∅),[9.20267]→[9.2:40](∅→∅),[9.581]→[9.2:40](∅→∅),[9.40]→[9.5833:5919](∅→∅),[9.141]→[9.107:144](∅→∅),[9.260]→[9.107:144](∅→∅),[9.339]→[9.107:144](∅→∅),[9.2433]→[9.107:144](∅→∅),[9.5919]→[9.107:144](∅→∅),[9.20353]→[9.107:144](∅→∅),[9.107]→[9.107:144](∅→∅),[9.144]→[9.5920:6034](∅→∅),[9.6034]→[9.6667:6899](∅→∅),[9.6899]→[9.108:431](∅→∅),[9.466]→[9.466:497](∅→∅),[9.497]→[9.196:269](∅→∅),[9.269]→[9.592:896](∅→∅),[9.592]→[9.592:896](∅→∅),[9.152]→[9.538:543](∅→∅),[9.896]→[9.538:543](∅→∅),[9.6899]→[9.538:543](∅→∅),[9.20831]→[9.538:543](∅→∅),[9.538]→[9.538:543](∅→∅),[9.543]→[9.2:390](∅→∅),[9.425]→[9.425:467](∅→∅),[9.467]→[4.0:26](∅→∅),[4.26]→[9.467:766](∅→∅),[9.467]→[9.467:766](∅→∅),[9.766]→[9.2:685](∅→∅),[9.720]→[9.720:1268](∅→∅),[9.766]→[9.543:593](∅→∅),[9.1268]→[9.543:593](∅→∅),[9.543]→[9.543:593](∅→∅),[9.648]→[9.648:687](∅→∅),[9.687]→[9.2164:2210](∅→∅),[9.2210]→[9.20832:20887](∅→∅),[9.6139]→[9.20832:20887](∅→∅),[9.687]→[9.20832:20887](∅→∅),[9.20887]→[9.2151:2183](∅→∅),[9.2183]→[9.20973:21059](∅→∅),[9.20973]→[9.20973:21059](∅→∅),[9.21094]→[9.21094:21125](∅→∅),[9.21125]→[9.270:343](∅→∅),[9.343]→[9.581:610](∅→∅),[9.528]→[9.581:610](∅→∅),[9.790]→[9.581:610](∅→∅),[9.960]→[9.581:610](∅→∅),[9.1398]→[9.581:610](∅→∅),[9.21195]→[9.581:610](∅→∅),[9.581]→[9.581:610](∅→∅),[9.610]→[9.6140:6314](∅→∅),[9.364]→[9.746:792](∅→∅),[9.477]→[9.746:792](∅→∅),[9.535]→[9.746:792](∅→∅),[9.2765]→[9.746:792](∅→∅),[9.6314]→[9.746:792](∅→∅),[9.21368]→[9.746:792](∅→∅),[9.746]→[9.746:792](∅→∅),[9.792]→[9.365:396](∅→∅),[9.396]→[9.6315:6516](∅→∅),[9.626]→[9.548:581](∅→∅),[9.670]→[9.548:581](∅→∅),[9.2995]→[9.548:581](∅→∅),[9.6516]→[9.548:581](∅→∅),[9.21568]→[9.548:581](∅→∅),[9.548]→[9.548:581](∅→∅),[9.581]→[9.6900:7132](∅→∅),[9.7132]→[9.2:68](∅→∅),[9.21973]→[9.2:68](∅→∅),[9.323]→[9.2:68](∅→∅),[9.134]→[9.134:173](∅→∅),[9.173]→[9.2211:2257](∅→∅),[9.2257]→[9.21974:22029](∅→∅),[9.6621]→[9.21974:22029](∅→∅),[9.173]→[9.21974:22029](∅→∅),[9.22029]→[9.2184:2248](∅→∅),[9.2248]→[9.22115:22201](∅→∅),[9.22115]→[9.22115:22201](∅→∅),[9.22236]→[9.22236:22267](∅→∅),[9.22267]→[9.344:417](∅→∅),[9.417]→[9.446:475](∅→∅),[9.624]→[9.446:475](∅→∅),[9.848]→[9.446:475](∅→∅),[9.1481]→[9.446:475](∅→∅),[9.22337]→[9.446:475](∅→∅),[9.446]→[9.446:475](∅→∅),[9.475]→[9.6622:6796](∅→∅),[9.720]→[9.611:663](∅→∅),[9.761]→[9.611:663](∅→∅),[9.791]→[9.611:663](∅→∅),[9.3198]→[9.611:663](∅→∅),[9.6796]→[9.611:663](∅→∅),[9.22510]→[9.611:663](∅→∅),[9.611]→[9.611:663](∅→∅),[9.663]→[9.721:752](∅→∅),[9.752]→[9.6797:6998](∅→∅),[9.904]→[9.838:889](∅→∅),[9.910]→[9.838:889](∅→∅),[9.926]→[9.838:889](∅→∅),[9.3428]→[9.838:889](∅→∅),[9.6998]→[9.838:889](∅→∅),[9.22710]→[9.838:889](∅→∅),[9.838]→[9.838:889](∅→∅),[9.889]→[9.905:936](∅→∅),[9.936]→[9.6999:7199](∅→∅),[9.1058]→[9.1087:1120](∅→∅),[9.1060]→[9.1087:1120](∅→∅),[9.3657]→[9.1087:1120](∅→∅),[9.7199]→[9.1087:1120](∅→∅),[9.22909]→[9.1087:1120](∅→∅),[9.1087]→[9.1087:1120](∅→∅),[9.1120]→[9.1090:1185](∅→∅),[9.1090]→[9.1090:1185](∅→∅),[9.1185]→[9.7133:7365](∅→∅),[9.7365]→[9.2:322](∅→∅),[9.357]→[9.357:429](∅→∅),[9.429]→[9.1601:1653](∅→∅),[9.1653]→[9.476:802](∅→∅),[9.476]→[9.476:802](∅→∅),[9.6255]→[9.2:40](∅→∅),[9.83]→[9.83:108](∅→∅),[9.108]→[9.7200:7257](∅→∅),[9.7257]→[9.2258:2304](∅→∅),[9.2304]→[9.23423:23485](∅→∅),[9.7361]→[9.23423:23485](∅→∅),[9.23423]→[9.23423:23485](∅→∅),[9.23485]→[9.2249:2281](∅→∅),[9.2281]→[9.23571:23657](∅→∅),[9.23571]→[9.23571:23657](∅→∅),[9.23692]→[9.23692:23723](∅→∅),[9.23723]→[9.1482:1508](∅→∅),[9.863]→[9.344:383](∅→∅),[9.1508]→[9.344:383](∅→∅),[9.344]→[9.344:383](∅→∅),[9.383]→[9.1654:1706](∅→∅),[9.1706]→[9.415:429](∅→∅),[9.3705]→[9.415:429](∅→∅),[9.415]→[9.415:429](∅→∅),[9.429]→[9.7366:7417](∅→∅),[9.7417]→[9.492:497](∅→∅),[9.23800]→[9.492:497](∅→∅),[9.492]→[9.492:497](∅→∅),[9.497]→[9.6255:6280](∅→∅),[9.6255]→[9.6255:6280](∅→∅),[9.6310]→[9.6310:6350](∅→∅),[9.6350]→[9.2305:2351](∅→∅),[9.2351]→[9.23801:23856](∅→∅),[9.7466]→[9.23801:23856](∅→∅),[9.6350]→[9.23801:23856](∅→∅),[9.23856]→[9.2282:2314](∅→∅),[9.2314]→[9.23942:24028](∅→∅),[9.23942]→[9.23942:24028](∅→∅),[9.24063]→[9.6506:6555](∅→∅),[9.6506]→[9.6506:6555](∅→∅),[9.6555]→[9.1509:1535](∅→∅),[9.1535]→[9.7467:7496](∅→∅),[9.7496]→[9.7418:7468](∅→∅),[9.7468]→[9.24101:24136](∅→∅),[9.6659]→[9.24101:24136](∅→∅),[9.24136]→[9.7469:7519](∅→∅),[9.7519]→[9.6749:6801](∅→∅),[9.6749]→[9.6749:6801](∅→∅),[9.6801]→[9.1707:1771](∅→∅),[9.1771]→[9.7520:7630](∅→∅),[9.3758]→[9.7520:7630](∅→∅),[9.7630]→[9.7497:7520](∅→∅),[9.24283]→[9.7497:7520](∅→∅),[9.7520]→[9.7631:7672](∅→∅),[9.7672]→[9.24314:24349](∅→∅),[9.7034]→[9.24314:24349](∅→∅),[9.24349]→[9.7673:7714](∅→∅),[9.7714]→[9.7115:7160](∅→∅),[9.7115]→[9.7115:7160](∅→∅),[9.7205]→[9.7205:7255](∅→∅),[9.7255]→[9.2:29](∅→∅),[9.29]→[9.7521:7589](∅→∅),[9.7589]→[9.2352:2398](∅→∅),[9.2398]→[9.24425:24493](∅→∅),[9.7693]→[9.24425:24493](∅→∅),[9.24425]→[9.24425:24493](∅→∅),[9.24493]→[9.2315:2551](∅→∅),[9.2551]→[9.7715:7783](∅→∅),[9.7783]→[9.24681:24767](∅→∅),[9.24681]→[9.24681:24767](∅→∅),[9.24802]→[9.2:80](∅→∅),[9.80]→[9.7779:7946](∅→∅),[9.85]→[9.7779:7946](∅→∅),[9.170]→[9.7779:7946](∅→∅),[9.24893]→[9.7779:7946](∅→∅),[9.7779]→[9.7779:7946](∅→∅),[9.7946]→[9.1536:1562](∅→∅),[9.1562]→[9.7694:7723](∅→∅),[9.7723]→[9.7784:7834](∅→∅),[9.7834]→[9.8065:8222](∅→∅),[9.8065]→[9.8065:8222](∅→∅),[9.8222]→[9.1772:1836](∅→∅),[9.1836]→[9.7835:7945](∅→∅),[9.3811]→[9.7835:7945](∅→∅),[9.7945]→[9.7724:7764](∅→∅),[9.25107]→[9.7724:7764](∅→∅),[9.7764]→[9.7946:7987](∅→∅),[9.8507]→[9.1265:1339](∅→∅),[9.1265]→[9.1265:1339](∅→∅),[9.1413]→[9.1413:1475](∅→∅),[9.1475]→[9.7924:7981](∅→∅),[9.7981]→[9.2446:2492](∅→∅),[9.2492]→[9.26150:26242](∅→∅),[9.8085]→[9.26150:26242](∅→∅),[9.26150]→[9.26150:26242](∅→∅),[9.26242]→[9.2585:2617](∅→∅),[9.2617]→[9.26328:26414](∅→∅),[9.26328]→[9.26328:26414](∅→∅),[9.26449]→[9.1590:1616](∅→∅),[9.1616]→[9.8086:8115](∅→∅),[9.8115]→[9.8508:8559](∅→∅),[9.8559]→[9.26487:26522](∅→∅),[9.1859]→[9.26487:26522](∅→∅),[9.26522]→[9.8560:8611](∅→∅),[9.8611]→[9.26523:26558](∅→∅),[9.1994]→[9.26523:26558](∅→∅),[9.26558]→[9.8612:8663](∅→∅),[9.8663]→[9.2129:2192](∅→∅),[9.2129]→[9.2129:2192](∅→∅),[9.2192]→[9.1902:1966](∅→∅),[9.1966]→[9.8664:8790](∅→∅),[9.3917]→[9.8664:8790](∅→∅),[9.8790]→[9.8116:8139](∅→∅),[9.26809]→[9.8116:8139](∅→∅),[9.8139]→[9.8791:8833](∅→∅),[9.8833]→[9.26840:26875](∅→∅),[9.2574]→[9.26840:26875](∅→∅),[9.26875]→[9.8834:8876](∅→∅),[9.106]→[9.26876:26911](∅→∅),[9.8876]→[9.26876:26911](∅→∅),[9.2701]→[9.26876:26911](∅→∅),[9.26911]→[3.2:320](∅→∅),[9.42]→[9.10090:10095](∅→∅),[9.211]→[9.10090:10095](∅→∅),[3.320]→[9.10090:10095](∅→∅),[9.2826]→[9.10090:10095](∅→∅),[9.8919]→[9.10090:10095](∅→∅),[9.10090]→[9.10090:10095](∅→∅),[9.10095]→[9.2:42](∅→∅),[9.87]→[9.87:408](∅→∅),[9.443]→[9.443:499](∅→∅),[9.499]→[9.1967:2031](∅→∅),[9.2031]→[9.8920:9046](∅→∅),[9.551]→[9.8920:9046](∅→∅),[9.9046]→[9.743:748](∅→∅),[9.743]→[9.743:748](∅→∅),[9.748]→[9.10095:10135](∅→∅),[9.10095]→[9.10095:10135](∅→∅),[9.10180]→[9.10180:10220](∅→∅),[9.10220]→[9.2493:2539](∅→∅),[9.2539]→[9.26912:26974](∅→∅),[9.8244]→[9.26912:26974](∅→∅),[9.10220]→[9.26912:26974](∅→∅),[9.26974]→[9.2618:2650](∅→∅),[9.2650]→[9.27060:27146](∅→∅),[9.27060]→[9.27060:27146](∅→∅),[9.27181]→[9.10383:10434](∅→∅),[9.10383]→[9.10383:10434](∅→∅),[9.10434]→[9.1617:1643](∅→∅),[9.1643]→[9.8245:8274](∅→∅),[9.8274]→[9.9047:9097](∅→∅),[9.9097]→[9.27219:27254](∅→∅),[9.10553]→[9.27219:27254](∅→∅),[9.27254]→[9.9098:9148](∅→∅),[9.9148]→[9.27255:27290](∅→∅),[9.10658]→[9.27255:27290](∅→∅),[9.27290]→[9.9149:9199](∅→∅),[9.9199]→[9.10763:10830](∅→∅),[9.10763]→[9.10763:10830](∅→∅),[9.10830]→[9.2032:2088](∅→∅),[9.2088]→[9.9200:9310](∅→∅),[9.3966]→[9.9200:9310](∅→∅),[9.9310]→[9.11013:11042](∅→∅),[9.27467]→[9.11013:11042](∅→∅),[9.11013]→[9.11013:11042](∅→∅),[9.11042]→[9.8275:8298](∅→∅),[9.8298]→[9.9311:9352](∅→∅),[9.9352]→[9.27499:27534](∅→∅),[9.11133]→[9.27499:27534](∅→∅),[9.27534]→[9.9353:9394](∅→∅),[9.9394]→[9.27535:27570](∅→∅),[9.11229]→[9.27535:27570](∅→∅),[9.27570]→[9.9395:9436](∅→∅),[9.9436]→[9.2:535](∅→∅),[9.570]→[9.570:1004](∅→∅),[9.1004]→[9.2089:2145](∅→∅),[9.2145]→[9.1052:1103](∅→∅),[9.1052]→[9.1052:1103](∅→∅),[9.1103]→[9.11325:11382](∅→∅),[9.9436]→[9.11325:11382](∅→∅),[9.11325]→[9.11325:11382](∅→∅),[9.11439]→[9.11439:11549](∅→∅),[9.11549]→[9.2540:2586](∅→∅),[9.2586]→[9.27571:27633](∅→∅),[9.8403]→[9.27571:27633](∅→∅),[9.11549]→[9.27571:27633](∅→∅),[9.27633]→[9.2651:2683](∅→∅),[9.2683]→[9.27719:27805](∅→∅),[9.27719]→[9.27719:27805](∅→∅),[9.27840]→[9.1644:1670](∅→∅),[9.1670]→[9.8404:8433](∅→∅),[9.8433]→[9.9437:9487](∅→∅),[9.9487]→[9.27878:27913](∅→∅),[9.11843]→[9.27878:27913](∅→∅),[9.27913]→[9.9488:9538](∅→∅),[9.9538]→[9.27914:27949](∅→∅),[9.11960]→[9.27914:27949](∅→∅),[9.27949]→[9.9539:9589](∅→∅),[9.9589]→[9.12077:12147](∅→∅),[9.12077]→[9.12077:12147](∅→∅),[9.12147]→[9.2146:2202](∅→∅),[9.2202]→[9.9590:9700](∅→∅),[9.4015]→[9.9590:9700](∅→∅),[9.9700]→[9.8434:8457](∅→∅),[9.28150]→[9.8434:8457](∅→∅),[9.8457]→[9.9701:9742](∅→∅),[9.9742]→[9.28181:28216](∅→∅),[9.12457]→[9.28181:28216](∅→∅),[9.28216]→[9.9743:9784](∅→∅),[9.9784]→[9.28217:28252](∅→∅),[9.12565]→[9.28217:28252](∅→∅),[9.28252]→[9.9785:9826](∅→∅),[9.9826]→[9.12673:12737](∅→∅),[9.12673]→[9.12673:12737](∅→∅),[9.12801]→[9.12801:12871](∅→∅),[9.12871]→[9.8458:8515](∅→∅),[9.8515]→[9.2587:2633](∅→∅),[9.2633]→[9.28317:28383](∅→∅),[9.8619]→[9.28317:28383](∅→∅),[9.28317]→[9.28317:28383](∅→∅),[9.28383]→[9.2684:2716](∅→∅),[9.2716]→[9.28469:28555](∅→∅),[9.28469]→[9.28469:28555](∅→∅),[9.28590]→[9.1671:1697](∅→∅),[9.1697]→[9.8620:8649](∅→∅),[9.8649]→[9.9827:9877](∅→∅),[9.9877]→[9.28628:28663](∅→∅),[9.13218]→[9.28628:28663](∅→∅),[9.28663]→[9.9878:9928](∅→∅),[9.9928]→[9.28664:28699](∅→∅),[9.13342]→[9.28664:28699](∅→∅),[9.28699]→[9.9929:10027](∅→∅),[9.10027]→[9.13514:13584](∅→∅),[9.13514]→[9.13514:13584](∅→∅),[9.13584]→[9.2203:2259](∅→∅),[9.2259]→[9.10028:10197](∅→∅),[9.4064]→[9.10028:10197](∅→∅),[9.949]→[9.8650:8673](∅→∅),[9.949]→[9.8650:8673](∅→∅),[9.10197]→[9.8650:8673](∅→∅),[9.29025]→[9.8650:8673](∅→∅),[9.8673]→[9.10198:10239](∅→∅),[9.10239]→[9.29056:29091](∅→∅),[9.14013]→[9.29056:29091](∅→∅),[9.29091]→[9.10240:10282](∅→∅),[9.10282]→[9.29092:29127](∅→∅),[9.14129]→[9.29092:29127](∅→∅),[9.29127]→[9.10283:10324](∅→∅),[9.10324]→[9.14244:14336](∅→∅),[9.14244]→[9.14244:14336](∅→∅),[9.14428]→[9.14428:14498](∅→∅),[9.14498]→[9.8674:8731](∅→∅),[9.8731]→[9.2634:2680](∅→∅),[9.2680]→[9.29192:29257](∅→∅),[9.8835]→[9.29192:29257](∅→∅),[9.29192]→[9.29192:29257](∅→∅),[9.29257]→[9.2717:2749](∅→∅),[9.2749]→[9.29343:29429](∅→∅),[9.29343]→[9.29343:29429](∅→∅),[9.29464]→[9.1698:1724](∅→∅),[9.1724]→[9.8836:8865](∅→∅),[9.8865]→[9.10325:10375](∅→∅),[9.10375]→[9.29502:29537](∅→∅),[9.14872]→[9.29502:29537](∅→∅),[9.29537]→[9.10376:10426](∅→∅),[9.10426]→[9.29538:29573](∅→∅),[9.15024]→[9.29538:29573](∅→∅),[9.29573]→[9.10427:10478](∅→∅),[9.2118]→[9.15178:15248](∅→∅),[9.10478]→[9.15178:15248](∅→∅),[9.15178]→[9.15178:15248](∅→∅),[9.15248]→[9.2260:2316](∅→∅),[9.2316]→[9.10479:10648](∅→∅),[9.4113]→[9.10479:10648](∅→∅),[9.2253]→[9.8866:8889](∅→∅),[9.10648]→[9.8866:8889](∅→∅),[9.29983]→[9.8866:8889](∅→∅),[9.8889]→[9.10649:10690](∅→∅),[9.10690]→[9.30014:30049](∅→∅),[9.15789]→[9.30014:30049](∅→∅),[9.30049]→[9.10691:10733](∅→∅),[9.2376]→[9.30050:30085](∅→∅),[9.10733]→[9.30050:30085](∅→∅),[9.15934]→[9.30050:30085](∅→∅),[9.30085]→[9.10734:10774](∅→∅),[9.2497]→[9.16075:16080](∅→∅),[9.10774]→[9.16075:16080](∅→∅),[9.16075]→[9.16075:16080](∅→∅),[9.16080]→[9.2:76](∅→∅),[9.155]→[9.8890:8947](∅→∅),[9.16235]→[9.8890:8947](∅→∅),[9.8947]→[9.2681:2727](∅→∅),[9.2727]→[9.30150:30215](∅→∅),[9.9051]→[9.30150:30215](∅→∅),[9.30150]→[9.30150:30215](∅→∅),[9.30215]→[9.2750:2782](∅→∅),[9.2782]→[9.30301:30387](∅→∅),[9.30301]→[9.30301:30387](∅→∅),[9.30422]→[9.1725:1751](∅→∅),[9.1751]→[9.9052:9081](∅→∅),[9.9081]→[9.10775:10825](∅→∅),[9.273]→[9.30460:30495](∅→∅),[9.10825]→[9.30460:30495](∅→∅),[9.16597]→[9.30460:30495](∅→∅),[9.30495]→[9.10826:10876](∅→∅),[9.391]→[9.30496:30531](∅→∅),[9.10876]→[9.30496:30531](∅→∅),[9.16737]→[9.30496:30531](∅→∅),[9.30531]→[9.10877:10928](∅→∅),[9.510]→[9.16879:16955](∅→∅),[9.2617]→[9.16879:16955](∅→∅),[9.10928]→[9.16879:16955](∅→∅),[9.16879]→[9.16879:16955](∅→∅),[9.16955]→[9.2317:2381](∅→∅),[9.2381]→[9.10929:11128](∅→∅),[9.4166]→[9.10929:11128](∅→∅),[9.911]→[9.17356:17458](∅→∅),[9.11128]→[9.17356:17458](∅→∅),[9.30935]→[9.17356:17458](∅→∅),[9.17356]→[9.17356:17458](∅→∅),[9.17458]→[9.2382:2438](∅→∅),[9.2438]→[9.11129:11298](∅→∅),[9.4215]→[9.11129:11298](∅→∅),[9.1282]→[9.9082:9105](∅→∅),[9.2740]→[9.9082:9105](∅→∅),[9.11298]→[9.9082:9105](∅→∅),[9.31309]→[9.9082:9105](∅→∅),[9.9105]→[9.11299:11341](∅→∅),[9.1392]→[9.31340:31375](∅→∅),[9.2851]→[9.31340:31375](∅→∅),[9.11341]→[9.31340:31375](∅→∅),[9.17953]→[9.31340:31375](∅→∅),[9.31375]→[9.11342:11382](∅→∅),[9.1500]→[9.31376:31411](∅→∅),[9.2960]→[9.31376:31411](∅→∅),[9.11382]→[9.31376:31411](∅→∅),[9.18082]→[9.31376:31411](∅→∅),[9.31411]→[9.11383:11424](∅→∅),[9.1609]→[9.18213:18256](∅→∅),[9.11424]→[9.18213:18256](∅→∅),[9.18213]→[9.18213:18256](∅→∅),[9.18299]→[9.18299:18405](∅→∅),[9.18405]→[9.2728:2774](∅→∅),[9.2774]→[9.31412:31474](∅→∅),[9.9210]→[9.31412:31474](∅→∅),[9.18405]→[9.31412:31474](∅→∅),[9.31474]→[9.2783:2815](∅→∅),[9.2815]→[9.31560:31646](∅→∅),[9.31560]→[9.31560:31646](∅→∅),[9.31681]→[9.1752:1778](∅→∅),[9.1778]→[9.9211:9240](∅→∅),[9.9240]→[9.11425:11475](∅→∅),[9.11475]→[9.31719:31754](∅→∅),[9.18672]→[9.31719:31754](∅→∅),[9.31754]→[9.11476:11526](∅→∅),[9.11526]→[9.31755:31790](∅→∅),[9.18775]→[9.31755:31790](∅→∅),[9.31790]→[9.11527:11577](∅→∅),[9.11577]→[9.18878:18940](∅→∅),[9.18878]→[9.18878:18940](∅→∅),[9.18940]→[9.2439:2491](∅→∅),[9.2491]→[9.11578:11688](∅→∅),[9.4262]→[9.11578:11688](∅→∅),[9.11688]→[9.19117:19146](∅→∅),[9.31963]→[9.19117:19146](∅→∅),[9.19117]→[9.19117:19146](∅→∅),[9.19146]→[9.9241:9264](∅→∅),[9.9264]→[9.11689:11730](∅→∅),[9.11730]→[9.31995:32030](∅→∅),[9.19235]→[9.31995:32030](∅→∅),[9.32030]→[9.11731:11772](∅→∅),[9.11772]→[9.32031:32066](∅→∅),[9.19329]→[9.32031:32066](∅→∅),[9.32066]→[9.11773:11814](∅→∅),[9.11814]→[9.19423:19428](∅→∅),[9.19423]→[9.19423:19428](∅→∅),[9.19428]→[9.1104:1630](∅→∅),[9.1665]→[9.1665:2095](∅→∅),[9.2095]→[9.2492:2544](∅→∅),[9.2544]→[9.2141:2197](∅→∅),[9.2141]→[9.2141:2197](∅→∅),[9.2197]→[9.19428:19476](∅→∅),[9.19428]→[9.19428:19476](∅→∅),[9.19529]→[9.19529:19624](∅→∅),[9.19624]→[9.2775:2821](∅→∅),[9.2821]→[9.32067:32129](∅→∅),[9.9369]→[9.32067:32129](∅→∅),[9.19624]→[9.32067:32129](∅→∅),[9.32129]→[9.2816:2848](∅→∅),[9.2848]→[9.32215:32301](∅→∅),[9.32215]→[9.32215:32301](∅→∅),[9.32336]→[9.1779:1805](∅→∅),[9.1805]→[9.9370:9399](∅→∅),[9.9399]→[9.11815:11865](∅→∅),[9.11865]→[9.32374:32409](∅→∅),[9.19901]→[9.32374:32409](∅→∅),[9.32409]→[9.11866:11916](∅→∅),[9.11916]→[9.32410:32445](∅→∅),[9.20014]→[9.32410:32445](∅→∅),[9.32445]→[9.11917:11967](∅→∅),[9.11967]→[9.20127:20193](∅→∅),[9.20127]→[9.20127:20193](∅→∅),[9.20193]→[9.2545:2597](∅→∅),[9.2597]→[9.11968:12078](∅→∅),[9.4309]→[9.11968:12078](∅→∅),[9.12078]→[9.9400:9423](∅→∅),[9.32638]→[9.9400:9423](∅→∅),[9.9423]→[9.12079:12120](∅→∅),[9.12120]→[9.32669:32704](∅→∅),[9.20489]→[9.32669:32704](∅→∅),[9.32704]→[9.12121:12162](∅→∅),[9.12162]→[9.32705:32740](∅→∅),[9.20593]→[9.32705:32740](∅→∅),[9.32740]→[9.12163:12204](∅→∅),[9.12204]→[9.2:423](∅→∅),[9.458]→[9.458:809](∅→∅),[9.809]→[9.2598:2650](∅→∅),[9.2650]→[9.855:965](∅→∅),[9.855]→[9.855:965](∅→∅),[9.965]→[9.20697:20757](∅→∅),[9.12204]→[9.20697:20757](∅→∅),[9.20697]→[9.20697:20757](∅→∅),[9.20817]→[9.20817:20879](∅→∅),[9.20879]→[9.9424:9481](∅→∅),[9.9481]→[9.2822:2868](∅→∅),[9.2868]→[9.32805:32871](∅→∅),[9.9585]→[9.32805:32871](∅→∅),[9.32805]→[9.32805:32871](∅→∅),[9.32871]→[9.2849:2881](∅→∅),[9.2881]→[9.32957:33043](∅→∅),[9.32957]→[9.32957:33043](∅→∅),[9.33078]→[9.1806:1832](∅→∅),[9.1832]→[9.9586:9615](∅→∅),[9.9615]→[9.12205:12255](∅→∅),[9.12255]→[9.33116:33151](∅→∅),[9.21222]→[9.33116:33151](∅→∅),[9.33151]→[9.12256:12306](∅→∅),[9.12306]→[9.21342:21417](∅→∅),[9.21342]→[9.21342:21417](∅→∅),[9.21417]→[9.2651:2703](∅→∅),[9.2703]→[9.9616:9639](∅→∅),[9.4356]→[9.9616:9639](∅→∅),[9.9639]→[9.12307:12349](∅→∅),[9.12349]→[9.33183:33218](∅→∅),[9.21555]→[9.33183:33218](∅→∅),[9.33218]→[9.12350:12391](∅→∅),[9.12391]→[9.33219:33254](∅→∅),[9.21666]→[9.33219:33254](∅→∅),[9.33254]→[9.12392:12669](∅→∅),[9.12669]→[9.22144:22206](∅→∅),[9.33674]→[9.22144:22206](∅→∅),[9.22144]→[9.22144:22206](∅→∅),[9.22268]→[9.22268:22319](∅→∅),[9.22319]→[9.9640:9697](∅→∅),[9.9697]→[9.2869:2915](∅→∅),[9.2915]→[9.33739:33805](∅→∅),[9.9801]→[9.33739:33805](∅→∅),[9.33739]→[9.33739:33805](∅→∅),[9.33805]→[9.2882:2914](∅→∅),[9.2914]→[9.33891:33977](∅→∅),[9.33891]→[9.33891:33977](∅→∅),[9.34012]→[9.1833:1859](∅→∅),[9.1859]→[9.9802:9831](∅→∅),[9.9831]→[9.12670:12720](∅→∅),[9.12720]→[9.34050:34085](∅→∅),[9.22664]→[9.34050:34085](∅→∅),[9.34085]→[9.12721:12771](∅→∅),[9.12771]→[9.34086:34121](∅→∅),[9.22786]→[9.34086:34121](∅→∅),[9.34121]→[9.12772:12822](∅→∅),[9.12822]→[9.22908:23000](∅→∅),[9.22908]→[9.22908:23000](∅→∅),[9.23000]→[9.2704:2756](∅→∅),[9.2756]→[9.9832:9855](∅→∅),[9.4403]→[9.9832:9855](∅→∅),[9.9855]→[9.12823:12864](∅→∅),[9.12864]→[9.34153:34188](∅→∅),[9.23139]→[9.34153:34188](∅→∅),[9.34188]→[9.12865:12906](∅→∅),[9.12906]→[9.34189:34224](∅→∅),[9.23252]→[9.34189:34224](∅→∅),[9.34224]→[9.12907:13184](∅→∅),[9.13184]→[9.23740:23795](∅→∅),[9.34652]→[9.23740:23795](∅→∅),[9.23740]→[9.23740:23795](∅→∅),[9.23850]→[9.23850:23975](∅→∅),[9.23975]→[9.2916:2962](∅→∅),[9.2962]→[9.34653:34719](∅→∅),[9.9960]→[9.34653:34719](∅→∅),[9.23975]→[9.34653:34719](∅→∅),[9.34719]→[9.2915:2947](∅→∅),[9.2947]→[9.34805:34891](∅→∅),[9.34805]→[9.34805:34891](∅→∅),[9.34926]→[9.1860:1886](∅→∅),[9.1886]→[9.9961:9990](∅→∅),[9.9990]→[9.13185:13235](∅→∅),[9.13235]→[9.34964:34999](∅→∅),[9.24258]→[9.34964:34999](∅→∅),[9.34999]→[9.13236:13286](∅→∅),[9.13286]→[9.35000:35035](∅→∅),[9.24373]→[9.35000:35035](∅→∅),[9.35035]→[9.13287:13337](∅→∅),[9.13337]→[9.24488:24554](∅→∅),[9.24488]→[9.24488:24554](∅→∅),[9.24554]→[9.2757:2809](∅→∅),[9.2809]→[9.13338:13448](∅→∅),[9.4450]→[9.13338:13448](∅→∅),[9.13448]→[9.9991:10014](∅→∅),[9.35232]→[9.9991:10014](∅→∅),[9.10014]→[9.24772:24794](∅→∅),[9.35262]→[9.24772:24794](∅→∅),[9.24772]→[9.24772:24794](∅→∅),[9.24794]→[9.35263:35298](∅→∅),[9.35298]→[9.13449:13490](∅→∅),[9.13490]→[9.35299:35334](∅→∅),[9.24900]→[9.35299:35334](∅→∅),[9.35334]→[9.13491:13532](∅→∅),[9.13532]→[9.25006:25034](∅→∅),[9.25006]→[9.25006:25034](∅→∅),[9.25062]→[9.25062:25102](∅→∅),[9.25102]→[9.2963:3009](∅→∅),[9.3009]→[9.35335:35390](∅→∅),[9.10119]→[9.35335:35390](∅→∅),[9.25102]→[9.35335:35390](∅→∅),[9.35390]→[9.2948:2980](∅→∅),[9.2980]→[9.35476:35562](∅→∅),[9.35476]→[9.35476:35562](∅→∅),[9.35597]→[9.25258:25306](∅→∅),[9.25258]→[9.25258:25306](∅→∅),[9.25306]→[9.1887:1913](∅→∅),[9.1913]→[9.10120:10149](∅→∅),[9.10149]→[9.13533:13583](∅→∅),[9.13583]→[9.35635:35670](∅→∅),[9.25408]→[9.35635:35670](∅→∅),[9.35670]→[9.13584:13634](∅→∅),[9.13634]→[9.25496:25544](∅→∅),[9.25496]→[9.25496:25544](∅→∅),[9.25544]→[9.2810:2870](∅→∅),[9.2870]→[9.13635:13745](∅→∅),[9.4501]→[9.13635:13745](∅→∅),[9.13745]→[9.10150:10173](∅→∅),[9.35813]→[9.10150:10173](∅→∅),[9.10173]→[9.13746:13787](∅→∅),[9.13787]→[9.35844:35879](∅→∅),[9.25769]→[9.35844:35879](∅→∅),[9.35879]→[9.13788:13829](∅→∅),[9.13829]→[9.25848:25902](∅→∅),[9.25848]→[9.25848:25902](∅→∅),[9.25956]→[9.25956:26026](∅→∅),[9.26026]→[9.10174:10231](∅→∅),[9.10231]→[9.3010:3056](∅→∅),[9.3056]→[9.35944:36010](∅→∅),[9.10335]→[9.35944:36010](∅→∅),[9.35944]→[9.35944:36010](∅→∅),[9.36010]→[9.2981:3013](∅→∅),[9.3013]→[9.36096:36182](∅→∅),[9.36096]→[9.36096:36182](∅→∅),[9.36217]→[9.1914:1940](∅→∅),[9.1940]→[9.10336:10365](∅→∅),[9.10365]→[9.13830:13880](∅→∅),[9.13880]→[9.36255:36290](∅→∅),[9.26363]→[9.36255:36290](∅→∅),[9.36290]→[9.13881:13931](∅→∅),[9.13931]→[9.36291:36326](∅→∅),[9.26477]→[9.36291:36326](∅→∅),[9.36326]→[9.13932:14029](∅→∅),[9.14029]→[9.26638:26702](∅→∅),[9.26638]→[9.26638:26702](∅→∅),[9.26702]→[9.2871:2931](∅→∅),[9.2931]→[9.14030:14199](∅→∅),[9.4552]→[9.14030:14199](∅→∅),[9.14199]→[9.10366:10389](∅→∅),[9.36622]→[9.10366:10389](∅→∅),[9.10389]→[9.14200:14242](∅→∅),[9.14242]→[9.36653:36688](∅→∅),[9.27094]→[9.36653:36688](∅→∅),[9.36688]→[9.14243:14284](∅→∅),[9.14284]→[9.36689:36724](∅→∅),[9.27199]→[9.36689:36724](∅→∅),[9.36724]→[9.14285:14326](∅→∅),[9.14326]→[9.27304:27367](∅→∅),[9.27304]→[9.27304:27367](∅→∅),[9.27430]→[9.36725:36816](∅→∅),[9.36816]→[9.10390:10447](∅→∅),[9.10447]→[9.3057:3103](∅→∅),[9.3103]→[9.36880:36943](∅→∅),[9.10551]→[9.36880:36943](∅→∅),[9.36880]→[9.36880:36943](∅→∅),[9.36943]→[9.3014:3046](∅→∅),[9.3046]→[9.37029:37115](∅→∅),[9.37029]→[9.37029:37115](∅→∅),[9.37150]→[9.1941:1967](∅→∅),[9.1967]→[9.10552:10581](∅→∅),[9.10581]→[9.14327:14377](∅→∅),[9.14377]→[9.37188:37223](∅→∅),[9.27851]→[9.37188:37223](∅→∅),[9.37223]→[9.14378:14475](∅→∅),[9.14475]→[9.28021:28085](∅→∅),[9.28021]→[9.28021:28085](∅→∅),[9.28085]→[9.2932:2992](∅→∅),[9.2992]→[9.14476:14645](∅→∅),[9.4603]→[9.14476:14645](∅→∅),[9.14645]→[9.10582:10605](∅→∅),[9.37546]→[9.10582:10605](∅→∅),[9.10605]→[9.14646:14688](∅→∅),[9.14688]→[9.37577:37612](∅→∅),[9.28513]→[9.37577:37612](∅→∅),[9.37612]→[9.14689:14730](∅→∅),[9.14730]→[9.37613:37648](∅→∅),[9.28627]→[9.37613:37648](∅→∅),[9.37648]→[9.14731:14773](∅→∅),[9.14773]→[9.28742:28797](∅→∅),[9.28742]→[9.28742:28797](∅→∅),[9.28852]→[9.28852:28904](∅→∅),[9.28904]→[9.10606:10663](∅→∅),[9.10663]→[9.3104:3150](∅→∅),[9.3150]→[9.37713:37775](∅→∅),[9.10767]→[9.37713:37775](∅→∅),[9.37713]→[9.37713:37775](∅→∅),[9.37775]→[9.3047:3079](∅→∅),[9.3079]→[9.37861:37947](∅→∅),[9.37861]→[9.37861:37947](∅→∅),[9.37982]→[9.1968:1994](∅→∅),[9.1994]→[9.10768:10797](∅→∅),[9.10797]→[9.14774:14824](∅→∅),[9.14824]→[9.38020:38055](∅→∅),[9.29238]→[9.38020:38055](∅→∅),[9.38055]→[9.14825:14875](∅→∅),[9.14875]→[9.38056:38091](∅→∅),[9.29353]→[9.38056:38091](∅→∅),[9.38091]→[9.14876:14926](∅→∅),[9.14926]→[9.29468:29525](∅→∅),[9.29468]→[9.29468:29525](∅→∅),[9.29525]→[9.2993:3053](∅→∅),[9.3053]→[9.14927:15096](∅→∅),[9.4654]→[9.14927:15096](∅→∅),[9.15096]→[9.10798:10821](∅→∅),[9.38390]→[9.10798:10821](∅→∅),[9.10821]→[9.15097:15138](∅→∅),[9.15138]→[9.38421:38456](∅→∅),[9.29920]→[9.38421:38456](∅→∅),[9.38456]→[9.15139:15178](∅→∅),[9.15178]→[9.38457:38492](∅→∅),[9.30024]→[9.38457:38492](∅→∅),[9.38492]→[9.15179:15219](∅→∅),[9.15219]→[9.30129:30211](∅→∅),[9.30129]→[9.30129:30211](∅→∅),[9.30293]→[9.30293:30337](∅→∅),[9.30337]→[9.10822:10879](∅→∅),[9.10879]→[9.3151:3197](∅→∅),[9.3197]→[9.38557:38619](∅→∅),[9.10983]→[9.38557:38619](∅→∅),[9.38557]→[9.38557:38619](∅→∅),[9.38619]→[9.3080:3112](∅→∅),[9.3112]→[9.38705:38791](∅→∅),[9.38705]→[9.38705:38791](∅→∅),[9.38826]→[9.1995:2021](∅→∅),[9.2021]→[9.10984:11013](∅→∅),[9.11013]→[9.15220:15270](∅→∅),[9.15270]→[9.30698:30763](∅→∅),[9.30698]→[9.30698:30763](∅→∅),[9.30763]→[9.3054:3114](∅→∅),[9.3114]→[9.15271:15440](∅→∅),[9.4705]→[9.15271:15440](∅→∅),[9.15440]→[9.11014:11037](∅→∅),[9.39243]→[9.11014:11037](∅→∅),[9.11037]→[9.15441:15480](∅→∅),[9.15480]→[9.39274:39309](∅→∅),[9.31264]→[9.39274:39309](∅→∅),[9.39309]→[9.15481:15521](∅→∅),[9.15521]→[9.31396:31401](∅→∅),[9.31396]→[9.31396:31401](∅→∅),[9.31401]→[9.102:188](∅→∅),[9.279]→[9.279:328](∅→∅),[9.328]→[9.11038:11095](∅→∅),[9.11095]→[9.3198:3244](∅→∅),[9.3244]→[9.39374:39419](∅→∅),[9.11199]→[9.39374:39419](∅→∅),[9.39374]→[9.39374:39419](∅→∅),[9.39419]→[9.3113:3145](∅→∅),[9.3145]→[9.39505:39591](∅→∅),[9.39505]→[9.39505:39591](∅→∅),[9.39626]→[9.2022:2048](∅→∅),[9.1163]→[9.529:603](∅→∅),[9.2048]→[9.529:603](∅→∅),[9.529]→[9.529:603](∅→∅),[9.603]→[9.364:411](∅→∅),[9.411]→[9.15522:15691](∅→∅),[9.15691]→[9.11200:11229](∅→∅),[9.40033]→[9.11200:11229](∅→∅),[9.11229]→[9.15692:15731](∅→∅),[9.15731]→[9.1142:1147](∅→∅),[9.1142]→[9.1142:1147](∅→∅),[9.1147]→[9.16:67](∅→∅),[9.31401]→[9.16:67](∅→∅),[9.123]→[9.123:175](∅→∅),[9.175]→[9.11230:11287](∅→∅),[9.11287]→[9.3245:3291](∅→∅),[9.3291]→[9.40134:40196](∅→∅),[9.11391]→[9.40134:40196](∅→∅),[9.40134]→[9.40134:40196](∅→∅),[9.40196]→[9.3146:3178](∅→∅),[9.3178]→[9.40282:40368](∅→∅),[9.40282]→[9.40282:40368](∅→∅),[9.40403]→[9.2049:2075](∅→∅),[9.2075]→[9.11392:11421](∅→∅),[9.11421]→[9.15732:15782](∅→∅),[9.15782]→[9.40441:40476](∅→∅),[9.510]→[9.40441:40476](∅→∅),[9.40476]→[9.15783:15833](∅→∅),[9.15833]→[9.40477:40512](∅→∅),[9.626]→[9.40477:40512](∅→∅),[9.40512]→[9.15834:15884](∅→∅),[9.15884]→[9.742:813](∅→∅),[9.742]→[9.742:813](∅→∅),[9.813]→[9.412:553](∅→∅),[9.553]→[9.15885:16054](∅→∅),[9.16054]→[9.11422:11445](∅→∅),[9.40814]→[9.11422:11445](∅→∅),[9.11445]→[9.16055:16096](∅→∅),[9.16096]→[9.40845:40880](∅→∅),[9.1270]→[9.40845:40880](∅→∅),[9.40880]→[9.16097:16139](∅→∅),[9.3047]→[9.40881:40916](∅→∅),[9.16139]→[9.40881:40916](∅→∅),[9.1379]→[9.40881:40916](∅→∅),[9.40916]→[9.16140:16180](∅→∅),[9.3132]→[9.2:6](∅→∅),[9.16180]→[9.2:6](∅→∅),[9.1484]→[9.2:6](∅→∅)
-- Arguably this should be called edit_tests.lua,-- but that would mess up the git blame at this point.function test_initial_state()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{}Text.redraw_all(Editor_state)edit.draw(Editor_state)check_eq(#Editor_state.lines, 1, '#lines')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')endfunction test_click_to_create_drawing()App.screen.init{width=800, height=600}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{}Text.redraw_all(Editor_state)edit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, 8,Editor_state.top+8, 1)-- cursor skips drawing to always remain on textcheck_eq(#Editor_state.lines, 2, '#lines')check_eq(Editor_state.cursor1.line, 2, 'cursor')endfunction test_backspace_to_delete_drawing()-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'```lines', '```', ''}Text.redraw_all(Editor_state)-- cursor is on text as always (outside tests this will get initialized correctly)Editor_state.cursor1.line = 2-- backspacing deletes the drawingedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(#Editor_state.lines, 1, '#lines')check_eq(Editor_state.cursor1.line, 1, 'cursor')endfunction test_backspace_from_start_of_final_line()-- display final line of text with cursor at start of itApp.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Editor_state.screen_top1 = {line=2, pos=1}Editor_state.cursor1 = {line=2, pos=1}Text.redraw_all(Editor_state)-- backspace scrolls upedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(#Editor_state.lines, 1, '#lines')check_eq(Editor_state.cursor1.line, 1, 'cursor')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')endfunction test_insert_first_character()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{}Text.redraw_all(Editor_state)edit.draw(Editor_state)edit.run_after_text_input(Editor_state, 'a')local y = Editor_state.topApp.screen.check(y, 'a', 'screen:1')endfunction test_press_ctrl()-- press ctrl while the cursor is on textApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{''}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.run_after_keychord(Editor_state, 'C-m', 'm')endfunction test_move_left()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'a'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_move_right()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'a'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'right', 'right')check_eq(Editor_state.cursor1.pos, 2, 'check')endfunction test_move_left_to_previous_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'left', 'left')check_eq(Editor_state.cursor1.line, 1, 'line')check_eq(Editor_state.cursor1.pos, 4, 'pos') -- past end of lineendfunction test_move_right_to_next_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4} -- past end of lineedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'right', 'right')check_eq(Editor_state.cursor1.line, 2, 'line')check_eq(Editor_state.cursor1.pos, 1, 'pos')endfunction test_move_to_start_of_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=3}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_move_to_start_of_previous_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4} -- at the space between wordsedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_skip_to_previous_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=5} -- at the start of second wordedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_skip_past_tab_to_previous_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def\tghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=10} -- within third wordedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 9, 'check')endfunction test_skip_multiple_spaces_to_previous_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=6} -- at the start of second wordedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.pos, 1, 'check')endfunction test_move_to_start_of_word_on_previous_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-left', 'left')check_eq(Editor_state.cursor1.line, 1, 'line')check_eq(Editor_state.cursor1.pos, 5, 'pos')endfunction test_move_past_end_of_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.pos, 4, 'check')endfunction test_skip_to_next_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4} -- at the space between wordsedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.pos, 8, 'check')endfunction test_skip_past_tab_to_next_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc\tdef'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1} -- at the space between wordsedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.pos, 4, 'check')endfunction test_skip_multiple_spaces_to_next_word()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4} -- at the start of second wordedit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.pos, 9, 'check')endfunction test_move_past_end_of_word_on_next_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=8}edit.draw(Editor_state)edit.run_after_keychord(Editor_state, 'M-right', 'right')check_eq(Editor_state.cursor1.line, 2, 'line')check_eq(Editor_state.cursor1.pos, 4, 'pos')endfunction test_click_moves_cursor()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each lineedit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')-- selection is empty to avoid perturbing future editscheck_nil(Editor_state.selection1.line, 'selection:line')check_nil(Editor_state.selection1.pos, 'selection:pos')endfunction test_click_to_left_of_line()-- display a line with the cursor in the middleApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=3}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}-- click to the left of the lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left-4,Editor_state.top+5, 1)-- cursor moves to start of linecheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_click_takes_margins_into_account()-- display two lines with cursor on one of themApp.screen.init{width=100, height=80}Editor_state = edit.initialize_test_state()Editor_state.left = 50 -- occupy only right side of screenEditor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}-- click on the other lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- cursor movescheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_click_on_empty_line()-- display two lines with the first one emptyApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}-- click on the empty lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- cursor movescheck_eq(Editor_state.cursor1.line, 1, 'cursor')-- selection remains emptycheck_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_click_below_final_line_of_file()-- display one lineApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}-- click below first lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+50, 1)-- cursor goes to bottomcheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')-- selection remains emptycheck_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_draw_text()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_draw_wrapping_text()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'defgh', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'de', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'fgh', 'screen:3')endfunction test_draw_word_wrapping_text()App.screen.init{width=60, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_click_on_wrapping_line()-- display two screen lines with cursor on one of themApp.screen.init{width=50, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=20}Editor_state.screen_top1 = {line=1, pos=1}-- click on the other lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- cursor movescheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_click_on_wrapping_line_takes_margins_into_account()-- display two screen lines with cursor on one of themApp.screen.init{width=100, height=80}Editor_state = edit.initialize_test_state()Editor_state.left = 50 -- occupy only right side of screenEditor_state.lines = load_array{'abc def ghi jkl mno pqr stu'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=20}Editor_state.screen_top1 = {line=1, pos=1}-- click on the other lineedit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- cursor movescheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')endfunction test_draw_text_wrapping_within_word()-- arrange a screen line that needs to be split within a wordApp.screen.init{width=60, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abcd e fghijk', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abcd ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'e fgh', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ijk', 'screen:3')endfunction test_draw_wrapping_text_containing_non_ascii()-- draw a long line containing non-ASCIIApp.screen.init{width=60, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'madam I’m adam', 'xyz'} -- notice the non-ASCII apostropheText.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'mad', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'am I', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, '’m a', 'screen:3')endfunction test_click_past_end_of_screen_line()-- display a wrapping lineApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()-- 12345678901234Editor_state.lines = load_array{"madam I'm adam"}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'madam ', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, "I'm ad", 'baseline/screen:2')y = y + Editor_state.line_height-- click past end of second screen lineedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of screen line (one more than final character shown)check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 13, 'cursor:pos')endfunction test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen()-- display a wrapping line from its second screen lineApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()-- 12345678901234Editor_state.lines = load_array{"madam I'm adam"}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=8}Editor_state.screen_top1 = {line=1, pos=7}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, "I'm ad", 'baseline/screen:2')y = y + Editor_state.line_height-- click past end of second screen lineedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of screen line (one more than final character shown)check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 13, 'cursor:pos')endfunction test_click_past_end_of_wrapping_line()-- display a wrapping lineApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()-- 12345678901234Editor_state.lines = load_array{"madam I'm adam"}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'madam ', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, "I'm ad", 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'am', 'baseline/screen:3')y = y + Editor_state.line_height-- click past the end of itedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of linecheck_eq(Editor_state.cursor1.pos, 15, 'cursor') -- one more than the number of UTF-8 code-pointsendfunction test_click_past_end_of_wrapping_line_containing_non_ascii()-- display a wrapping line containing non-ASCIIApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()-- 12345678901234Editor_state.lines = load_array{'madam I’m adam'} -- notice the non-ASCII apostropheText.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'madam ', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'I’m ad', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'am', 'baseline/screen:3')y = y + Editor_state.line_height-- click past the end of itedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of linecheck_eq(Editor_state.cursor1.pos, 15, 'cursor') -- one more than the number of UTF-8 code-pointsendfunction test_click_past_end_of_word_wrapping_line()-- display a long line wrapping at a word boundary on a screen of more realistic lengthApp.screen.init{width=160, height=80}Editor_state = edit.initialize_test_state()-- 0 1 2-- 123456789012345678901Editor_state.lines = load_array{'the quick brown fox jumped over the lazy dog'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'the quick brown fox ', 'baseline/screen:1')y = y + Editor_state.line_height-- click past the end of the screen lineedit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)-- cursor moves to end of screen line (one more than final character shown)check_eq(Editor_state.cursor1.pos, 21, 'cursor')endfunction test_select_text()-- display a line of textApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- select a letterApp.fake_key_press('lshift')edit.run_after_keychord(Editor_state, 'S-right', 'right')App.fake_key_release('lshift')edit.key_release(Editor_state, 'lshift')-- selection persists even after shift is releasedcheck_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 1, 'selection:pos')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')endfunction test_cursor_movement_without_shift_resets_selection()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- press an arrow key without shiftedit.run_after_keychord(Editor_state, 'right', 'right')-- no change to data, selection is resetcheck_nil(Editor_state.selection1.line, 'check')check_eq(Editor_state.lines[1].data, 'abc', 'data')endfunction test_edit_deletes_selection()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- press a keyedit.run_after_text_input(Editor_state, 'x')-- selected text is deleted and replaced with the keycheck_eq(Editor_state.lines[1].data, 'xbc', 'check')endfunction test_edit_with_shift_key_deletes_selection()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- mimic precise keypresses for a capital letterApp.fake_key_press('lshift')edit.keychord_press(Editor_state, 'd', 'd')edit.text_input(Editor_state, 'D')edit.key_release(Editor_state, 'd')App.fake_key_release('lshift')-- selected text is deleted and replaced with the keycheck_nil(Editor_state.selection1.line, 'check')check_eq(Editor_state.lines[1].data, 'Dbc', 'data')endfunction test_copy_does_not_reset_selection()-- display a line of text with a selectionApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- copy selectionedit.run_after_keychord(Editor_state, 'C-c', 'c')check_eq(App.clipboard, 'a', 'clipboard')-- selection is reset since shift key is not pressedcheck(Editor_state.selection1.line, 'check')endfunction test_cut()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- press a keyedit.run_after_keychord(Editor_state, 'C-x', 'x')check_eq(App.clipboard, 'a', 'clipboard')-- selected text is deletedcheck_eq(Editor_state.lines[1].data, 'bc', 'data')endfunction test_paste_replaces_selection()-- display a line of text with a selectionApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.selection1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- set clipboardApp.clipboard = 'xyz'-- paste selectionedit.run_after_keychord(Editor_state, 'C-v', 'v')-- selection is reset since shift key is not pressed-- selection includes the newline, so it's also deletedcheck_eq(Editor_state.lines[1].data, 'xyzdef', 'check')endfunction test_deleting_selection_may_scroll()-- display lines 2/3/4App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=2}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:3')-- set up a selection starting above the currently displayed pageEditor_state.selection1 = {line=1, pos=2}-- delete selectionedit.run_after_keychord(Editor_state, 'backspace', 'backspace')-- page scrolls upcheck_eq(Editor_state.screen_top1.line, 1, 'check')check_eq(Editor_state.lines[1].data, 'ahi', 'data')endfunction test_edit_wrapping_text()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=4}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)edit.run_after_text_input(Editor_state, 'g')local y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'de', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'fg', 'screen:3')endfunction test_insert_newline()-- display a few linesApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- hitting the enter key splits the lineedit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'a', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'bc', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:3')endfunction test_insert_newline_at_start_of_line()-- display a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}-- hitting the enter key splits the lineedit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')check_eq(Editor_state.lines[1].data, '', 'data:1')check_eq(Editor_state.lines[2].data, 'abc', 'data:2')endfunction test_insert_from_clipboard()-- display a few linesApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- paste some text including a newline, check that new line is createdApp.clipboard = 'xy\nz'edit.run_after_keychord(Editor_state, 'C-v', 'v')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'axy', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'zbc', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:3')endfunction test_select_text_using_mouse()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each line-- press and hold on first locationedit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- drag and release somewhere elseedit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 2, 'selection:pos')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')endfunction test_select_text_using_mouse_starting_above_text()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each line-- press mouse above first line of textedit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 1, 'selection:pos')endfunction test_select_text_using_mouse_starting_above_text_wrapping_line()-- first screen line starts in the middle of a lineApp.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'defgh', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=5}Editor_state.screen_top1 = {line=2, pos=3}-- press mouse above first line of textedit.draw(Editor_state)edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)-- selection is at screen topcheck(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')check_eq(Editor_state.selection1.line, 2, 'selection:line')check_eq(Editor_state.selection1.pos, 3, 'selection:pos')endfunction test_select_text_using_mouse_starting_below_text()-- I'd like to test what happens when a mouse click is below some page of-- text, potentially even in the middle of a line.-- However, it's brittle to set up a text line boundary just right.-- So I'm going to just check things below the bottom of the final line of-- text when it's in the middle of the screen.-- final screen line ends in the middle of screenApp.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abcde'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'ab', 'baseline:screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'cde', 'baseline:screen:2')-- press mouse above first line of textedit.run_after_mouse_press(Editor_state, 5,App.screen.height-5, 1)-- selection is past bottom-most text in screencheck(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 6, 'selection:pos')endfunction test_select_text_using_mouse_and_shift()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each line-- click on first locationedit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- hold down shift and click somewhere elseApp.fake_key_press('lshift')edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)App.fake_key_release('lshift')check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 2, 'selection:pos')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')endfunction test_select_text_repeatedly_using_mouse_and_shift()App.screen.init{width=50, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state) -- populate line_cache.startpos for each line-- click on first locationedit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)-- hold down shift and click on a second locationApp.fake_key_press('lshift')edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)-- hold down shift and click at a third locationApp.fake_key_press('lshift')edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+Editor_state.line_height+5, 1)App.fake_key_release('lshift')-- selection is between first and third location. forget the second location, not the first.check_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 2, 'selection:pos')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')endfunction test_select_all_text()-- display a single line of textApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- select allApp.fake_key_press('lctrl')edit.run_after_keychord(Editor_state, 'C-a', 'a')App.fake_key_release('lctrl')edit.key_release(Editor_state, 'lctrl')-- selectioncheck_eq(Editor_state.selection1.line, 1, 'selection:line')check_eq(Editor_state.selection1.pos, 1, 'selection:pos')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 8, 'cursor:pos')endfunction test_cut_without_selection()-- display a few linesApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.selection1 = {}edit.draw(Editor_state)-- try to cut without selecting textedit.run_after_keychord(Editor_state, 'C-x', 'x')-- no crashcheck_nil(Editor_state.selection1.line, 'check')endfunction test_pagedown()App.screen.init{width=120, height=45}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}-- initially the first two lines are displayededit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')-- after pagedown the bottom line becomes the topedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:2')endfunction test_pagedown_skips_drawings()-- some lines of text with a drawing intermixedlocal drawing_width = 50App.screen.init{width=Editor_state.left+drawing_width, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', -- height 15'```lines', '```', -- height 25'def', -- height 15'ghi'} -- height 15Text.redraw_all(Editor_state)check_eq(Editor_state.lines[2].mode, 'drawing', 'baseline/lines')Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}local drawing_height = Drawing_padding_height + drawing_width/2 -- default-- initially the screen displays the first line and the drawing-- 15px margin + 15px line1 + 10px margin + 25px drawing + 10px margin = 75px < screen height 80pxedit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')-- after pagedown the screen draws the drawing up top-- 15px margin + 10px margin + 25px drawing + 10px margin + 15px line3 = 75px < screen height 80pxedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor')y = Editor_state.top + drawing_heightApp.screen.check(y, 'def', 'screen:1')endfunction test_pagedown_can_start_from_middle_of_long_wrapping_line()-- draw a few lines starting from a very long wrapping lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu vwx yza bcd efg hij', 'XYZ'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc ', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def ', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'baseline/screen:3')-- after pagedown we scroll down the very long wrapping lineedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 9, 'screen_top:pos')y = Editor_state.topApp.screen.check(y, 'ghi ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl ', 'screen:2')y = y + Editor_state.line_heightif Version == '12.0' then-- HACK: Maybe v12.0 uses a different font? Strange that it only causes-- issues in a couple of places.-- We'll need to rethink our tests if issues like this start to multiply.App.screen.check(y, 'mno ', 'screen:3')elseApp.screen.check(y, 'mn', 'screen:3')endendfunction test_pagedown_never_moves_up()-- draw the final screen line of a wrapping lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=9}Editor_state.screen_top1 = {line=1, pos=9}edit.draw(Editor_state)-- pagedown makes no changeedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 9, 'screen_top:pos')endfunction test_down_arrow_moves_cursor()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}-- initially the first three lines are displayededit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the down arrow, the cursor moves down by 1 lineedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor')-- the screen is unchangedy = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_down_arrow_skips_drawing()-- some lines of text with a drawing intermixedlocal drawing_width = 50App.screen.init{width=Editor_state.left+drawing_width, height=100}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', -- height 15'```lines', '```', -- height 25'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightlocal drawing_height = Drawing_padding_height + drawing_width/2 -- defaulty = y + drawing_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')check(Editor_state.cursor_x, 'baseline/cursor_x')-- after hitting the down arrow the cursor moves down by 2 lines, skipping the drawingedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.cursor1.line, 3, 'cursor')endfunction test_down_arrow_scrolls_down_by_one_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the down arrow the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 4, 'cursor')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_down_arrow_scrolls_down_by_one_screen_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the down arrow the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghij', 'baseline/screen:3')-- after hitting the down arrow the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghij', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:3')endfunction test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up()App.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghij', 'baseline/screen:3')-- after hitting pagedown the screen scrolls down to start of a long lineedit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')check_eq(Editor_state.screen_top1.line, 3, 'baseline2/screen_top')check_eq(Editor_state.cursor1.line, 3, 'baseline2/cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'baseline2/cursor:pos')-- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll upedit.run_after_keychord(Editor_state, 'down', 'down')check_eq(Editor_state.screen_top1.line, 3, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'ghij', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')endfunction test_up_arrow_moves_cursor()-- display the first 3 lines with the cursor on the bottom lineApp.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the up arrow the cursor moves up by 1 lineedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor')-- the screen is unchangedy = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_up_arrow_skips_drawing()-- some lines of text with a drawing intermixedlocal drawing_width = 50App.screen.init{width=Editor_state.left+drawing_width, height=100}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', -- height 15'```lines', '```', -- height 25'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightlocal drawing_height = Drawing_padding_height + drawing_width/2 -- defaulty = y + drawing_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')check(Editor_state.cursor_x, 'baseline/cursor_x')-- after hitting the up arrow the cursor moves up by 2 lines, skipping the drawingedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.cursor1.line, 1, 'cursor')endfunction test_up_arrow_scrolls_up_by_one_line()-- display the lines 2/3/4 with the cursor on line 2App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:3')-- after hitting the up arrow the screen scrolls up by one lineedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_up_arrow_scrolls_up_by_one_line_skipping_drawing()-- display lines 3/4/5 with a drawing just off screen at line 2App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', '```lines', '```', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=3, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:3')-- after hitting the up arrow the screen scrolls up to previous text lineedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')endfunction test_up_arrow_scrolls_up_by_one_screen_line()-- display lines starting from second screen line of a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=6}Editor_state.screen_top1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:2')-- after hitting the up arrow the screen scrolls up to first screen lineedit.run_after_keychord(Editor_state, 'up', 'up')y = Editor_state.topApp.screen.check(y, 'ghi ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')endfunction test_up_arrow_scrolls_up_to_final_screen_line()-- display lines starting just after a long lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'ghi', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:3')-- after hitting the up arrow the screen scrolls up to final screen line of previous lineedit.run_after_keychord(Editor_state, 'up', 'up')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 5, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')endfunction test_up_arrow_scrolls_up_to_empty_line()-- display a screenful of text with an empty line just above it outside the screenApp.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'', 'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the up arrow the screen scrolls up by one lineedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')y = Editor_state.top-- empty first liney = y + Editor_state.line_heightApp.screen.check(y, 'abc', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:3')endfunction test_pageup()App.screen.init{width=120, height=45}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}-- initially the last two lines are displayededit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')-- after pageup the cursor goes to first lineedit.run_after_keychord(Editor_state, 'pageup', 'pageup')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')endfunction test_pageup_scrolls_up_by_screen_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'ghi', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the page-up key the screen scrolls up to topedit.run_after_keychord(Editor_state, 'pageup', 'pageup')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'abc ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:3')endfunction test_pageup_scrolls_up_from_middle_screen_line()-- display a few lines starting from the middle of a line (Editor_state.cursor1.pos > 1)App.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=5}Editor_state.screen_top1 = {line=2, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the page-up key the screen scrolls up to topedit.run_after_keychord(Editor_state, 'pageup', 'pageup')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'abc ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'screen:3')endfunction test_enter_on_bottom_line_scrolls_down()-- display a few lines with cursor on bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after hitting the enter key the screen scrolls downedit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 4, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'g', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'hi', 'screen:3')endfunction test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()-- display just the bottom line on screenApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=4, pos=2}Editor_state.screen_top1 = {line=4, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')-- after hitting the enter key the screen does not scroll downedit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.screen_top1.line, 4, 'screen_top')check_eq(Editor_state.cursor1.line, 5, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'j', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:2')endfunction test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom()-- display just an empty bottom line on screenApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', ''}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)-- after hitting the inserting_text key the screen does not scroll downedit.run_after_text_input(Editor_state, 'a')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')local y = Editor_state.topApp.screen.check(y, 'a', 'screen:1')endfunction test_typing_on_bottom_line_scrolls_down()-- display a few lines with cursor on bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=4}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:3')-- after typing something the line wraps and the screen scrolls downedit.run_after_text_input(Editor_state, 'j')edit.run_after_text_input(Editor_state, 'k')edit.run_after_text_input(Editor_state, 'l')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 7, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghij', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:3')end---- I'm checking the precise state of the screen in this file, an inherently-- brittle approach that depends on details of the font and text shaping-- algorithms used by a particular release of LÖVE.---- (This brittleness is one reason lines2 and its forks have no tests.)---- To manage the brittleness, there'll be one version of this file for each-- distinct LÖVE version that introduces font changes. - replacement in text_tests.lua at line 12[9.7]→[9.7:61](∅→∅),[9.120]→[9.120:182](∅→∅),[9.182]→[9.11446:11503](∅→∅),[9.11503]→[9.3292:3338](∅→∅),[9.3338]→[9.40981:41047](∅→∅),[9.11607]→[9.40981:41047](∅→∅),[9.40981]→[9.40981:41047](∅→∅),[9.41047]→[9.3179:3211](∅→∅),[9.3211]→[9.41133:41178](∅→∅),[9.41133]→[9.41133:41178](∅→∅),[9.41213]→[9.372:404](∅→∅),[9.372]→[9.372:404](∅→∅),[9.404]→[9.41214:41255](∅→∅),[9.41255]→[9.2076:2102](∅→∅),[9.2102]→[9.11608:11637](∅→∅),[9.11637]→[9.16181:16231](∅→∅),[9.16231]→[9.41293:41328](∅→∅),[9.565]→[9.41293:41328](∅→∅),[9.41328]→[9.16232:16282](∅→∅),[9.16282]→[9.684:761](∅→∅),[9.684]→[9.684:761](∅→∅),[9.761]→[9.3115:3171](∅→∅),[9.3171]→[9.11638:11661](∅→∅),[9.4940]→[9.11638:11661](∅→∅),[9.11661]→[9.16283:16325](∅→∅),[9.16325]→[9.41360:41395](∅→∅),[9.900]→[9.41360:41395](∅→∅),[9.41395]→[9.16326:16367](∅→∅),[9.16367]→[9.41396:41431](∅→∅),[9.1010]→[9.41396:41431](∅→∅),[9.41431]→[9.16368:16645](∅→∅)
function test_left_arrow_scrolls_up_in_wrapped_line()-- display lines starting from second screen line of a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.screen_top1 = {line=3, pos=5}-- cursor is at top of screenEditor_state.cursor1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:2')-- after hitting the left arrow the screen scrolls up to first screen lineedit.run_after_keychord(Editor_state, 'left', 'left')y = Editor_state.topApp.screen.check(y, 'ghi ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')Version, Major_version = App.love_version()if Major_version == 11 thenload_file_from_source_or_save_directory('text_tests_love11.lua')elseif Major_version == 12 then-- not released/stable yetload_file_from_source_or_save_directory('text_tests_love12.lua') - edit in text_tests.lua at line 19[9.1487]→[9.1487:1545](∅→∅),[9.1607]→[9.1607:1677](∅→∅),[9.1677]→[9.11662:11719](∅→∅),[9.11719]→[9.3339:3385](∅→∅),[9.3385]→[9.41912:41978](∅→∅),[9.11823]→[9.41912:41978](∅→∅),[9.41912]→[9.41912:41978](∅→∅),[9.41978]→[9.3212:3244](∅→∅),[9.3244]→[9.42064:42109](∅→∅),[9.42064]→[9.42064:42109](∅→∅),[9.42144]→[9.1867:1908](∅→∅),[9.1867]→[9.1867:1908](∅→∅),[9.1908]→[9.42145:42186](∅→∅),[9.42186]→[9.2103:2129](∅→∅),[9.2129]→[9.11824:11853](∅→∅),[9.11853]→[9.16646:16696](∅→∅),[9.16696]→[9.42224:42259](∅→∅),[9.2072]→[9.42224:42259](∅→∅),[9.42259]→[9.16697:16747](∅→∅),[9.16747]→[9.42260:42295](∅→∅),[9.2194]→[9.42260:42295](∅→∅),[9.42295]→[9.16748:16846](∅→∅),[9.16846]→[9.2364:2435](∅→∅),[9.2364]→[9.2364:2435](∅→∅),[9.2435]→[9.3172:3230](∅→∅),[9.3230]→[9.16847:17016](∅→∅),[9.4990]→[9.16847:17016](∅→∅),[9.17016]→[9.11854:11877](∅→∅),[9.42615]→[9.11854:11877](∅→∅),[9.11877]→[9.17017:17058](∅→∅),[9.17058]→[9.42646:42681](∅→∅),[9.2857]→[9.42646:42681](∅→∅),[9.42681]→[9.17059:17101](∅→∅),[9.17101]→[9.42682:42717](∅→∅),[9.2971]→[9.42682:42717](∅→∅),[9.42717]→[9.17102:17143](∅→∅),[9.17143]→[9.3084:3137](∅→∅),[9.3084]→[9.3084:3137](∅→∅),[9.3190]→[9.3190:3252](∅→∅),[9.3252]→[9.11878:11935](∅→∅),[9.11935]→[9.3386:3432](∅→∅),[9.3432]→[9.42782:42848](∅→∅),[9.12039]→[9.42782:42848](∅→∅),[9.42782]→[9.42782:42848](∅→∅),[9.42848]→[9.3245:3277](∅→∅),[9.3277]→[9.42934:42979](∅→∅),[9.42934]→[9.42934:42979](∅→∅),[9.43014]→[9.3442:3474](∅→∅),[9.3442]→[9.3442:3474](∅→∅),[9.3474]→[9.43015:43056](∅→∅),[9.43056]→[9.2130:2156](∅→∅),[9.2156]→[9.12040:12069](∅→∅),[9.12069]→[9.17144:17194](∅→∅),[9.17194]→[9.43094:43129](∅→∅),[9.3629]→[9.43094:43129](∅→∅),[9.43129]→[9.17195:17245](∅→∅),[9.17245]→[9.3742:3809](∅→∅),[9.3742]→[9.3742:3809](∅→∅),[9.3809]→[9.3231:3287](∅→∅),[9.3287]→[9.12070:12093](∅→∅),[9.5039]→[9.12070:12093](∅→∅),[9.12093]→[9.17246:17288](∅→∅),[9.17288]→[9.43161:43196](∅→∅),[9.3942]→[9.43161:43196](∅→∅),[9.43196]→[9.17289:17330](∅→∅),[9.17330]→[9.43197:43232](∅→∅),[9.4046]→[9.43197:43232](∅→∅),[9.43232]→[9.17331:17608](∅→∅),[9.4489]→[9.1484:1489](∅→∅),[9.17608]→[9.1484:1489](∅→∅),[9.43624]→[9.1484:1489](∅→∅),[9.1484]→[9.1484:1489](∅→∅),[9.1489]→[9.4490:4539](∅→∅),[9.4593]→[9.4593:4663](∅→∅),[9.4663]→[9.12094:12151](∅→∅),[9.12151]→[9.3433:3479](∅→∅),[9.3479]→[9.43689:43755](∅→∅),[9.12255]→[9.43689:43755](∅→∅),[9.43689]→[9.43689:43755](∅→∅),[9.43755]→[9.3278:3310](∅→∅),[9.3310]→[9.43841:43886](∅→∅),[9.43841]→[9.43841:43886](∅→∅),[9.43921]→[9.4853:4894](∅→∅),[9.4853]→[9.4853:4894](∅→∅),[9.4894]→[9.43922:43963](∅→∅),[9.43963]→[9.2157:2183](∅→∅),[9.2183]→[9.12256:12285](∅→∅),[9.12285]→[9.17609:17659](∅→∅),[9.17659]→[9.44001:44036](∅→∅),[9.5050]→[9.44001:44036](∅→∅),[9.44036]→[9.17660:17710](∅→∅),[9.17710]→[9.44037:44072](∅→∅),[9.5164]→[9.44037:44072](∅→∅),[9.44072]→[9.17711:17809](∅→∅),[9.17809]→[9.5326:5385](∅→∅),[9.5326]→[9.5326:5385](∅→∅),[9.5385]→[9.3288:3342](∅→∅),[9.3342]→[9.17810:17979](∅→∅),[9.5087]→[9.17810:17979](∅→∅),[9.17979]→[9.12286:12309](∅→∅),[9.44368]→[9.12286:12309](∅→∅),[9.12309]→[9.17980:18021](∅→∅),[9.18021]→[9.44399:44434](∅→∅),[9.5773]→[9.44399:44434](∅→∅),[9.44434]→[9.18022:18064](∅→∅),[9.18064]→[9.44435:44470](∅→∅),[9.5879]→[9.44435:44470](∅→∅),[9.44470]→[9.18065:18106](∅→∅),[9.18106]→[9.5984:5989](∅→∅),[9.5984]→[9.5984:5989](∅→∅),[9.1489]→[9.31401:31512](∅→∅),[9.5989]→[9.31401:31512](∅→∅),[9.31401]→[9.31401:31512](∅→∅),[9.31582]→[9.622:663](∅→∅),[9.663]→[9.3480:3526](∅→∅),[9.3526]→[9.44471:44540](∅→∅),[9.12414]→[9.44471:44540](∅→∅),[9.663]→[9.44471:44540](∅→∅),[9.44540]→[9.3311:3343](∅→∅),[9.3343]→[9.44626:44713](∅→∅),[9.44626]→[9.44626:44713](∅→∅),[9.44748]→[9.2184:2210](∅→∅),[9.2210]→[9.12415:12444](∅→∅),[9.12444]→[9.18107:18167](∅→∅),[9.18167]→[9.44786:44821](∅→∅),[9.32015]→[9.44786:44821](∅→∅),[9.44821]→[9.18168:18228](∅→∅),[9.18228]→[9.44822:44857](∅→∅),[9.32155]→[9.44822:44857](∅→∅),[9.44857]→[9.18229:18280](∅→∅),[9.18280]→[9.32286:32347](∅→∅),[9.32286]→[9.32286:32347](∅→∅),[9.32347]→[9.554:695](∅→∅),[9.695]→[9.18281:18336](∅→∅),[9.1063]→[9.12445:12468](∅→∅),[9.18336]→[9.12445:12468](∅→∅),[9.44946]→[9.12445:12468](∅→∅),[9.12468]→[9.18337:18397](∅→∅),[9.18397]→[9.44977:45012](∅→∅),[9.32650]→[9.44977:45012](∅→∅),[9.45012]→[9.18398:18458](∅→∅),[9.18458]→[9.45013:45048](∅→∅),[9.32790]→[9.45013:45048](∅→∅),[9.45048]→[9.18459:18510](∅→∅),[9.18510]→[9.32921:33004](∅→∅),[9.32921]→[9.32921:33004](∅→∅),[9.33004]→[3.321:436](∅→∅),[3.436]→[9.33130:33154](∅→∅),[9.1141]→[9.33130:33154](∅→∅),[9.1205]→[9.33130:33154](∅→∅),[9.1812]→[9.33130:33154](∅→∅),[9.5356]→[9.33130:33154](∅→∅),[9.12584]→[9.33130:33154](∅→∅),[9.45163]→[9.33130:33154](∅→∅),[9.33130]→[9.33130:33154](∅→∅),[9.33154]→[9.18511:18567](∅→∅),[9.18567]→[3.437:492](∅→∅),[3.492]→[9.33305:33350](∅→∅),[9.1063]→[9.33305:33350](∅→∅),[9.1291]→[9.33305:33350](∅→∅),[9.18622]→[9.33305:33350](∅→∅),[9.45341]→[9.33305:33350](∅→∅),[9.33305]→[9.33305:33350](∅→∅),[9.33395]→[9.33395:33490](∅→∅),[9.33490]→[9.3527:3573](∅→∅),[9.3573]→[9.45342:45404](∅→∅),[9.12689]→[9.45342:45404](∅→∅),[9.33490]→[9.45342:45404](∅→∅),[9.45404]→[9.3344:3376](∅→∅),[9.3376]→[9.45490:45576](∅→∅),[9.45490]→[9.45490:45576](∅→∅),[9.45611]→[9.2211:2237](∅→∅),[9.2237]→[9.12690:12719](∅→∅),[9.12719]→[9.18623:18673](∅→∅),[9.18673]→[9.45649:45684](∅→∅),[9.33759]→[9.45649:45684](∅→∅),[9.45684]→[9.18674:18724](∅→∅),[9.18724]→[9.45685:45720](∅→∅),[9.33864]→[9.45685:45720](∅→∅),[9.45720]→[9.18725:18775](∅→∅),[9.18775]→[9.33969:34032](∅→∅),[9.33969]→[9.33969:34032](∅→∅),[9.34032]→[9.3343:3409](∅→∅),[9.3409]→[9.18776:18886](∅→∅),[9.5410]→[9.18776:18886](∅→∅),[9.18886]→[9.12720:12743](∅→∅),[9.45897]→[9.12720:12743](∅→∅),[9.12743]→[9.18887:18931](∅→∅),[9.18931]→[9.45928:45963](∅→∅),[9.34314]→[9.45928:45963](∅→∅),[9.45963]→[9.18932:18973](∅→∅),[9.18973]→[9.45964:45999](∅→∅),[9.34410]→[9.45964:45999](∅→∅),[9.45999]→[9.18974:19015](∅→∅),[9.19015]→[9.34506:34563](∅→∅),[9.34506]→[9.34506:34563](∅→∅),[9.34620]→[9.34620:34682](∅→∅),[9.34682]→[9.12744:12801](∅→∅),[9.3620]→[9.3620:3666](∅→∅),[9.3666]→[9.46064:46130](∅→∅),[9.13009]→[9.46064:46130](∅→∅),[9.46064]→[9.46064:46130](∅→∅),[9.46130]→[9.3377:3409](∅→∅),[9.3409]→[9.46216:46302](∅→∅),[9.46216]→[9.46216:46302](∅→∅),[9.46337]→[9.2238:2264](∅→∅),[9.2264]→[9.13010:13039](∅→∅),[9.13039]→[9.19016:19066](∅→∅),[9.19066]→[9.46375:46410](∅→∅),[9.35022]→[9.46375:46410](∅→∅),[9.46410]→[9.19067:19117](∅→∅),[9.19117]→[9.35139:35209](∅→∅),[9.35139]→[9.35139:35209](∅→∅),[9.35209]→[9.3410:3476](∅→∅),[9.3476]→[9.13040:13063](∅→∅),[9.5464]→[9.13040:13063](∅→∅),[9.13063]→[9.19118:19160](∅→∅),[9.3220]→[9.46442:46477](∅→∅),[9.19160]→[9.46442:46477](∅→∅),[9.35352]→[9.46442:46477](∅→∅),[9.46477]→[9.19161:19201](∅→∅),[9.3306]→[9.46478:46513](∅→∅),[9.19201]→[9.46478:46513](∅→∅),[9.35458]→[9.46478:46513](∅→∅),[9.46513]→[9.19202:19479](∅→∅),[9.19479]→[9.2208:2258](∅→∅),[9.46921]→[9.2208:2258](∅→∅),[9.35921]→[9.2208:2258](∅→∅),[9.2308]→[9.2308:2360](∅→∅),[9.2360]→[9.13064:13121](∅→∅),[9.3713]→[9.3713:3759](∅→∅),[9.3759]→[9.46986:47034](∅→∅),[9.13329]→[9.46986:47034](∅→∅),[9.46986]→[9.46986:47034](∅→∅),[9.47034]→[9.3410:3442](∅→∅),[9.3442]→[9.47120:47161](∅→∅),[9.47120]→[9.47120:47161](∅→∅),[9.47161]→[9.2497:2537](∅→∅),[9.2497]→[9.2497:2537](∅→∅),[9.2537]→[9.3477:3543](∅→∅),[9.3543]→[9.19480:19538](∅→∅),[9.5518]→[9.19480:19538](∅→∅),[9.2652]→[9.35921:36115](∅→∅),[9.19538]→[9.35921:36115](∅→∅),[9.47252]→[9.35921:36115](∅→∅),[9.35921]→[9.35921:36115](∅→∅),[9.36161]→[9.36161:36235](∅→∅),[9.36235]→[9.13330:13387](∅→∅),[9.3806]→[9.3806:3852](∅→∅),[9.3852]→[9.47317:47386](∅→∅),[9.13595]→[9.47317:47386](∅→∅),[9.47317]→[9.47317:47386](∅→∅),[9.47386]→[9.3443:3475](∅→∅),[9.3475]→[9.47472:47557](∅→∅),[9.47472]→[9.47472:47557](∅→∅),[9.47557]→[9.36424:36505](∅→∅),[9.36424]→[9.36424:36505](∅→∅),[9.36505]→[9.3544:3610](∅→∅),[9.3610]→[9.19539:19592](∅→∅),[9.5572]→[9.19539:19592](∅→∅),[9.19592]→[9.36617:36661](∅→∅),[9.47645]→[9.36617:36661](∅→∅),[9.36617]→[9.36617:36661](∅→∅),[9.36661]→[9.19593:19703](∅→∅),[9.19703]→[9.36813:36839](∅→∅),[9.47824]→[9.36813:36839](∅→∅),[9.36813]→[9.36813:36839](∅→∅),[9.36839]→[9.19704:19759](∅→∅),[9.19759]→[9.36915:36969](∅→∅),[9.47914]→[9.36915:36969](∅→∅),[9.36915]→[9.36915:36969](∅→∅),[9.37023]→[9.37023:37096](∅→∅),[9.37096]→[9.13596:13653](∅→∅),[9.13653]→[9.3853:3899](∅→∅),[9.3945]→[9.47979:48048](∅→∅),[9.13861]→[9.47979:48048](∅→∅),[9.47979]→[9.47979:48048](∅→∅),[9.48048]→[9.3476:3508](∅→∅),[9.3508]→[9.48134:48219](∅→∅),[9.48134]→[9.48134:48219](∅→∅),[9.48219]→[9.37285:37331](∅→∅),[9.37285]→[9.37285:37331](∅→∅),[9.37331]→[9.3611:3677](∅→∅),[9.3677]→[9.19760:19813](∅→∅),[9.5626]→[9.19760:19813](∅→∅),[9.19813]→[9.37451:37491](∅→∅),[9.48315]→[9.37451:37491](∅→∅),[9.37451]→[9.37451:37491](∅→∅),[9.37491]→[9.19814:19924](∅→∅),[9.19924]→[9.37659:37685](∅→∅),[9.48510]→[9.37659:37685](∅→∅),[9.37659]→[9.37659:37685](∅→∅),[9.37685]→[9.19925:19980](∅→∅),[9.19980]→[9.37769:37820](∅→∅),[9.48608]→[9.37769:37820](∅→∅),[9.37769]→[9.37769:37820](∅→∅),[9.37871]→[9.37871:37944](∅→∅),[9.37944]→[9.13862:13919](∅→∅),[9.13919]→[9.3946:3992](∅→∅),[9.4038]→[9.48673:48742](∅→∅),[9.14127]→[9.48673:48742](∅→∅),[9.48673]→[9.48673:48742](∅→∅),[9.48742]→[9.3509:3541](∅→∅),[9.3541]→[9.48828:48913](∅→∅),[9.48828]→[9.48828:48913](∅→∅),[9.48913]→[9.38133:38224](∅→∅),[9.38133]→[9.38133:38224](∅→∅),[9.38224]→[9.3678:3744](∅→∅),[9.3744]→[9.19981:20093](∅→∅),[9.5680]→[9.19981:20093](∅→∅),[9.20093]→[9.38426:38468](∅→∅),[9.49104]→[9.38426:38468](∅→∅),[9.38426]→[9.38426:38468](∅→∅),[9.38468]→[9.20094:20204](∅→∅),[9.20204]→[9.38630:38656](∅→∅),[9.49293]→[9.38630:38656](∅→∅),[9.38630]→[9.38630:38656](∅→∅),[9.38656]→[9.20205:20260](∅→∅),[9.20260]→[9.38737:38783](∅→∅),[9.49388]→[9.38737:38783](∅→∅),[9.38737]→[9.38737:38783](∅→∅),[9.38829]→[9.38829:38875](∅→∅),[9.38875]→[9.14128:14185](∅→∅),[9.14185]→[9.4039:4085](∅→∅),[9.4131]→[9.49453:49522](∅→∅),[9.14393]→[9.49453:49522](∅→∅),[9.49453]→[9.49453:49522](∅→∅),[9.49522]→[9.3542:3574](∅→∅),[9.3574]→[9.49608:49693](∅→∅),[9.49608]→[9.49608:49693](∅→∅),[9.49693]→[9.39064:39134](∅→∅),[9.39064]→[9.39064:39134](∅→∅),[9.39134]→[9.3745:3811](∅→∅),[9.3811]→[9.20261:20371](∅→∅),[9.5734]→[9.20261:20371](∅→∅),[9.20371]→[9.39328:39370](∅→∅),[9.49876]→[9.39328:39370](∅→∅),[9.39328]→[9.39328:39370](∅→∅),[9.39370]→[9.20372:20482](∅→∅),[9.20482]→[9.39526:39552](∅→∅),[9.50059]→[9.39526:39552](∅→∅),[9.39526]→[9.39526:39552](∅→∅),[9.39552]→[9.20483:20538](∅→∅),[9.20538]→[9.39630:39678](∅→∅),[9.50151]→[9.39630:39678](∅→∅),[9.39630]→[9.39630:39678](∅→∅),[9.39726]→[9.39726:39774](∅→∅),[9.39774]→[9.14394:14451](∅→∅),[9.14451]→[9.4132:4178](∅→∅),[9.4224]→[9.50216:50285](∅→∅),[9.14659]→[9.50216:50285](∅→∅),[9.50216]→[9.50216:50285](∅→∅),[9.50285]→[9.3575:3607](∅→∅),[9.3607]→[9.50371:50456](∅→∅),[9.50371]→[9.50371:50456](∅→∅),[9.50456]→[9.39963:40038](∅→∅),[9.39963]→[9.39963:40038](∅→∅),[9.40038]→[9.3812:3878](∅→∅),[9.3878]→[9.20539:20649](∅→∅),[9.5788]→[9.20539:20649](∅→∅),[9.20649]→[9.40232:40274](∅→∅),[9.50639]→[9.40232:40274](∅→∅),[9.40232]→[9.40232:40274](∅→∅),[9.40274]→[9.20650:20760](∅→∅),[9.20760]→[9.40430:40456](∅→∅),[9.50822]→[9.40430:40456](∅→∅),[9.40430]→[9.40430:40456](∅→∅),[9.40456]→[9.20761:20816](∅→∅),[9.20816]→[9.40534:40572](∅→∅),[9.50914]→[9.40534:40572](∅→∅),[9.40534]→[9.40534:40572](∅→∅),[9.40610]→[9.40610:40650](∅→∅),[9.40650]→[9.4225:4271](∅→∅),[9.4271]→[9.50915:50970](∅→∅),[9.14764]→[9.50915:50970](∅→∅),[9.40650]→[9.50915:50970](∅→∅),[9.50970]→[9.3608:3640](∅→∅),[9.3640]→[9.51056:51142](∅→∅),[9.51056]→[9.51056:51142](∅→∅),[9.51177]→[9.40806:40830](∅→∅),[9.40806]→[9.40806:40830](∅→∅),[9.40830]→[9.2265:2291](∅→∅),[9.2291]→[9.696:743](∅→∅),[9.743]→[9.20817:21081](∅→∅),[9.21081]→[9.14765:14794](∅→∅),[9.51546]→[9.14765:14794](∅→∅),[9.14794]→[9.21082:21132](∅→∅),[9.21132]→[9.51583:51618](∅→∅),[9.41276]→[9.51583:51618](∅→∅),[9.51618]→[9.21133:21184](∅→∅),[9.21184]→[9.51619:51654](∅→∅),[9.41375]→[9.51619:51654](∅→∅),[9.51654]→[9.21185:21235](∅→∅),[9.21235]→[9.41473:41483](∅→∅),[9.41473]→[9.41473:41483](∅→∅),[9.41483]→[9.3879:3931](∅→∅),[9.3931]→[9.21236:21464](∅→∅),[9.5883]→[9.21236:21464](∅→∅),[9.21464]→[9.14795:14818](∅→∅),[9.51987]→[9.14795:14818](∅→∅),[9.14818]→[9.21465:21506](∅→∅),[9.21506]→[9.52018:52053](∅→∅),[9.41879]→[9.52018:52053](∅→∅),[9.52053]→[9.21507:21548](∅→∅),[9.21548]→[9.52054:52089](∅→∅),[9.41968]→[9.52054:52089](∅→∅),[9.52089]→[9.21549:21590](∅→∅),[9.21590]→[9.42057:42095](∅→∅),[9.42057]→[9.42057:42095](∅→∅),[9.42133]→[9.42133:42173](∅→∅),[9.42173]→[9.4272:4318](∅→∅),[9.4318]→[9.52090:52146](∅→∅),[9.14923]→[9.52090:52146](∅→∅),[9.42173]→[9.52090:52146](∅→∅),[9.52146]→[9.3641:3673](∅→∅),[9.3673]→[9.52232:52318](∅→∅),[9.52232]→[9.52232:52318](∅→∅),[9.52353]→[9.42330:42354](∅→∅),[9.42330]→[9.42330:42354](∅→∅),[9.42354]→[9.3932:3998](∅→∅),[9.3998]→[9.21591:21855](∅→∅),[9.5937]→[9.21591:21855](∅→∅),[9.21855]→[9.14924:14953](∅→∅),[9.52722]→[9.14924:14953](∅→∅),[9.14953]→[9.21856:21906](∅→∅),[9.21906]→[9.52759:52794](∅→∅),[9.42807]→[9.52759:52794](∅→∅),[9.52794]→[9.21907:21957](∅→∅),[9.21957]→[9.52795:52830](∅→∅),[9.42905]→[9.52795:52830](∅→∅),[9.52830]→[9.21958:22008](∅→∅),[9.22008]→[9.43003:43065](∅→∅),[9.43003]→[9.43003:43065](∅→∅),[9.43065]→[9.3999:4051](∅→∅),[9.4051]→[9.22009:22367](∅→∅),[9.5985]→[9.22009:22367](∅→∅),[9.22367]→[9.14954:14977](∅→∅),[9.53345]→[9.14954:14977](∅→∅),[9.14977]→[9.22368:22409](∅→∅),[9.22409]→[9.53376:53411](∅→∅),[9.43617]→[9.53376:53411](∅→∅),[9.53411]→[9.22410:22452](∅→∅),[9.22452]→[9.53412:53447](∅→∅),[9.43707]→[9.53412:53447](∅→∅),[9.53447]→[9.22453:22494](∅→∅),[9.22494]→[9.2:47](∅→∅),[9.43796]→[9.2:47](∅→∅),[9.92]→[9.92:144](∅→∅),[9.144]→[9.664:703](∅→∅),[9.703]→[9.4319:4365](∅→∅),[9.4365]→[9.53448:53489](∅→∅),[9.15082]→[9.53448:53489](∅→∅),[9.703]→[9.53448:53489](∅→∅),[9.53489]→[9.3674:3706](∅→∅),[9.3706]→[9.53575:53705](∅→∅),[9.53575]→[9.53575:53705](∅→∅),[9.53740]→[9.2292:2318](∅→∅),[9.1313]→[9.355:381](∅→∅),[9.2318]→[9.355:381](∅→∅),[9.355]→[9.355:381](∅→∅),[9.381]→[9.744:791](∅→∅),[9.791]→[9.22495:22617](∅→∅),[9.22617]→[9.574:584](∅→∅),[9.53929]→[9.574:584](∅→∅),[9.574]→[9.574:584](∅→∅),[9.584]→[9.4052:4156](∅→∅),[9.34]→[9.616:643](∅→∅),[9.4156]→[9.616:643](∅→∅),[9.6127]→[9.616:643](∅→∅),[9.616]→[9.616:643](∅→∅),[9.643]→[9.22618:22720](∅→∅),[9.785]→[9.43796:43800](∅→∅),[9.22720]→[9.43796:43800](∅→∅),[9.54098]→[9.43796:43800](∅→∅),[9.43796]→[9.43796:43800](∅→∅),[9.43800]→[9.2:26](∅→∅),[9.54]→[9.54:140](∅→∅),[9.140]→[9.2:120](∅→∅),[9.76]→[9.202:320](∅→∅),[9.120]→[9.202:320](∅→∅),[9.202]→[9.202:320](∅→∅),[9.355]→[9.355:381](∅→∅),[9.678]→[9.678:703](∅→∅),[9.703]→[9.4157:4209](∅→∅),[9.4209]→[9.792:839](∅→∅),[9.750]→[9.792:839](∅→∅),[9.839]→[9.4210:4270](∅→∅),[9.4270]→[9.22721:22835](∅→∅),[9.846]→[9.22721:22835](∅→∅),[9.22835]→[9.992:1051](∅→∅),[9.992]→[9.992:1051](∅→∅),[9.1051]→[9.77:122](∅→∅),[9.122]→[9.1051:1085](∅→∅),[9.1051]→[9.1051:1085](∅→∅),[9.1085]→[9.4271:4323](∅→∅),[9.4323]→[9.840:888](∅→∅),[9.1132]→[9.840:888](∅→∅),[9.888]→[9.4324:4440](∅→∅),[9.4440]→[9.22836:22894](∅→∅),[9.1277]→[9.22836:22894](∅→∅),[9.22894]→[9.121:177](∅→∅),[9.177]→[9.1423:1427](∅→∅),[9.22950]→[9.1423:1427](∅→∅),[9.1423]→[9.1423:1427](∅→∅),[9.1427]→[9.2:3](∅→∅),[9.3]→[9.2:33](∅→∅),[9.69]→[9.69:155](∅→∅),[9.155]→[9.178:256](∅→∅),[9.256]→[9.200:232](∅→∅),[9.200]→[9.200:232](∅→∅),[9.232]→[9.257:298](∅→∅),[9.298]→[9.273:318](∅→∅),[9.273]→[9.273:318](∅→∅),[9.353]→[9.353:404](∅→∅),[9.404]→[9.4441:4493](∅→∅),[9.4493]→[9.889:936](∅→∅),[9.451]→[9.889:936](∅→∅),[9.936]→[9.497:533](∅→∅),[9.497]→[9.497:533](∅→∅),[9.533]→[9.4494:4546](∅→∅),[9.4546]→[9.22951:23009](∅→∅),[9.579]→[9.22951:23009](∅→∅),[9.23009]→[9.299:355](∅→∅),[9.355]→[9.741:746](∅→∅),[9.23065]→[9.741:746](∅→∅),[9.741]→[9.741:746](∅→∅),[9.746]→[9.3:31](∅→∅),[9.3]→[9.3:31](∅→∅),[9.64]→[9.64:150](∅→∅),[9.150]→[9.356:448](∅→∅),[9.448]→[9.191:223](∅→∅),[9.191]→[9.191:223](∅→∅),[9.223]→[9.449:490](∅→∅),[9.490]→[9.264:309](∅→∅),[9.264]→[9.264:309](∅→∅),[9.344]→[9.344:395](∅→∅),[9.395]→[9.4547:4599](∅→∅),[9.4599]→[9.937:984](∅→∅),[9.442]→[9.937:984](∅→∅),[9.984]→[9.4600:4660](∅→∅),[9.4660]→[9.538:556](∅→∅),[9.538]→[9.538:556](∅→∅),[9.556]→[9.23066:23124](∅→∅),[9.23124]→[9.491:547](∅→∅),[9.547]→[9.712:716](∅→∅),[9.23180]→[9.712:716](∅→∅),[9.712]→[9.712:716](∅→∅),[9.716]→[9.2:39](∅→∅),[9.80]→[9.80:166](∅→∅),[9.166]→[9.548:623](∅→∅),[9.623]→[9.211:329](∅→∅),[9.211]→[9.211:329](∅→∅),[9.364]→[9.364:423](∅→∅),[9.423]→[9.4661:4713](∅→∅),[9.4713]→[9.985:1032](∅→∅),[9.470]→[9.985:1032](∅→∅),[9.1032]→[9.4714:4766](∅→∅),[9.4766]→[9.562:580](∅→∅),[9.562]→[9.562:580](∅→∅),[9.580]→[9.23181:23239](∅→∅),[9.23239]→[9.624:680](∅→∅),[9.680]→[9.2:271](∅→∅),[9.271]→[7.2:499](∅→∅),[7.499]→[9.271:493](∅→∅),[9.271]→[9.271:493](∅→∅),[9.493]→[9.752:756](∅→∅),[9.680]→[9.752:756](∅→∅),[9.23295]→[9.752:756](∅→∅),[9.752]→[9.752:756](∅→∅)
function test_right_arrow_scrolls_down_in_wrapped_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.screen_top1 = {line=1, pos=1}-- cursor is at bottom right of screenEditor_state.cursor1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the right arrow the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'right', 'right')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 6, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_home_scrolls_up_in_wrapped_line()-- display lines starting from second screen line of a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.screen_top1 = {line=3, pos=5}-- cursor is at top of screenEditor_state.cursor1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:2')-- after hitting home the screen scrolls up to first screen lineedit.run_after_keychord(Editor_state, 'home', 'home')y = Editor_state.topApp.screen.check(y, 'ghi ', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')endfunction test_end_scrolls_down_in_wrapped_line()-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.screen_top1 = {line=1, pos=1}-- cursor is at bottom right of screenEditor_state.cursor1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting end the screen scrolls down by one lineedit.run_after_keychord(Editor_state, 'end', 'end')check_eq(Editor_state.screen_top1.line, 2, 'screen_top')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 8, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'def', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi ', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_position_cursor_on_recently_edited_wrapping_line()-- draw a line wrapping over 2 screen linesApp.screen.init{width=100, height=200}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc def ghi jkl mno pqr ', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=25}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc def ghi ', 'baseline1/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl mno pqr ', 'baseline1/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'baseline1/screen:3')-- add to the line until it's wrapping over 3 screen linesedit.run_after_text_input(Editor_state, 's')edit.run_after_text_input(Editor_state, 't')edit.run_after_text_input(Editor_state, 'u')check_eq(Editor_state.cursor1.pos, 28, 'cursor:pos')y = Editor_state.topApp.screen.check(y, 'abc def ghi ', 'baseline2/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl mno pqr ', 'baseline2/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'stu', 'baseline2/screen:3')-- try to move the cursor earlier in the third screen line by clicking the mouseedit.run_after_mouse_release(Editor_state, Editor_state.left+2,Editor_state.top+Editor_state.line_height*2+5, 1)-- cursor should movecheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 25, 'cursor:pos')endfunction test_backspace_can_scroll_up()-- display the lines 2/3/4 with the cursor on line 2App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=2, pos=1}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:3')-- after hitting backspace the screen scrolls up by one lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')y = Editor_state.topApp.screen.check(y, 'abcdef', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'screen:3')endfunction test_backspace_can_scroll_up_screen_line()-- display lines starting from second screen line of a lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=5}Editor_state.screen_top1 = {line=3, pos=5}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'jkl', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'baseline/screen:2')-- after hitting backspace the screen scrolls up by one screen lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')y = Editor_state.topApp.screen.check(y, 'ghij', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'kl', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'mno', 'screen:3')check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')check_eq(Editor_state.cursor1.line, 3, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')endfunction test_backspace_past_line_boundary()-- position cursor at start of a (non-first) lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}-- backspace joins with previous lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'abcdef', 'check')end-- some tests for operating over selections created using Shift- chords-- we're just testing delete_selection, and it works the same for all keysfunction test_backspace_over_selection()-- select just one character within a line with cursor before selectionApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}-- backspace deletes the selected character, even though it's after the cursoredit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'bc', 'data')-- cursor (remains) at start of selectioncheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_backspace_over_selection_reverse()-- select just one character within a line with cursor after selectionApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.selection1 = {line=1, pos=1}-- backspace deletes the selected characteredit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'bc', 'data')-- cursor moves to start of selectioncheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_backspace_over_multiple_lines()-- select just one character within a line with cursor after selectionApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.selection1 = {line=4, pos=2}-- backspace deletes the region and joins the remaining portions of lines on either sideedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'akl', 'data:1')check_eq(Editor_state.lines[2].data, 'mno', 'data:2')-- cursor remains at start of selectioncheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_backspace_to_end_of_line()-- select region from cursor to end of lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=2}Editor_state.selection1 = {line=1, pos=4}-- backspace deletes rest of line without joining to any other lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'a', 'data:1')check_eq(Editor_state.lines[2].data, 'def', 'data:2')-- cursor remains at start of selectioncheck_eq(Editor_state.cursor1.line, 1, 'cursor:line')check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_backspace_to_start_of_line()-- select region from cursor to start of lineApp.screen.init{width=Editor_state.left+30, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.selection1 = {line=2, pos=3}-- backspace deletes beginning of line without joining to any other lineedit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.lines[1].data, 'abc', 'data:1')check_eq(Editor_state.lines[2].data, 'f', 'data:2')-- cursor remains at start of selectioncheck_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')-- selection is clearedcheck_nil(Editor_state.selection1.line, 'selection')endfunction test_undo_insert_text()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=4}Editor_state.screen_top1 = {line=1, pos=1}-- insert a characteredit.draw(Editor_state)edit.run_after_text_input(Editor_state, 'g')check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'baseline/cursor:pos')check_nil(Editor_state.selection1.line, 'baseline/selection:line')check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'defg', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'baseline/screen:3')-- undoedit.run_after_keychord(Editor_state, 'C-z', 'z')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection:line')check_nil(Editor_state.selection1.pos, 'selection:pos')y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'screen:3')endfunction test_undo_delete_text()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'defg', 'xyz'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=5}Editor_state.screen_top1 = {line=1, pos=1}-- delete a characteredit.run_after_keychord(Editor_state, 'backspace', 'backspace')check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')check_eq(Editor_state.cursor1.pos, 4, 'baseline/cursor:pos')check_nil(Editor_state.selection1.line, 'baseline/selection:line')check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')local y = Editor_state.topApp.screen.check(y, 'abc', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'def', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'baseline/screen:3')-- undo--? -- after undo, the backspaced key is selectededit.run_after_keychord(Editor_state, 'C-z', 'z')check_eq(Editor_state.cursor1.line, 2, 'cursor:line')check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')check_nil(Editor_state.selection1.line, 'selection:line')check_nil(Editor_state.selection1.pos, 'selection:pos')--? check_eq(Editor_state.selection1.line, 2, 'selection:line')--? check_eq(Editor_state.selection1.pos, 4, 'selection:pos')y = Editor_state.topApp.screen.check(y, 'abc', 'screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'defg', 'screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'xyz', 'screen:3')endfunction test_undo_restores_selection()-- display a line of text with some part selectedApp.screen.init{width=75, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.selection1 = {line=1, pos=2}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- delete selected textedit.run_after_text_input(Editor_state, 'x')check_eq(Editor_state.lines[1].data, 'xbc', 'baseline')check_nil(Editor_state.selection1.line, 'baseline:selection')-- undoedit.run_after_keychord(Editor_state, 'C-z', 'z')edit.run_after_keychord(Editor_state, 'C-z', 'z')-- selection is restoredcheck_eq(Editor_state.selection1.line, 1, 'line')check_eq(Editor_state.selection1.pos, 2, 'pos')endfunction test_search()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'```lines', '```', 'def', 'ghi', '’deg'} -- contains unicode quote in final lineText.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for a stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'd')edit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.cursor1.line, 2, '1/cursor:line')check_eq(Editor_state.cursor1.pos, 1, '1/cursor:pos')-- reset cursorEditor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}-- search for second occurrenceedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'de')edit.run_after_keychord(Editor_state, 'down', 'down')edit.run_after_keychord(Editor_state, 'return', 'return')check_eq(Editor_state.cursor1.line, 4, '2/cursor:line')check_eq(Editor_state.cursor1.pos, 2, '2/cursor:pos')endfunction test_search_upwards()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'’abc', 'abd'} -- contains unicode quoteText.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for a stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'a')-- search for previous occurrenceedit.run_after_keychord(Editor_state, 'up', 'up')check_eq(Editor_state.cursor1.line, 1, '2/cursor:line')check_eq(Editor_state.cursor1.pos, 2, '2/cursor:pos')endfunction test_search_wrap()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'’abc', 'def'} -- contains unicode quote in first lineText.redraw_all(Editor_state)Editor_state.cursor1 = {line=2, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for a stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'a')edit.run_after_keychord(Editor_state, 'return', 'return')-- cursor wrapscheck_eq(Editor_state.cursor1.line, 1, '1/cursor:line')check_eq(Editor_state.cursor1.pos, 2, '1/cursor:pos')endfunction test_search_wrap_upwards()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc ’abd'} -- contains unicode quoteText.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search upwards for a stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_text_input(Editor_state, 'a')edit.run_after_keychord(Editor_state, 'up', 'up')-- cursor wrapscheck_eq(Editor_state.cursor1.line, 1, '1/cursor:line')check_eq(Editor_state.cursor1.pos, 6, '1/cursor:pos')endfunction test_search_downwards_from_end_of_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=4}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for empty stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_keychord(Editor_state, 'down', 'down')-- no crashendfunction test_search_downwards_from_final_pos_of_line()App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', 'def', 'ghi'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=1, pos=3}Editor_state.screen_top1 = {line=1, pos=1}edit.draw(Editor_state)-- search for empty stringedit.run_after_keychord(Editor_state, 'C-f', 'f')edit.run_after_keychord(Editor_state, 'down', 'down')-- no crashend