resolve conflicts
Dependencies
- [2]
QIR3VBYIresolve conflicts - [3]
2EELKVO2resolve conflicts - [4]
JZG2P7IWresolve conflicts - [5]
B4USRORMdisable DPI scaling - [6]
VZPH3XJKupdate source editor - [7]
BLWAYPKVextract a module - [8]
BJ5X5O4Alet's prevent the text cursor from ever getting on a drawing - [9]
JOPVPUSAediting source code from within the app - [10]
BULPIBEGbeginnings of a module for the text editor - [11]
JJDUDMVXMerge lines.love - [12]
QMRQL2FOresolve conflicts - [13]
4CTZOJPCstop pretending globals are local - [14]
6LJZN727handle chords - [15]
NDHQN23Gdone passing left/right margins everywhere - [16]
KWHC65JIMerge lines.love - [17]
CE4LZV4Tdrop last couple of manual tests - [18]
NBTKJTI5resolve conflicts - [19]
CRBLAWBOresolve conflicts - [20]
DSLD74DKlots more tests - [21]
HGC5RGJPswitch to line index in a function - [22]
R5QXEHUIsomebody stop me - [23]
VP5KC4XZMerge lines.love - [24]
UBA2ZUCPremove a duplicate print to screen - [25]
XGHCLIKBMerge lines.love - [26]
2H76FV5Sbugfix: searching files containing unicode - [27]
KKQKPGCIresolve conflicts - [28]
D4B52CQ2Merge lines.love - [29]
RSZD5A7Gforgot to add json.lua - [30]
TYFAGQWSrepeat bugfix on source editor - [31]
LWPFEZBIMerge lines.love - [32]
TFUNIT6Mresolve conflicts - [33]
RAXUQQ6ZMerge lines.love - [34]
OYVFFWBKmove - [35]
SGMA5JLEsave the list of tests in repo - [36]
A4BSGS2CMerge lines.love - [37]
ONHKBLLCMerge lines.love - [38]
VXORMHMEdelete experimental REPL - [39]
A42EMHOQplumb through all supported args in LÖVE handlers - [40]
3PSFWAILMerge lines.love - [41]
T4FRZSYLdelete an ancient, unused file - [42]
AVTNUQYRbasic test-enabled framework - [43]
ZLJYLPOTMerge lines.love - [44]
S2YQBEYCsnapshot: test for a new regression - [45]
LSYLEVBDdrop some redundant args when clearing the cache - [46]
GZ5WULJVswitch source side to new screen-line-based render - [47]
N2NUGNN4include a brief reference enabling many useful apps - [48]
KWIVKQQ7Merge lines.love - [49]
2TQUKHBCMerge lines.love - [50]
MBAJPTDJresolve conflicts - [51]
FS2ITYYHrecord a known issue - [52]
ORRSP7FVdeduce test names on failures - [53]
2L5MEZV3experiment: new edit namespace - [54]
KKMFQDR4editing source code from within the app - [55]
LNUHQOGHstart passing in Editor_state explicitly - [56]
LDFXFRUObring a few things in sync between run and source - [57]
5SM6DRHKport inscript's bugfix to source editor - [58]
F4RUTONDsplit up editor tests between LÖVE 11 and LÖVE 12 - [59]
4SR3Z4Y3document the version of LÖVE I've been using - [60]
2CTN2IEFMerge lines.love - [61]
UN7GKYV5support hyperlinks in the source editor - [62]
ORKN6EOBMerge lines.love - [63]
73OCE2MCafter much struggle, a brute-force undo - [64]
RLCO2SNKwrap lines like lines2 forks - [65]
S2QMLRXLstop creating a singleton table for every word - [66]
MD3W5IRAnew fork: rip out drawing support - [67]
S7CSVBHZresolve conflicts - [68]
YF2ATH2QMerge lines.love - [69]
ISOFHXB2App.width can no longer take a Text - [70]
6K5PFF6Xhelper: trimming whitespace from strings - [71]
NQM25OZVreduce use of rfind - [72]
OGUV4HSAremove some memory leaks from rendered fragments - [73]
FZBXBUFFbugfix: search - [74]
4YDBYBA4clean up memory leak experiments - [75]
VHQCNMARseveral more modules - [76]
TGHAJBESuse line cache for drawings as well - [77]
Q7XPSKIIMerge lines.love - [78]
6VJTQKW7start supporting LÖVE v12 - [79]
AQMZJXURuse editor state font for width calculations - [80]
QZUFJMD5resolve conflicts - [81]
YXQOITYSMerge lines.love - [82]
VHUNJHXBMerge lines.love - [83]
TOXPJJYYresolve conflicts - [84]
K2X6G75Zstart writing some tests for drawings - [85]
LF7BWEG4group all editor globals - [86]
LAW2O3NWextract variable Margin_left - [87]
2JLVAYHBstart decoupling editor tests from App - [88]
3QNOKBFMbeginnings of a test harness - [89]
5XA7TKWYpull font into editor - [90]
ILOA5BYFseparate data structure for each line's cache data - [91]
KMSL74GAsupport selections in the source editor - [92]
XX7G2FFJintermingle freehand line drawings with text - [93]
QYIFOHW3first test! - [94]
MUJTM6REbring back a level of wrapping - [95]
D2GCFTTTclean up repl functionality - [96]
EETIR4GXbugfix: skip over drawings when searching - [97]
T42Y5MLOexplicitly specify app name - [98]
PLKNHYZ4extract a function - [99]
B4FAIVRAMerge lines.love - [100]
ED4Z6ORCcleaner API for file-system access - [101]
TVCPXAAUrename - [102]
PFT5Y2ZYmove - [103]
RUB7L6GYresolve conflicts - [104]
HTWAM4NZbugfix: scrolling in left/right movements - [105]
TLOAPLBJadd a license - [106]
I64IPGJXavoid saving fragments in lines - [107]
CFJ4FLCQclean up an unnecessary mutation - [108]
H4R5BHVYno more Text allocations - [109]
6Z6WH62Wresolve conflicts - [110]
66X36NZNa little more prose describing manual_tests - [111]
LXTTOB33extract a couple of files - [112]
OTIBCAUJlove2d scaffold - [113]
2CK5QI7Wmake love event names consistent - [114]
APYPFFS3call edit rather than App callbacks in tests - [*]
JYZKEDDGMerge lines.love
Change contents
- file deletion: source_text_tests.lua source_text_tests.lua
---- 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.Version, Major_version = App.love_version()if Major_version == 11 thenload_file_from_source_or_save_directory('source_text_tests_love11.lua')elseif Major_version == 12 then-- not released/stable yetload_file_from_source_or_save_directory('source_text_tests_love12.lua')end - file deletion: source_text.lua source_text.lua
--? -- render screen line--? App.color(Text_color)--? App.screen.print(screen_line, State.left,y)for pos,char in utf8chars(line.data) dolocal w = State.font:getWidth(char)if Text.should_word_wrap(State, line.data, pos, char, x)or x+w > State.width -- truncate within a wordthenx = 0x = x + wendend-- Check whether to word-wrap line at pos which will be positioned at x.---- We wrap at the start of a word (non-space just after space) if the word-- (non-spaces followed by spaces) wouldn't fit in the rest of the line.---- x lies between 0 and editor.width.---- Postcondition:-- Current line is not wider than editor.width---- Desired properties in priority order:-- Next line doesn't start with whitespace-- Current line ends with whitespace (a.k.a. word wrap)-- Current line is close to full-- None of these is guaranteed. But we should never satisfy a lower priority-- before a higher one.function Text.should_word_wrap(editor, line, pos, char, x)if char:match('%s') then return false endif pos == 1 then return false endif Text.match(line, pos-1, '%S') then return false endlocal offset = Text.offset(line, pos)-- most of the time a word is printable chars + whitespacelocal s = line:match('%S+%s*', offset)assert(s)local w = editor.font:getWidth(s)if x+w < editor.width then return false endif w > editor.width then return false end -- we're going to need to truncate the next word anywayif x < 0.8*editor.width thenlocal s2 = line:match('%S+', offset)local w2 = editor.font:getWidth(s2)if x+w2 > editor.width then-- there'll be some non-whitespace left over for the next linereturn falseendreturn true--== shortcuts that mutate text (must schedule_save)end-- create a new iterator for s which provides the index and UTF-8 bytes corresponding to each codepointfunction utf8chars(s, startpos)local next_pos = startpos or 1 -- in code pointslocal next_offset = utf8.offset(s, next_pos) -- in bytesreturn function()assert(next_offset) -- never call the iterator after it returns nillocal curr_pos = next_posnext_pos = next_pos+1local curr_offset = next_offsetnext_offset = utf8.offset(s, 2, next_offset)if next_offset == nil then return endlocal curr_char = s:sub(curr_offset, next_offset-1)return curr_pos, curr_charendendif chord == 'return' thenlocal before_line = State.cursor1.linelocal before = snapshot(State, before_line)Text.insert_return(State)endendendtable.insert(line_cache.screen_line_starting_pos, pos)-- render colorized textlocal x = State.leftfor frag in screen_line:gmatch('%S*%s*') doselect_color(frag)App.screen.print(frag, x,y) - file deletion: source_edit.lua source_edit.lua
function edit.update_font_settings(State, font_height, font)State.font = font or love.graphics.newFont(State.font_height)State.line_height = math.floor(font_height*1.3)State.font_height = font_height - edit in text_tests_love12.lua at line 12
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') - edit in text_tests_love12.lua at line 14
function 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')end - edit in text_tests_love12.lua at line 966
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') - edit in text_tests_love12.lua at line 1038
function 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')end - edit in text_tests_love12.lua at line 1181
function 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')end - edit in text_tests_love12.lua at line 1206
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') - edit in text_tests_love11.lua at line 12
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') - edit in text_tests_love11.lua at line 968
function 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')end - edit in text_tests_love11.lua at line 1038
function 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')end - edit in text_tests_love11.lua at line 1179
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') - edit in text_tests_love11.lua at line 1208
function 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')end - replacement in text_tests_love11.lua at line 1870
Editor_state.lines = load_array{'```lines', '```', 'def', 'ghi', '’deg'} -- contains unicode quote in final lineEditor_state.lines = load_array{'abc', 'def', 'ghi', '’deg'} -- contains unicode quote in final line - edit in text_tests.lua at line 2[7.28]→[7.2:283](∅→∅),[7.28]→[7.2:283](∅→∅),[7.24349]→[7.7673:7714](∅→∅),[7.24349]→[7.7673:7714](∅→∅)
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')App.screen.check(y, 'ghi', 'screen:2') - edit in text_tests.lua at line 19[7.381]→[7.2:49](∅→∅),[7.381]→[7.2:49](∅→∅),[7.49]→[7.3:3](∅→∅),[7.49]→[7.3:3](∅→∅),[7.3]→[7.22495:22617](∅→∅),[7.791]→[7.22495:22617](∅→∅),[7.791]→[7.22495:22617](∅→∅),[7.140]→[7.2:108](∅→∅),[7.140]→[7.2:108](∅→∅)
edit.run_after_text_input(Editor_state, 'x')check_eq(Editor_state.lines[1].data, 'xbc', 'baseline')check_nil(Editor_state.selection1.line, 'baseline:selection')Editor_state.lines = load_array{'abc', 'def', 'ghi', '’deg'} -- contains unicode quote in final line - edit in text.lua at line 93[7.124]→[7.1894:1938](∅→∅),[7.1938]→[7.5:5](∅→∅),[7.1938]→[7.5:5](∅→∅),[7.5]→[7.222:271](∅→∅),[7.168]→[7.222:271](∅→∅),[7.168]→[7.222:271](∅→∅)
for frag in line.data:gmatch('%S*%s*') dolocal frag_width = State.font:getWidth(frag) - edit in text.lua at line 1014
- replacement in source_text_tests.lua at line 2[7.3600]→[7.7:119](∅→∅),[7.119]→[7.3600:3631](∅→∅),[7.3600]→[7.3600:3631](∅→∅),[7.3666]→[7.3666:3846](∅→∅),[7.3846]→[7.8:289](∅→∅),[7.289]→[7.812:857](∅→∅),[7.4242]→[7.812:857](∅→∅),[7.857]→[7.1251:1292](∅→∅),[7.1292]→[7.942:1201](∅→∅),[7.942]→[7.942:1201](∅→∅),[7.1201]→[7.290:386](∅→∅),[7.386]→[7.1363:1412](∅→∅),[7.1363]→[7.1363:1412](∅→∅),[7.1461]→[7.1461:1893](∅→∅),[7.1893]→[7.7:73](∅→∅),[7.73]→[7.387:483](∅→∅),[7.1946]→[7.387:483](∅→∅),[7.483]→[7.4242:4298](∅→∅),[7.2116]→[7.4242:4298](∅→∅),[7.4242]→[7.4242:4298](∅→∅),[7.4354]→[7.4354:4691](∅→∅),[7.4691]→[7.74:140](∅→∅),[7.140]→[7.484:639](∅→∅),[7.4744]→[7.484:639](∅→∅),[7.639]→[7.5031:5075](∅→∅),[7.5031]→[7.5031:5075](∅→∅),[7.5119]→[7.5119:5299](∅→∅),[7.5299]→[7.56:103](∅→∅),[7.103]→[7.5345:5374](∅→∅),[7.5345]→[7.5345:5374](∅→∅),[7.5374]→[7.640:679](∅→∅),[7.679]→[7.5445:5477](∅→∅),[7.5445]→[7.5445:5477](∅→∅),[7.5509]→[7.5509:5794](∅→∅),[7.5829]→[7.141:193](∅→∅),[7.193]→[7.5876:5907](∅→∅),[7.5876]→[7.5876:5907](∅→∅),[7.5938]→[7.5938:6162](∅→∅),[7.6162]→[7.194:250](∅→∅),[7.250]→[7.680:729](∅→∅),[7.6210]→[7.680:729](∅→∅),[7.729]→[7.6272:6304](∅→∅),[7.6272]→[7.6272:6304](∅→∅),[7.6336]→[7.6336:6560](∅→∅),[7.6560]→[7.251:309](∅→∅),[7.309]→[7.730:779](∅→∅),[7.6609]→[7.730:779](∅→∅),[7.779]→[7.6672:6720](∅→∅),[7.6672]→[7.6672:6720](∅→∅),[7.6768]→[7.6768:7001](∅→∅),[7.7001]→[7.310:366](∅→∅),[7.366]→[7.780:897](∅→∅),[7.7049]→[7.780:897](∅→∅),[7.897]→[7.7238:7283](∅→∅),[7.7238]→[7.7238:7283](∅→∅),[7.7328]→[7.7328:7582](∅→∅),[7.7582]→[7.367:425](∅→∅),[7.425]→[7.898:994](∅→∅),[7.7631]→[7.898:994](∅→∅),[7.994]→[7.7793:7836](∅→∅),[7.7793]→[7.7793:7836](∅→∅),[7.7879]→[7.7879:8105](∅→∅),[7.8105]→[7.426:484](∅→∅),[7.484]→[7.995:1044](∅→∅),[7.8155]→[7.995:1044](∅→∅),[7.1044]→[7.8229:8281](∅→∅),[7.8229]→[7.8229:8281](∅→∅),[7.8333]→[7.8333:8594](∅→∅),[7.8594]→[7.485:543](∅→∅),[7.543]→[7.1045:1094](∅→∅),[7.8644]→[7.1045:1094](∅→∅),[7.1094]→[7.8727:8770](∅→∅),[7.8727]→[7.8727:8770](∅→∅),[7.8813]→[7.8813:9075](∅→∅),[7.9075]→[7.544:602](∅→∅),[7.602]→[7.1095:1144](∅→∅),[7.9125]→[7.1095:1144](∅→∅),[7.1144]→[7.9199:9251](∅→∅),[7.9199]→[7.9199:9251](∅→∅),[7.9303]→[7.9303:9561](∅→∅),[7.9561]→[7.603:661](∅→∅),[7.661]→[7.1145:1194](∅→∅),[7.9611]→[7.1145:1194](∅→∅),[7.1194]→[7.9694:9753](∅→∅),[7.9694]→[7.9694:9753](∅→∅),[7.9812]→[7.9812:10075](∅→∅),[7.10075]→[7.662:720](∅→∅),[7.720]→[7.1195:1244](∅→∅),[7.10125]→[7.1195:1244](∅→∅),[7.1244]→[7.10215:10275](∅→∅),[7.10215]→[7.10215:10275](∅→∅),[7.10335]→[7.10335:10572](∅→∅),[7.10572]→[7.721:779](∅→∅),[7.779]→[7.1245:1341](∅→∅),[7.10622]→[7.1245:1341](∅→∅),[7.1341]→[7.10814:10857](∅→∅),[7.10814]→[7.10814:10857](∅→∅),[7.10900]→[7.10900:11130](∅→∅),[7.11130]→[7.780:840](∅→∅),[7.840]→[7.1342:1391](∅→∅),[7.11181]→[7.1342:1391](∅→∅),[7.1391]→[7.11255:11294](∅→∅),[7.11255]→[7.11255:11294](∅→∅),[7.11333]→[7.11333:11594](∅→∅),[7.11594]→[7.841:901](∅→∅),[7.901]→[7.1392:1441](∅→∅),[7.11645]→[7.1392:1441](∅→∅),[7.1441]→[7.11715:11763](∅→∅),[7.11715]→[7.11715:11763](∅→∅),[7.11811]→[7.11811:12073](∅→∅),[7.12073]→[7.902:962](∅→∅),[7.962]→[7.1442:1491](∅→∅),[7.12124]→[7.1442:1491](∅→∅),[7.1491]→[7.12203:12258](∅→∅),[7.12203]→[7.12203:12258](∅→∅),[7.12313]→[7.12313:12576](∅→∅),[7.12576]→[7.1293:1353](∅→∅),[7.1024]→[7.1492:1541](∅→∅),[7.1353]→[7.1492:1541](∅→∅),[7.12627]→[7.1492:1541](∅→∅),[7.1541]→[7.12713:12769](∅→∅),[7.12713]→[7.12713:12769](∅→∅),[7.12825]→[7.12825:13062](∅→∅),[7.13062]→[7.1354:1414](∅→∅),[7.1086]→[7.1542:1638](∅→∅),[7.1414]→[7.1542:1638](∅→∅),[7.13113]→[7.1542:1638](∅→∅),[7.1638]→[7.13297:13302](∅→∅),[7.13297]→[7.13297:13302](∅→∅),[7.13302]→[7.104:139](∅→∅),[7.179]→[7.179:218](∅→∅),[7.218]→[7.13462:13508](∅→∅),[7.13462]→[7.13462:13508](∅→∅),[7.13508]→[7.219:274](∅→∅),[7.274]→[7.13556:13588](∅→∅),[7.13556]→[7.13556:13588](∅→∅),[7.13588]→[7.275:316](∅→∅),[7.316]→[7.13629:13674](∅→∅),[7.13629]→[7.13629:13674](∅→∅),[7.13709]→[7.317:348](∅→∅),[7.348]→[7.1415:1488](∅→∅),[7.1488]→[7.443:531](∅→∅),[7.443]→[7.443:531](∅→∅),[7.531]→[7.1639:1749](∅→∅),[7.1749]→[7.697:754](∅→∅),[7.697]→[7.697:754](∅→∅),[7.754]→[7.1750:1868](∅→∅),[7.129]→[7.13950:13955](∅→∅),[7.928]→[7.13950:13955](∅→∅),[7.1868]→[7.13950:13955](∅→∅),[7.13950]→[7.13950:13955](∅→∅),[7.13955]→[7.929:967](∅→∅),[7.1010]→[7.14058:14352](∅→∅),[7.14058]→[7.14058:14352](∅→∅),[7.14387]→[7.7:38](∅→∅),[7.38]→[7.14387:14569](∅→∅),[7.14387]→[7.14387:14569](∅→∅),[7.14569]→[7.1869:2076](∅→∅),[7.269]→[7.14763:14768](∅→∅),[7.1311]→[7.14763:14768](∅→∅),[7.2076]→[7.14763:14768](∅→∅),[7.14763]→[7.14763:14768](∅→∅),[7.14768]→[7.1312:1361](∅→∅),[7.1415]→[7.14893:15257](∅→∅),[7.14893]→[7.14893:15257](∅→∅),[7.15292]→[7.39:70](∅→∅),[7.70]→[7.15292:15451](∅→∅),[7.15292]→[7.15292:15451](∅→∅),[7.15451]→[7.2077:2284](∅→∅),[7.420]→[7.15667:15672](∅→∅),[7.1749]→[7.15667:15672](∅→∅),[7.2284]→[7.15667:15672](∅→∅),[7.15667]→[7.15667:15672](∅→∅),[7.15672]→[7.1750:1786](∅→∅),[7.1827]→[7.15771:16067](∅→∅),[7.15771]→[7.15771:16067](∅→∅),[7.16102]→[7.71:102](∅→∅),[7.102]→[7.16102:16261](∅→∅),[7.16102]→[7.16102:16261](∅→∅),[7.16261]→[7.103:285](∅→∅),[7.285]→[2.7:54](∅→∅),[2.54]→[7.323:589](∅→∅),[7.323]→[7.323:589](∅→∅),[7.624]→[7.624:796](∅→∅),[7.796]→[2.55:192](∅→∅),[2.192]→[7.822:948](∅→∅),[7.2336]→[7.822:948](∅→∅),[7.948]→[7.16352:16383](∅→∅),[7.1908]→[7.16352:16383](∅→∅),[7.2336]→[7.16352:16383](∅→∅),[7.16352]→[7.16352:16383](∅→∅),[7.16414]→[7.16414:16673](∅→∅),[7.16708]→[7.16708:16763](∅→∅),[7.16763]→[7.2337:2378](∅→∅),[7.2378]→[7.16823:16858](∅→∅),[7.16823]→[7.16823:16858](∅→∅),[7.16858]→[7.2379:2420](∅→∅),[7.2420]→[7.16918:16953](∅→∅),[7.16918]→[7.16918:16953](∅→∅),[7.16953]→[7.2421:2462](∅→∅),[7.2462]→[7.17013:17053](∅→∅),[7.17013]→[7.17013:17053](∅→∅),[7.17093]→[7.17093:17353](∅→∅),[7.17388]→[7.17388:17443](∅→∅),[7.17443]→[7.2463:2504](∅→∅),[7.2504]→[7.17512:17547](∅→∅),[7.17512]→[7.17512:17547](∅→∅),[7.17547]→[7.2505:2545](∅→∅),[7.2545]→[7.17615:17650](∅→∅),[7.17615]→[7.17615:17650](∅→∅),[7.17650]→[7.2546:2587](∅→∅),[7.2587]→[7.17719:17764](∅→∅),[7.17719]→[7.17719:17764](∅→∅),[7.17809]→[7.17809:18068](∅→∅),[7.18103]→[7.18103:18158](∅→∅),[7.18158]→[7.2588:2630](∅→∅),[7.2630]→[7.18233:18268](∅→∅),[7.18233]→[7.18233:18268](∅→∅),[7.18268]→[7.2631:2673](∅→∅),[7.2673]→[7.18343:18378](∅→∅),[7.18343]→[7.18343:18378](∅→∅),[7.18378]→[7.2674:2715](∅→∅),[7.2715]→[7.18452:18457](∅→∅),[7.18452]→[7.18452:18457](∅→∅),[7.18457]→[7.1909:1948](∅→∅),[7.1948]→[7.6:63](∅→∅),[7.63]→[7.18612:18881](∅→∅),[7.18612]→[7.18612:18881](∅→∅),[7.18916]→[7.18916:19075](∅→∅),[7.19075]→[7.2716:2923](∅→∅),[7.561]→[7.19271:19276](∅→∅),[7.2296]→[7.19271:19276](∅→∅),[7.2923]→[7.19271:19276](∅→∅),[7.19271]→[7.19271:19276](∅→∅),[7.19276]→[7.2297:2363](∅→∅),[7.2363]→[7.64:121](∅→∅),[7.121]→[7.19485:19817](∅→∅),[7.19485]→[7.19485:19817](∅→∅),[7.19852]→[7.19852:20011](∅→∅),[7.20011]→[7.2924:3131](∅→∅),[7.729]→[7.20261:20377](∅→∅),[7.2819]→[7.20261:20377](∅→∅),[7.3131]→[7.20261:20377](∅→∅),[7.20261]→[7.20261:20377](∅→∅),[7.20429]→[7.20429:20690](∅→∅),[7.20725]→[7.20725:20780](∅→∅),[7.20780]→[7.3132:3175](∅→∅),[7.3175]→[7.20863:20898](∅→∅),[7.20863]→[7.20863:20898](∅→∅),[7.20898]→[7.3176:3219](∅→∅),[7.3219]→[7.20981:21016](∅→∅),[7.20981]→[7.20981:21016](∅→∅),[7.21016]→[7.3220:3261](∅→∅),[7.3261]→[7.21097:21201](∅→∅),[7.21097]→[7.21097:21201](∅→∅),[7.21262]→[7.21262:21562](∅→∅),[7.21597]→[7.21597:21652](∅→∅),[7.21652]→[7.3262:3303](∅→∅),[7.3303]→[7.21742:21777](∅→∅),[7.21742]→[7.21742:21777](∅→∅),[7.21777]→[7.3304:3346](∅→∅),[7.3346]→[7.21868:21903](∅→∅),[7.21868]→[7.21868:21903](∅→∅),[7.21903]→[7.3347:3391](∅→∅),[7.3391]→[7.21996:22001](∅→∅),[7.21996]→[7.21996:22001](∅→∅),[7.22001]→[7.122:168](∅→∅),[7.168]→[7.22084:22418](∅→∅),[7.22084]→[7.22084:22418](∅→∅),[7.22453]→[7.22453:22508](∅→∅),[7.22508]→[7.3392:3445](∅→∅),[7.3445]→[7.22593:22628](∅→∅),[7.22593]→[7.22593:22628](∅→∅),[7.22628]→[7.3446:3499](∅→∅),[7.3499]→[7.22713:22860](∅→∅),[7.22713]→[7.22713:22860](∅→∅),[7.22860]→[7.7:85](∅→∅),[7.85]→[7.3500:3556](∅→∅),[7.22900]→[7.3500:3556](∅→∅),[7.3556]→[7.86:141](∅→∅),[7.141]→[7.23075:23158](∅→∅),[7.3611]→[7.23075:23158](∅→∅),[7.23075]→[7.23075:23158](∅→∅),[7.23241]→[7.23241:23603](∅→∅),[7.23638]→[7.23638:23693](∅→∅),[7.23693]→[7.3612:3665](∅→∅),[7.3665]→[7.23817:23964](∅→∅),[7.23817]→[7.23817:23964](∅→∅),[7.23964]→[7.142:220](∅→∅),[7.220]→[7.3666:3722](∅→∅),[7.24004]→[7.3666:3722](∅→∅),[7.3722]→[7.221:276](∅→∅),[7.276]→[7.24257:24310](∅→∅),[7.3777]→[7.24257:24310](∅→∅),[7.24257]→[7.24257:24310](∅→∅),[7.24363]→[7.24363:24697](∅→∅),[7.24732]→[7.24732:24787](∅→∅),[7.24787]→[7.3778:3831](∅→∅),[7.3831]→[7.24881:24916](∅→∅),[7.24881]→[7.24881:24916](∅→∅),[7.24916]→[7.3832:3885](∅→∅),[7.3885]→[7.25010:25045](∅→∅),[7.25010]→[7.25010:25045](∅→∅),[7.25045]→[7.3886:3935](∅→∅),[7.3935]→[7.25135:25303](∅→∅),[7.25135]→[7.25135:25303](∅→∅),[7.25303]→[7.3936:4037](∅→∅),[7.4037]→[7.25445:25519](∅→∅),[7.25445]→[7.25445:25519](∅→∅),[7.25593]→[7.25593:25986](∅→∅),[7.26021]→[7.26021:26076](∅→∅),[7.26076]→[7.4038:4091](∅→∅),[7.4091]→[7.26191:26226](∅→∅),[7.26191]→[7.26191:26226](∅→∅),[7.26226]→[7.4092:4147](∅→∅),[7.4147]→[7.26343:26378](∅→∅),[7.26343]→[7.26343:26378](∅→∅),[7.26378]→[7.4148:4197](∅→∅),[7.4197]→[7.26489:26657](∅→∅),[7.26489]→[7.26489:26657](∅→∅),[7.26657]→[7.4198:4299](∅→∅),[7.4299]→[7.26820:26878](∅→∅),[7.26820]→[7.26820:26878](∅→∅),[7.26936]→[7.26936:27425](∅→∅),[7.27460]→[7.27460:27515](∅→∅),[7.27515]→[7.4300:4367](∅→∅),[7.4367]→[7.27628:27776](∅→∅),[7.27628]→[7.27628:27776](∅→∅),[7.27776]→[7.277:406](∅→∅),[7.406]→[7.730:763](∅→∅),[7.4419]→[7.730:763](∅→∅),[7.27913]→[7.730:763](∅→∅),[7.796]→[7.796:1072](∅→∅),[7.1107]→[7.1107:1185](∅→∅),[7.1185]→[7.1489:1549](∅→∅),[7.1148]→[7.1236:1269](∅→∅),[7.1549]→[7.1236:1269](∅→∅),[7.1236]→[7.1236:1269](∅→∅),[7.1269]→[7.2820:2863](∅→∅),[7.2863]→[7.6:59](∅→∅),[7.59]→[7.4420:4652](∅→∅),[7.4652]→[7.1682:1750](∅→∅),[7.1682]→[7.1682:1750](∅→∅),[7.1818]→[7.1818:2158](∅→∅),[7.2193]→[7.2193:2257](∅→∅),[7.2257]→[7.1550:1608](∅→∅),[7.1208]→[7.2306:2349](∅→∅),[7.1608]→[7.2306:2349](∅→∅),[7.2306]→[7.2306:2349](∅→∅),[7.2349]→[7.4653:4758](∅→∅),[7.2560]→[7.27913:27918](∅→∅),[7.4758]→[7.27913:27918](∅→∅),[7.27913]→[7.27913:27918](∅→∅),[7.27918]→[7.2561:2600](∅→∅),[7.2644]→[7.2644:2984](∅→∅),[7.3019]→[7.3019:3062](∅→∅),[7.3062]→[7.2916:2963](∅→∅),[7.2963]→[7.3108:3164](∅→∅),[7.3108]→[7.3108:3164](∅→∅),[7.3164]→[7.4759:4814](∅→∅),[7.4814]→[7.3245:3304](∅→∅),[7.3245]→[7.3245:3304](∅→∅),[7.3363]→[7.3363:3703](∅→∅),[7.3738]→[7.3738:3846](∅→∅),[7.3846]→[7.2964:3085](∅→∅),[7.3085]→[7.3969:4058](∅→∅),[7.3969]→[7.3969:4058](∅→∅),[7.4058]→[7.4815:4920](∅→∅),[7.4920]→[7.4251:4302](∅→∅),[7.4251]→[7.4251:4302](∅→∅),[7.4353]→[7.4353:4686](∅→∅),[7.4721]→[7.4721:4767](∅→∅),[7.4767]→[7.1209:1261](∅→∅),[7.1261]→[7.4921:4965](∅→∅),[7.4814]→[7.4921:4965](∅→∅),[7.4965]→[7.4897:4952](∅→∅),[7.4897]→[7.4897:4952](∅→∅),[7.4952]→[7.4966:5013](∅→∅),[7.5013]→[7.5032:5057](∅→∅),[7.5032]→[7.5032:5057](∅→∅),[7.5082]→[7.5082:5422](∅→∅),[7.5457]→[7.5457:5500](∅→∅),[7.5500]→[7.1262:1314](∅→∅),[7.1314]→[7.5014:5058](∅→∅),[7.5547]→[7.5014:5058](∅→∅),[7.5058]→[7.5604:5634](∅→∅),[7.5604]→[7.5604:5634](∅→∅),[7.5634]→[7.5059:5112](∅→∅),[7.5112]→[7.5700:5746](∅→∅),[7.5700]→[7.5700:5746](∅→∅),[7.5792]→[7.5792:6132](∅→∅),[7.6167]→[7.6167:6257](∅→∅),[7.6257]→[7.1315:1367](∅→∅),[7.1367]→[7.6304:6417](∅→∅),[7.6304]→[7.6304:6417](∅→∅),[7.6417]→[7.5113:5171](∅→∅),[7.5171]→[7.6503:6554](∅→∅),[7.6503]→[7.6503:6554](∅→∅),[7.6605]→[7.6605:6896](∅→∅),[7.6931]→[7.6931:6986](∅→∅),[7.6986]→[7.5172:5222](∅→∅),[7.5222]→[7.7075:7110](∅→∅),[7.7075]→[7.7075:7110](∅→∅),[7.7110]→[7.5223:5273](∅→∅),[7.5273]→[7.7199:7234](∅→∅),[7.7199]→[7.7199:7234](∅→∅),[7.7234]→[7.5274:5324](∅→∅),[7.5324]→[7.7323:7457](∅→∅),[7.7323]→[7.7323:7457](∅→∅),[7.7457]→[7.1368:1434](∅→∅),[7.1434]→[7.7510:7531](∅→∅),[7.7510]→[7.7510:7531](∅→∅),[7.7531]→[7.5325:5433](∅→∅),[7.5433]→[7.7711:7716](∅→∅),[7.7711]→[7.7711:7716](∅→∅),[7.7716]→[7.27918:27953](∅→∅),[7.27918]→[7.27918:27953](∅→∅),[7.27993]→[7.27993:28251](∅→∅),[7.28286]→[7.28286:28312](∅→∅),[7.28312]→[7.3086:3133](∅→∅),[7.3133]→[7.28358:28387](∅→∅),[7.28358]→[7.28358:28387](∅→∅),[7.28387]→[7.5434:5475](∅→∅),[7.5475]→[7.28456:28491](∅→∅),[7.28456]→[7.28456:28491](∅→∅),[7.28491]→[7.5476:5516](∅→∅),[7.5516]→[7.28559:28594](∅→∅),[7.28559]→[7.28559:28594](∅→∅),[7.28594]→[7.5517:5557](∅→∅),[7.5557]→[7.28662:28698](∅→∅),[7.28662]→[7.28662:28698](∅→∅),[7.28734]→[7.28734:29042](∅→∅),[7.29077]→[7.29077:29132](∅→∅),[7.29132]→[7.5558:5608](∅→∅),[7.5608]→[7.29206:29241](∅→∅),[7.29206]→[7.29206:29241](∅→∅),[7.29241]→[7.5609:5659](∅→∅),[7.5659]→[7.29315:29350](∅→∅),[7.29315]→[7.29315:29350](∅→∅),[7.29350]→[7.5660:5710](∅→∅),[7.5710]→[7.29424:29467](∅→∅),[7.29424]→[7.29424:29467](∅→∅),[7.29467]→[7.1435:1495](∅→∅),[7.1495]→[7.5711:5880](∅→∅),[7.29517]→[7.5711:5880](∅→∅),[7.5880]→[7.29758:29781](∅→∅),[7.29758]→[7.29758:29781](∅→∅),[7.29781]→[7.5881:5920](∅→∅),[7.5920]→[7.29844:29879](∅→∅),[7.29844]→[7.29844:29879](∅→∅),[7.29879]→[7.5921:5961](∅→∅),[7.5961]→[7.29943:29978](∅→∅),[7.29943]→[7.29943:29978](∅→∅),[7.29978]→[7.5962:6003](∅→∅),[7.6003]→[7.30043:30096](∅→∅),[7.30043]→[7.30043:30096](∅→∅),[7.30149]→[7.30149:30431](∅→∅),[7.30466]→[7.30466:30509](∅→∅),[7.30509]→[7.1496:1556](∅→∅),[7.1556]→[7.6004:6223](∅→∅),[7.30559]→[7.6004:6223](∅→∅),[7.6223]→[7.30942:30985](∅→∅),[7.30942]→[7.30942:30985](∅→∅),[7.31028]→[7.31028:31336](∅→∅),[7.31371]→[7.31371:31426](∅→∅),[7.31426]→[7.6224:6274](∅→∅),[7.6274]→[7.31507:31542](∅→∅),[7.31507]→[7.31507:31542](∅→∅),[7.31542]→[7.6275:6325](∅→∅),[7.6325]→[7.31623:31658](∅→∅),[7.31623]→[7.31623:31658](∅→∅),[7.31658]→[7.6326:6376](∅→∅),[7.6376]→[7.31739:31838](∅→∅),[7.31739]→[7.31739:31838](∅→∅),[7.31838]→[7.1557:1609](∅→∅),[7.1609]→[7.6377:6546](∅→∅),[7.31885]→[7.6377:6546](∅→∅),[7.6546]→[7.32147:32170](∅→∅),[7.32147]→[7.32147:32170](∅→∅),[7.32170]→[7.6547:6588](∅→∅),[7.6588]→[7.32242:32277](∅→∅),[7.32242]→[7.32242:32277](∅→∅),[7.32277]→[7.6589:6630](∅→∅),[7.6630]→[7.32349:32384](∅→∅),[7.32349]→[7.32349:32384](∅→∅),[7.32384]→[7.6631:6672](∅→∅),[7.6672]→[7.8022:8067](∅→∅),[7.8022]→[7.8022:8067](∅→∅),[7.8112]→[7.8112:8370](∅→∅),[7.8405]→[7.8405:8436](∅→∅),[7.8436]→[7.1609:1682](∅→∅),[7.1682]→[7.8531:8806](∅→∅),[7.8531]→[7.8531:8806](∅→∅),[7.8806]→[7.6673:6905](∅→∅),[7.6905]→[7.120:443](∅→∅),[7.478]→[7.478:509](∅→∅),[7.509]→[7.1683:1756](∅→∅),[7.1756]→[7.604:908](∅→∅),[7.604]→[7.604:908](∅→∅),[7.908]→[7.9170:9175](∅→∅),[7.6905]→[7.9170:9175](∅→∅),[7.9170]→[7.9170:9175](∅→∅),[7.9175]→[7.909:1297](∅→∅),[7.1332]→[7.1332:1374](∅→∅),[7.1374]→[7.7:33](∅→∅),[7.33]→[7.1374:2356](∅→∅),[7.1374]→[7.1374:2356](∅→∅),[7.2391]→[7.2391:2939](∅→∅),[7.2939]→[7.9175:9225](∅→∅),[7.9175]→[7.9175:9225](∅→∅),[7.9280]→[7.9280:9538](∅→∅),[7.9573]→[7.9573:9604](∅→∅),[7.9604]→[7.1757:1830](∅→∅),[7.1830]→[7.9699:10213](∅→∅),[7.9699]→[7.9699:10213](∅→∅),[7.10213]→[7.6906:7138](∅→∅),[7.7138]→[7.10617:10683](∅→∅),[7.10617]→[7.10617:10683](∅→∅),[7.10749]→[7.10749:11039](∅→∅),[7.11074]→[7.11074:11105](∅→∅),[7.11105]→[7.1831:1904](∅→∅),[7.1904]→[7.11200:12097](∅→∅),[7.11200]→[7.11200:12097](∅→∅),[7.12097]→[7.7139:7371](∅→∅),[7.7371]→[7.2940:3260](∅→∅),[7.3295]→[7.3295:3367](∅→∅),[7.3367]→[7.1610:1662](∅→∅),[7.1662]→[7.3414:3735](∅→∅),[7.3414]→[7.3414:3735](∅→∅),[7.3735]→[7.33196:33201](∅→∅),[7.7371]→[7.33196:33201](∅→∅),[7.12545]→[7.33196:33201](∅→∅),[7.33196]→[7.33196:33201](∅→∅),[7.33201]→[7.12546:12584](∅→∅),[7.12627]→[7.12627:12935](∅→∅),[7.12970]→[7.12970:13066](∅→∅),[7.13066]→[7.1663:1715](∅→∅),[7.1715]→[7.13113:13127](∅→∅),[7.13113]→[7.13113:13127](∅→∅),[7.13127]→[7.7372:7423](∅→∅),[7.7423]→[7.13203:13208](∅→∅),[7.13203]→[7.13203:13208](∅→∅),[7.13208]→[7.33201:33226](∅→∅),[7.33201]→[7.33201:33226](∅→∅),[7.33256]→[7.33256:33515](∅→∅),[7.33550]→[7.33550:33654](∅→∅),[7.33654]→[7.7424:7474](∅→∅),[7.7474]→[7.33722:33757](∅→∅),[7.33722]→[7.33722:33757](∅→∅),[7.33757]→[7.7475:7525](∅→∅),[7.7525]→[7.33825:33877](∅→∅),[7.33825]→[7.33825:33877](∅→∅),[7.33877]→[7.1716:1780](∅→∅),[7.1780]→[7.7526:7636](∅→∅),[7.33929]→[7.7526:7636](∅→∅),[7.7636]→[7.34075:34098](∅→∅),[7.34075]→[7.34075:34098](∅→∅),[7.34098]→[7.7637:7678](∅→∅),[7.7678]→[7.34157:34192](∅→∅),[7.34157]→[7.34157:34192](∅→∅),[7.34192]→[7.7679:7720](∅→∅),[7.7720]→[7.2117:2162](∅→∅),[7.34251]→[7.2117:2162](∅→∅),[7.2207]→[7.2207:2702](∅→∅),[7.2702]→[7.7721:7789](∅→∅),[7.7789]→[7.2803:2889](∅→∅),[7.2803]→[7.2803:2889](∅→∅),[7.2924]→[7.2924:3224](∅→∅),[7.3224]→[7.7790:7840](∅→∅),[7.7840]→[7.3307:3464](∅→∅),[7.3307]→[7.3307:3464](∅→∅),[7.3464]→[7.1781:1845](∅→∅),[7.1845]→[7.7841:7951](∅→∅),[7.3516]→[7.7841:7951](∅→∅),[7.7951]→[7.3692:3732](∅→∅),[7.3692]→[7.3692:3732](∅→∅),[7.3732]→[7.7952:7993](∅→∅),[7.3806]→[7.34251:34325](∅→∅),[7.7993]→[7.34251:34325](∅→∅),[7.34251]→[7.34251:34325](∅→∅),[7.34399]→[7.34399:34774](∅→∅),[7.34809]→[7.34809:34864](∅→∅),[7.34864]→[7.7994:8045](∅→∅),[7.8045]→[7.34977:35012](∅→∅),[7.34977]→[7.34977:35012](∅→∅),[7.35012]→[7.8046:8097](∅→∅),[7.8097]→[7.35125:35160](∅→∅),[7.35125]→[7.35125:35160](∅→∅),[7.35160]→[7.8098:8149](∅→∅),[7.8149]→[7.35273:35336](∅→∅),[7.35273]→[7.35273:35336](∅→∅),[7.35336]→[7.1846:1910](∅→∅),[7.1910]→[7.8150:8276](∅→∅),[7.35388]→[7.8150:8276](∅→∅),[7.8276]→[7.35638:35661](∅→∅),[7.35638]→[7.35638:35661](∅→∅),[7.35661]→[7.8277:8319](∅→∅),[7.8319]→[7.35765:35800](∅→∅),[7.35765]→[7.35765:35800](∅→∅),[7.35800]→[7.8320:8362](∅→∅),[7.8362]→[7.35904:35939](∅→∅),[7.35904]→[7.35904:35939](∅→∅),[7.35939]→[7.7:325](∅→∅),[7.46]→[7.36043:36088](∅→∅),[7.325]→[7.36043:36088](∅→∅),[7.8405]→[7.36043:36088](∅→∅),[7.36043]→[7.36043:36088](∅→∅),[7.36133]→[7.36133:36454](∅→∅),[7.36489]→[7.36489:36545](∅→∅),[7.36545]→[7.1911:1975](∅→∅),[7.1975]→[7.8406:8532](∅→∅),[7.36597]→[7.8406:8532](∅→∅),[7.8532]→[7.36789:36834](∅→∅),[7.36789]→[7.36789:36834](∅→∅),[7.36879]→[7.36879:37145](∅→∅),[7.37180]→[7.37180:37286](∅→∅),[7.37286]→[7.8533:8583](∅→∅),[7.8583]→[7.37369:37404](∅→∅),[7.37369]→[7.37369:37404](∅→∅),[7.37404]→[7.8584:8634](∅→∅),[7.8634]→[7.37487:37522](∅→∅),[7.37487]→[7.37487:37522](∅→∅),[7.37522]→[7.8635:8685](∅→∅),[7.8685]→[7.37605:37672](∅→∅),[7.37605]→[7.37605:37672](∅→∅),[7.37672]→[7.1976:2032](∅→∅),[7.2032]→[7.8686:8796](∅→∅),[7.37720]→[7.8686:8796](∅→∅),[7.8796]→[7.37896:37948](∅→∅),[7.37896]→[7.37896:37948](∅→∅),[7.37948]→[7.8797:8838](∅→∅),[7.8838]→[7.38022:38057](∅→∅),[7.38022]→[7.38022:38057](∅→∅),[7.38057]→[7.8839:8880](∅→∅),[7.8880]→[7.38131:38166](∅→∅),[7.38131]→[7.38131:38166](∅→∅),[7.38166]→[7.8881:8922](∅→∅),[7.8922]→[7.63:596](∅→∅),[7.631]→[7.631:1065](∅→∅),[7.1065]→[7.2033:2089](∅→∅),[7.2089]→[7.1113:1164](∅→∅),[7.1113]→[7.1113:1164](∅→∅),[7.1164]→[7.38240:38297](∅→∅),[7.8922]→[7.38240:38297](∅→∅),[7.38240]→[7.38240:38297](∅→∅),[7.38354]→[7.38354:38690](∅→∅),[7.38725]→[7.38725:38780](∅→∅),[7.38780]→[7.8923:8973](∅→∅),[7.8973]→[7.38875:38910](∅→∅),[7.38875]→[7.38875:38910](∅→∅),[7.38910]→[7.8974:9024](∅→∅),[7.9024]→[7.39005:39040](∅→∅),[7.39005]→[7.39005:39040](∅→∅),[7.39040]→[7.9025:9075](∅→∅),[7.9075]→[7.39135:39205](∅→∅),[7.39135]→[7.39135:39205](∅→∅),[7.39205]→[7.2090:2146](∅→∅),[7.2146]→[7.9076:9186](∅→∅),[7.39253]→[7.9076:9186](∅→∅),[7.9186]→[7.39453:39476](∅→∅),[7.39453]→[7.39453:39476](∅→∅),[7.39476]→[7.9187:9228](∅→∅),[7.9228]→[7.39562:39597](∅→∅),[7.39562]→[7.39562:39597](∅→∅),[7.39597]→[7.9229:9270](∅→∅),[7.9270]→[7.39683:39718](∅→∅),[7.39683]→[7.39683:39718](∅→∅),[7.39718]→[7.9271:9312](∅→∅),[7.9312]→[7.39804:39868](∅→∅),[7.39804]→[7.39804:39868](∅→∅),[7.39932]→[7.39932:40289](∅→∅),[7.40324]→[7.40324:40379](∅→∅),[7.40379]→[7.9313:9363](∅→∅),[7.9363]→[7.40481:40516](∅→∅),[7.40481]→[7.40481:40516](∅→∅),[7.40516]→[7.9364:9414](∅→∅),[7.9414]→[7.40618:40653](∅→∅),[7.40618]→[7.40618:40653](∅→∅),[7.40653]→[7.9415:9513](∅→∅),[7.9513]→[7.40803:40873](∅→∅),[7.40803]→[7.40803:40873](∅→∅),[7.40873]→[7.2147:2203](∅→∅),[7.2203]→[7.9514:9683](∅→∅),[7.40921]→[7.9514:9683](∅→∅),[7.9683]→[7.41246:41269](∅→∅),[7.41246]→[7.41246:41269](∅→∅),[7.41269]→[7.9684:9725](∅→∅),[7.9725]→[7.41362:41397](∅→∅),[7.41362]→[7.41362:41397](∅→∅),[7.41397]→[7.9726:9768](∅→∅),[7.9768]→[7.41491:41526](∅→∅),[7.41491]→[7.41491:41526](∅→∅),[7.41526]→[7.9769:9810](∅→∅),[7.9810]→[7.41619:41711](∅→∅),[7.41619]→[7.41619:41711](∅→∅),[7.41803]→[7.41803:42159](∅→∅),[7.42194]→[7.42194:42249](∅→∅),[7.42249]→[7.9811:9861](∅→∅),[7.9861]→[7.42379:42414](∅→∅),[7.42379]→[7.42379:42414](∅→∅),[7.42414]→[7.9862:9912](∅→∅),[7.9912]→[7.42544:42579](∅→∅),[7.42544]→[7.42544:42579](∅→∅),[7.42579]→[7.9913:9964](∅→∅),[7.9964]→[7.42710:42780](∅→∅),[7.42710]→[7.42710:42780](∅→∅),[7.42780]→[7.2204:2260](∅→∅),[7.2260]→[7.9965:10134](∅→∅),[7.42828]→[7.9965:10134](∅→∅),[7.10134]→[7.43237:43260](∅→∅),[7.43237]→[7.43237:43260](∅→∅),[7.43260]→[7.10135:10176](∅→∅),[7.10176]→[7.43381:43416](∅→∅),[7.43381]→[7.43381:43416](∅→∅),[7.43416]→[7.10177:10219](∅→∅),[7.10219]→[7.43538:43573](∅→∅),[7.43538]→[7.43538:43573](∅→∅),[7.43573]→[7.10220:10260](∅→∅),[7.10260]→[7.43693:43698](∅→∅),[7.43693]→[7.43693:43698](∅→∅),[7.43698]→[7.3134:3208](∅→∅),[7.3287]→[7.43853:44139](∅→∅),[7.43853]→[7.43853:44139](∅→∅),[7.44174]→[7.44174:44229](∅→∅),[7.44229]→[7.10261:10311](∅→∅),[7.3405]→[7.44347:44382](∅→∅),[7.10311]→[7.44347:44382](∅→∅),[7.44347]→[7.44347:44382](∅→∅),[7.44382]→[7.10312:10362](∅→∅),[7.3523]→[7.44500:44535](∅→∅),[7.10362]→[7.44500:44535](∅→∅),[7.44500]→[7.44500:44535](∅→∅),[7.44535]→[7.10363:10414](∅→∅),[7.3642]→[7.44654:44730](∅→∅),[7.10414]→[7.44654:44730](∅→∅),[7.44654]→[7.44654:44730](∅→∅),[7.44730]→[7.2261:2325](∅→∅),[7.2325]→[7.10415:10614](∅→∅),[7.44782]→[7.10415:10614](∅→∅),[7.4043]→[7.45185:45287](∅→∅),[7.10614]→[7.45185:45287](∅→∅),[7.45185]→[7.45185:45287](∅→∅),[7.45287]→[7.2326:2382](∅→∅),[7.2382]→[7.10615:10784](∅→∅),[7.45335]→[7.10615:10784](∅→∅),[7.4414]→[7.45708:45731](∅→∅),[7.10784]→[7.45708:45731](∅→∅),[7.45708]→[7.45708:45731](∅→∅),[7.45731]→[7.10785:10827](∅→∅),[7.4524]→[7.45841:45876](∅→∅),[7.10827]→[7.45841:45876](∅→∅),[7.45841]→[7.45841:45876](∅→∅),[7.45876]→[7.10828:10868](∅→∅),[7.4632]→[7.45984:46019](∅→∅),[7.10868]→[7.45984:46019](∅→∅),[7.45984]→[7.45984:46019](∅→∅),[7.46019]→[7.10869:10910](∅→∅),[7.4741]→[7.46128:46171](∅→∅),[7.10910]→[7.46128:46171](∅→∅),[7.46128]→[7.46128:46171](∅→∅),[7.46214]→[7.46214:46546](∅→∅),[7.46581]→[7.46581:46636](∅→∅),[7.46636]→[7.10911:10961](∅→∅),[7.10961]→[7.46717:46752](∅→∅),[7.46717]→[7.46717:46752](∅→∅),[7.46752]→[7.10962:11012](∅→∅),[7.11012]→[7.46833:46868](∅→∅),[7.46833]→[7.46833:46868](∅→∅),[7.46868]→[7.11013:11063](∅→∅),[7.11063]→[7.46949:47011](∅→∅),[7.46949]→[7.46949:47011](∅→∅),[7.47011]→[7.2383:2435](∅→∅),[7.2435]→[7.11064:11174](∅→∅),[7.47057]→[7.11064:11174](∅→∅),[7.11174]→[7.47229:47281](∅→∅),[7.47229]→[7.47229:47281](∅→∅),[7.47281]→[7.11175:11216](∅→∅),[7.11216]→[7.47353:47388](∅→∅),[7.47353]→[7.47353:47388](∅→∅),[7.47388]→[7.11217:11258](∅→∅),[7.11258]→[7.47460:47495](∅→∅),[7.47460]→[7.47460:47495](∅→∅),[7.47495]→[7.11259:11300](∅→∅),[7.11300]→[7.47567:47572](∅→∅),[7.47567]→[7.47567:47572](∅→∅),[7.47572]→[7.1165:1691](∅→∅),[7.1726]→[7.1726:2156](∅→∅),[7.2156]→[7.2436:2488](∅→∅),[7.2488]→[7.2202:2258](∅→∅),[7.2202]→[7.2202:2258](∅→∅),[7.2258]→[7.47572:47620](∅→∅),[7.47572]→[7.47572:47620](∅→∅),[7.47673]→[7.47673:47994](∅→∅),[7.48029]→[7.48029:48084](∅→∅),[7.48084]→[7.11301:11351](∅→∅),[7.11351]→[7.48175:48210](∅→∅),[7.48175]→[7.48175:48210](∅→∅),[7.48210]→[7.11352:11402](∅→∅),[7.11402]→[7.48301:48336](∅→∅),[7.48301]→[7.48301:48336](∅→∅),[7.48336]→[7.11403:11453](∅→∅),[7.11453]→[7.48427:48493](∅→∅),[7.48427]→[7.48427:48493](∅→∅),[7.48493]→[7.2489:2541](∅→∅),[7.2541]→[7.11454:11564](∅→∅),[7.48539]→[7.11454:11564](∅→∅),[7.11564]→[7.48731:48754](∅→∅),[7.48731]→[7.48731:48754](∅→∅),[7.48754]→[7.11565:11606](∅→∅),[7.11606]→[7.48836:48871](∅→∅),[7.48836]→[7.48836:48871](∅→∅),[7.48871]→[7.11607:11648](∅→∅),[7.11648]→[7.48953:48988](∅→∅),[7.48953]→[7.48953:48988](∅→∅),[7.48988]→[7.11649:11690](∅→∅),[7.11690]→[7.6:427](∅→∅),[7.462]→[7.462:813](∅→∅),[7.813]→[7.2542:2594](∅→∅),[7.2594]→[7.859:969](∅→∅),[7.859]→[7.859:969](∅→∅),[7.969]→[7.49070:49130](∅→∅),[7.11690]→[7.49070:49130](∅→∅),[7.49070]→[7.49070:49130](∅→∅),[7.49190]→[7.49190:49539](∅→∅),[7.49574]→[7.49574:49629](∅→∅),[7.49629]→[7.11691:11741](∅→∅),[7.11741]→[7.49727:49762](∅→∅),[7.49727]→[7.49727:49762](∅→∅),[7.49762]→[7.11742:11792](∅→∅),[7.11792]→[7.49860:49935](∅→∅),[7.49860]→[7.49860:49935](∅→∅),[7.49935]→[7.2595:2647](∅→∅),[7.2647]→[7.49981:50004](∅→∅),[7.49981]→[7.49981:50004](∅→∅),[7.50004]→[7.11793:11835](∅→∅),[7.11835]→[7.50094:50129](∅→∅),[7.50094]→[7.50094:50129](∅→∅),[7.50129]→[7.11836:11877](∅→∅),[7.11877]→[7.50218:50253](∅→∅),[7.50218]→[7.50218:50253](∅→∅),[7.50253]→[7.11878:12155](∅→∅),[7.12155]→[7.50761:50823](∅→∅),[7.50761]→[7.50761:50823](∅→∅),[7.50885]→[7.50885:51223](∅→∅),[7.51258]→[7.51258:51313](∅→∅),[7.51313]→[7.12156:12206](∅→∅),[7.12206]→[7.51413:51448](∅→∅),[7.51413]→[7.51413:51448](∅→∅),[7.51448]→[7.12207:12257](∅→∅),[7.12257]→[7.51548:51583](∅→∅),[7.51548]→[7.51548:51583](∅→∅),[7.51583]→[7.12258:12308](∅→∅),[7.12308]→[7.51683:51775](∅→∅),[7.51683]→[7.51683:51775](∅→∅),[7.51775]→[7.2648:2700](∅→∅),[7.2700]→[7.51821:51844](∅→∅),[7.51821]→[7.51821:51844](∅→∅),[7.51844]→[7.12309:12350](∅→∅),[7.12350]→[7.51935:51970](∅→∅),[7.51935]→[7.51935:51970](∅→∅),[7.51970]→[7.12351:12392](∅→∅),[7.12392]→[7.52061:52096](∅→∅),[7.52061]→[7.52061:52096](∅→∅),[7.52096]→[7.12393:12670](∅→∅),[7.12670]→[7.52614:52669](∅→∅),[7.52614]→[7.52614:52669](∅→∅),[7.52724]→[7.52724:53079](∅→∅),[7.53114]→[7.53114:53169](∅→∅),[7.53169]→[7.12671:12721](∅→∅),[7.12721]→[7.53262:53297](∅→∅),[7.53262]→[7.53262:53297](∅→∅),[7.53297]→[7.12722:12772](∅→∅),[7.12772]→[7.53390:53425](∅→∅),[7.53390]→[7.53390:53425](∅→∅),[7.53425]→[7.12773:12823](∅→∅),[7.12823]→[7.53518:53584](∅→∅),[7.53518]→[7.53518:53584](∅→∅),[7.53584]→[7.2701:2753](∅→∅),[7.2753]→[7.12824:12934](∅→∅),[7.53630]→[7.12824:12934](∅→∅),[7.12934]→[7.53826:53906](∅→∅),[7.53826]→[7.53826:53906](∅→∅),[7.53906]→[7.12935:12976](∅→∅),[7.12976]→[7.53990:54025](∅→∅),[7.53990]→[7.53990:54025](∅→∅),[7.54025]→[7.12977:13018](∅→∅),[7.13018]→[7.54109:54137](∅→∅),[7.54109]→[7.54109:54137](∅→∅),[7.54165]→[7.54165:54424](∅→∅),[7.54459]→[7.54459:54562](∅→∅),[7.54562]→[7.13019:13069](∅→∅),[7.13069]→[7.54628:54663](∅→∅),[7.54628]→[7.54628:54663](∅→∅),[7.54663]→[7.13070:13120](∅→∅),[7.13120]→[7.54729:54777](∅→∅),[7.54729]→[7.54729:54777](∅→∅),[7.54777]→[7.2754:2814](∅→∅),[7.2814]→[7.13121:13231](∅→∅),[7.54827]→[7.13121:13231](∅→∅),[7.13231]→[7.54969:54992](∅→∅),[7.54969]→[7.54969:54992](∅→∅),[7.54992]→[7.13232:13273](∅→∅),[7.13273]→[7.55049:55084](∅→∅),[7.55049]→[7.55049:55084](∅→∅),[7.55084]→[7.13274:13315](∅→∅),[7.13315]→[7.55141:55195](∅→∅),[7.55141]→[7.55141:55195](∅→∅),[7.55249]→[7.55249:55606](∅→∅),[7.55641]→[7.55641:55696](∅→∅),[7.55696]→[7.13316:13366](∅→∅),[7.13366]→[7.55788:55823](∅→∅),[7.55788]→[7.55788:55823](∅→∅),[7.55823]→[7.13367:13417](∅→∅),[7.13417]→[7.55915:55950](∅→∅),[7.55915]→[7.55915:55950](∅→∅),[7.55950]→[7.13418:13515](∅→∅),[7.13515]→[7.56089:56153](∅→∅),[7.56089]→[7.56089:56153](∅→∅),[7.56153]→[7.2815:2875](∅→∅),[7.2875]→[7.13516:13685](∅→∅),[7.56203]→[7.13516:13685](∅→∅),[7.13685]→[7.56498:56521](∅→∅),[7.56498]→[7.56498:56521](∅→∅),[7.56521]→[7.13686:13728](∅→∅),[7.13728]→[7.56605:56640](∅→∅),[7.56605]→[7.56605:56640](∅→∅),[7.56640]→[7.13729:13770](∅→∅),[7.13770]→[7.56723:56758](∅→∅),[7.56723]→[7.56723:56758](∅→∅),[7.56758]→[7.13771:13812](∅→∅),[7.13812]→[7.56841:56904](∅→∅),[7.56841]→[7.56841:56904](∅→∅),[7.56967]→[7.56967:57342](∅→∅),[7.57377]→[7.57377:57432](∅→∅),[7.57432]→[7.13813:13863](∅→∅),[7.13863]→[7.57533:57568](∅→∅),[7.57533]→[7.57533:57568](∅→∅),[7.57568]→[7.13864:13961](∅→∅),[7.13961]→[7.57716:57780](∅→∅),[7.57716]→[7.57716:57780](∅→∅),[7.57780]→[7.2876:2936](∅→∅),[7.2936]→[7.13962:14131](∅→∅),[7.57830]→[7.13962:14131](∅→∅),[7.14131]→[7.58152:58175](∅→∅),[7.58152]→[7.58152:58175](∅→∅),[7.58175]→[7.14132:14174](∅→∅),[7.14174]→[7.58268:58303](∅→∅),[7.58268]→[7.58268:58303](∅→∅),[7.58303]→[7.14175:14216](∅→∅),[7.14216]→[7.58395:58430](∅→∅),[7.58395]→[7.58395:58430](∅→∅),[7.58430]→[7.14217:14259](∅→∅),[7.14259]→[7.58523:58578](∅→∅),[7.58523]→[7.58523:58578](∅→∅),[7.58633]→[7.58633:58968](∅→∅),[7.59003]→[7.59003:59058](∅→∅),[7.59058]→[7.14260:14310](∅→∅),[7.14310]→[7.59151:59186](∅→∅),[7.59151]→[7.59151:59186](∅→∅),[7.59186]→[7.14311:14361](∅→∅),[7.14361]→[7.59279:59314](∅→∅),[7.59279]→[7.59279:59314](∅→∅),[7.59314]→[7.14362:14412](∅→∅),[7.14412]→[7.59407:59464](∅→∅),[7.59407]→[7.59407:59464](∅→∅),[7.59464]→[7.2937:2997](∅→∅),[7.2997]→[7.14413:14582](∅→∅),[7.59514]→[7.14413:14582](∅→∅),[7.14582]→[7.59812:59835](∅→∅),[7.59812]→[7.59812:59835](∅→∅),[7.59835]→[7.14583:14624](∅→∅),[7.14624]→[7.59919:59954](∅→∅),[7.59919]→[7.59919:59954](∅→∅),[7.59954]→[7.14625:14664](∅→∅),[7.14664]→[7.60036:60071](∅→∅),[7.60036]→[7.60036:60071](∅→∅),[7.60071]→[7.14665:14705](∅→∅),[7.14705]→[7.60154:60236](∅→∅),[7.60154]→[7.60154:60236](∅→∅),[7.60318]→[7.60318:60645](∅→∅),[7.60680]→[7.60680:60735](∅→∅),[7.60735]→[7.14706:14756](∅→∅),[7.14756]→[7.60855:60920](∅→∅),[7.60855]→[7.60855:60920](∅→∅),[7.60920]→[7.2998:3058](∅→∅),[7.3058]→[7.14757:14926](∅→∅),[7.60970]→[7.14757:14926](∅→∅),[7.14926]→[7.61349:61372](∅→∅),[7.61349]→[7.61349:61372](∅→∅),[7.61372]→[7.14927:14966](∅→∅),[7.14966]→[7.61481:61516](∅→∅),[7.61481]→[7.61481:61516](∅→∅),[7.61516]→[7.14967:15007](∅→∅),[7.15007]→[7.61626:61717](∅→∅),[7.61626]→[7.61626:61717](∅→∅),[7.61808]→[7.61808:62123](∅→∅),[7.62158]→[7.62158:62258](∅→∅),[7.62258]→[7.4742:4789](∅→∅),[7.4789]→[7.15008:15177](∅→∅),[7.15177]→[7.62710:62739](∅→∅),[7.62710]→[7.62710:62739](∅→∅),[7.62739]→[7.15178:15217](∅→∅),[7.15217]→[7.62857:62913](∅→∅),[7.62857]→[7.62857:62913](∅→∅),[7.62969]→[7.62969:63304](∅→∅),[7.63339]→[7.63339:63394](∅→∅),[7.63394]→[7.15218:15268](∅→∅),[7.15268]→[7.63488:63523](∅→∅),[7.63488]→[7.63488:63523](∅→∅),[7.63523]→[7.15269:15319](∅→∅),[7.15319]→[7.63617:63652](∅→∅),[7.63617]→[7.63617:63652](∅→∅),[7.63652]→[7.15320:15370](∅→∅),[7.15370]→[7.63746:63817](∅→∅),[7.63746]→[7.63746:63817](∅→∅),[7.63817]→[7.4790:4931](∅→∅),[7.4931]→[7.15371:15540](∅→∅),[7.15540]→[7.64256:64279](∅→∅),[7.64256]→[7.64256:64279](∅→∅),[7.64279]→[7.15541:15582](∅→∅),[7.15582]→[7.64364:64399](∅→∅),[7.64364]→[7.64364:64399](∅→∅),[7.64399]→[7.15583:15625](∅→∅),[7.15625]→[7.64485:64520](∅→∅),[7.64485]→[7.64485:64520](∅→∅),[7.64520]→[7.15626:15666](∅→∅),[7.15666]→[7.64604:64608](∅→∅),[7.64604]→[7.64604:64608](∅→∅)
-- Arguably this should be called source_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 source_text_tests.lua at line 12[7.64609]→[7.64609:64663](∅→∅),[7.64722]→[7.64722:65030](∅→∅),[7.65065]→[7.65065:65193](∅→∅),[7.65193]→[7.15667:15717](∅→∅),[7.15717]→[7.65290:65325](∅→∅),[7.65290]→[7.65290:65325](∅→∅),[7.65325]→[7.15718:15768](∅→∅),[7.15768]→[7.65422:65499](∅→∅),[7.65422]→[7.65422:65499](∅→∅),[7.65499]→[7.3059:3115](∅→∅),[7.3115]→[7.65547:65570](∅→∅),[7.65547]→[7.65547:65570](∅→∅),[7.65570]→[7.15769:15811](∅→∅),[7.15811]→[7.65659:65694](∅→∅),[7.65659]→[7.65659:65694](∅→∅),[7.65694]→[7.15812:15853](∅→∅),[7.15853]→[7.65782:65817](∅→∅),[7.65782]→[7.65782:65817](∅→∅),[7.65817]→[7.15854:16131](∅→∅)
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('source_text_tests_love11.lua')elseif Major_version == 12 then-- not released/stable yetload_file_from_source_or_save_directory('source_text_tests_love12.lua') - edit in source_text_tests.lua at line 19[7.66324]→[7.66324:66382](∅→∅),[7.66444]→[7.66444:66760](∅→∅),[7.66795]→[7.66795:66932](∅→∅),[7.66932]→[7.16132:16182](∅→∅),[7.16182]→[7.67032:67067](∅→∅),[7.67032]→[7.67032:67067](∅→∅),[7.67067]→[7.16183:16233](∅→∅),[7.16233]→[7.67167:67202](∅→∅),[7.67167]→[7.67167:67202](∅→∅),[7.67202]→[7.16234:16332](∅→∅),[7.16332]→[7.67350:67421](∅→∅),[7.67350]→[7.67350:67421](∅→∅),[7.67421]→[7.1905:1963](∅→∅),[7.1963]→[7.16333:16502](∅→∅),[7.3175]→[7.16333:16502](∅→∅),[7.67470]→[7.16333:16502](∅→∅),[7.16502]→[7.67789:67812](∅→∅),[7.67789]→[7.67789:67812](∅→∅),[7.67812]→[7.16503:16544](∅→∅),[7.16544]→[7.67903:67938](∅→∅),[7.67903]→[7.67903:67938](∅→∅),[7.67938]→[7.16545:16587](∅→∅),[7.16587]→[7.68030:68065](∅→∅),[7.68030]→[7.68030:68065](∅→∅),[7.68065]→[7.16588:16629](∅→∅),[7.16629]→[7.68156:68209](∅→∅),[7.68156]→[7.68156:68209](∅→∅),[7.68262]→[7.68262:68570](∅→∅),[7.68605]→[7.68605:68733](∅→∅),[7.68733]→[7.16630:16680](∅→∅),[7.16680]→[7.68824:68859](∅→∅),[7.68824]→[7.68824:68859](∅→∅),[7.68859]→[7.16681:16731](∅→∅),[7.16731]→[7.68950:69017](∅→∅),[7.68950]→[7.68950:69017](∅→∅),[7.69017]→[7.3176:3232](∅→∅),[7.3232]→[7.69065:69088](∅→∅),[7.69065]→[7.69065:69088](∅→∅),[7.69088]→[7.16732:16774](∅→∅),[7.16774]→[7.69171:69206](∅→∅),[7.69171]→[7.69171:69206](∅→∅),[7.69206]→[7.16775:16816](∅→∅),[7.16816]→[7.69288:69323](∅→∅),[7.69288]→[7.69288:69323](∅→∅),[7.69323]→[7.16817:17094](∅→∅),[7.17094]→[7.69796:69850](∅→∅),[7.69796]→[7.69796:69850](∅→∅),[7.69904]→[7.69904:70220](∅→∅),[7.70255]→[7.70255:70392](∅→∅),[7.70392]→[7.17095:17145](∅→∅),[7.17145]→[7.70484:70519](∅→∅),[7.70484]→[7.70484:70519](∅→∅),[7.70519]→[7.17146:17196](∅→∅),[7.17196]→[7.70611:70646](∅→∅),[7.70611]→[7.70611:70646](∅→∅),[7.70646]→[7.17197:17295](∅→∅),[7.17295]→[7.70786:70845](∅→∅),[7.70786]→[7.70786:70845](∅→∅),[7.70845]→[7.3233:3287](∅→∅),[7.3287]→[7.17296:17465](∅→∅),[7.70892]→[7.17296:17465](∅→∅),[7.17465]→[7.71187:71210](∅→∅),[7.71187]→[7.71187:71210](∅→∅),[7.71210]→[7.17466:17507](∅→∅),[7.17507]→[7.71293:71328](∅→∅),[7.71293]→[7.71293:71328](∅→∅),[7.71328]→[7.17508:17550](∅→∅),[7.17550]→[7.71412:71447](∅→∅),[7.71412]→[7.71412:71447](∅→∅),[7.71447]→[7.17551:17592](∅→∅),[7.17592]→[7.71530:71646](∅→∅),[7.71530]→[7.71530:71646](∅→∅),[7.71716]→[7.71716:71991](∅→∅),[7.72026]→[7.72026:72081](∅→∅),[7.72081]→[7.17593:17653](∅→∅),[7.17653]→[7.72199:72234](∅→∅),[7.72199]→[7.72199:72234](∅→∅),[7.72234]→[7.17654:17714](∅→∅),[7.17714]→[7.72352:72387](∅→∅),[7.72352]→[7.72352:72387](∅→∅),[7.72387]→[7.17715:17766](∅→∅),[7.17766]→[7.72496:72557](∅→∅),[7.72496]→[7.72496:72557](∅→∅),[7.72557]→[7.4932:5073](∅→∅),[7.5073]→[7.17767:17822](∅→∅),[7.17822]→[7.72808:72831](∅→∅),[7.72808]→[7.72808:72831](∅→∅),[7.72831]→[7.17823:17883](∅→∅),[7.17883]→[7.72949:72984](∅→∅),[7.72949]→[7.72949:72984](∅→∅),[7.72984]→[7.17884:17944](∅→∅),[7.17944]→[7.73102:73137](∅→∅),[7.73102]→[7.73102:73137](∅→∅),[7.73137]→[7.17945:17996](∅→∅),[7.17996]→[7.73246:73329](∅→∅),[7.73246]→[7.73246:73329](∅→∅),[7.73329]→[7.326:441](∅→∅),[7.441]→[7.73442:73466](∅→∅),[7.13324]→[7.73442:73466](∅→∅),[7.73442]→[7.73442:73466](∅→∅),[7.73466]→[7.17997:18053](∅→∅),[7.18053]→[7.442:497](∅→∅),[7.497]→[7.73693:73738](∅→∅),[7.18108]→[7.73693:73738](∅→∅),[7.73693]→[7.73693:73738](∅→∅),[7.73783]→[7.73783:74104](∅→∅),[7.74139]→[7.74139:74194](∅→∅),[7.74194]→[7.18109:18159](∅→∅),[7.18159]→[7.74277:74312](∅→∅),[7.74277]→[7.74277:74312](∅→∅),[7.74312]→[7.18160:18210](∅→∅),[7.18210]→[7.74395:74430](∅→∅),[7.74395]→[7.74395:74430](∅→∅),[7.74430]→[7.18211:18261](∅→∅),[7.18261]→[7.74513:74576](∅→∅),[7.74513]→[7.74513:74576](∅→∅),[7.74576]→[7.3288:3354](∅→∅),[7.3354]→[7.18262:18372](∅→∅),[7.74629]→[7.18262:18372](∅→∅),[7.18372]→[7.74805:74828](∅→∅),[7.74805]→[7.74805:74828](∅→∅),[7.74828]→[7.18373:18417](∅→∅),[7.18417]→[7.74905:74940](∅→∅),[7.74905]→[7.74905:74940](∅→∅),[7.74940]→[7.18418:18459](∅→∅),[7.18459]→[7.75014:75049](∅→∅),[7.75014]→[7.75014:75049](∅→∅),[7.75049]→[7.18460:18501](∅→∅),[7.18501]→[7.75123:75180](∅→∅),[7.75123]→[7.75123:75180](∅→∅),[7.75237]→[7.75237:75586](∅→∅),[7.75621]→[7.75621:75676](∅→∅),[7.75676]→[7.18502:18552](∅→∅),[7.18552]→[7.75771:75806](∅→∅),[7.75771]→[7.75771:75806](∅→∅),[7.75806]→[7.18553:18603](∅→∅),[7.18603]→[7.75901:75971](∅→∅),[7.75901]→[7.75901:75971](∅→∅),[7.75971]→[7.3355:3421](∅→∅),[7.3421]→[7.76024:76047](∅→∅),[7.76024]→[7.76024:76047](∅→∅),[7.76047]→[7.18604:18646](∅→∅),[7.18646]→[7.76134:76169](∅→∅),[7.76134]→[7.76134:76169](∅→∅),[7.76169]→[7.18647:18687](∅→∅),[7.18687]→[7.76254:76289](∅→∅),[7.76254]→[7.76254:76289](∅→∅),[7.76289]→[7.18688:18965](∅→∅),[7.18965]→[7.76782:76832](∅→∅),[7.76782]→[7.76782:76832](∅→∅),[7.76882]→[7.76882:77198](∅→∅),[7.77198]→[7.3422:3488](∅→∅),[7.3488]→[7.18966:19024](∅→∅),[7.77251]→[7.18966:19024](∅→∅),[7.19024]→[7.77341:77346](∅→∅),[7.77341]→[7.77341:77346](∅→∅),[7.77346]→[7.13325:13514](∅→∅),[7.13560]→[7.13560:14004](∅→∅),[7.14004]→[7.3489:3555](∅→∅),[7.3555]→[7.19025:19078](∅→∅),[7.14057]→[7.19025:19078](∅→∅),[7.19078]→[7.14144:14188](∅→∅),[7.14144]→[7.14144:14188](∅→∅),[7.14188]→[7.19079:19189](∅→∅),[7.19189]→[7.14366:14392](∅→∅),[7.14366]→[7.14366:14392](∅→∅),[7.14392]→[7.19190:19245](∅→∅),[7.19245]→[7.14481:14535](∅→∅),[7.14481]→[7.14481:14535](∅→∅),[7.14589]→[7.14589:14997](∅→∅),[7.14997]→[7.3556:3622](∅→∅),[7.3622]→[7.19246:19299](∅→∅),[7.15050]→[7.19246:19299](∅→∅),[7.19299]→[7.15145:15185](∅→∅),[7.15145]→[7.15145:15185](∅→∅),[7.15185]→[7.19300:19410](∅→∅),[7.19410]→[7.15379:15405](∅→∅),[7.15379]→[7.15379:15405](∅→∅),[7.15405]→[7.19411:19466](∅→∅),[7.19466]→[7.15502:15553](∅→∅),[7.15502]→[7.15502:15553](∅→∅),[7.15604]→[7.15604:16057](∅→∅),[7.16057]→[7.3623:3689](∅→∅),[7.3689]→[7.19467:19579](∅→∅),[7.16110]→[7.19467:19579](∅→∅),[7.19579]→[7.16300:16342](∅→∅),[7.16300]→[7.16300:16342](∅→∅),[7.16342]→[7.19580:19690](∅→∅),[7.19690]→[7.16530:16556](∅→∅),[7.16530]→[7.16530:16556](∅→∅),[7.16556]→[7.19691:19746](∅→∅),[7.19746]→[7.16650:16696](∅→∅),[7.16650]→[7.16650:16696](∅→∅),[7.16742]→[7.16742:17147](∅→∅),[7.17147]→[7.3690:3756](∅→∅),[7.3756]→[7.19747:19857](∅→∅),[7.17200]→[7.19747:19857](∅→∅),[7.19857]→[7.17382:17424](∅→∅),[7.17382]→[7.17382:17424](∅→∅),[7.17424]→[7.19858:19968](∅→∅),[7.19968]→[7.17606:17632](∅→∅),[7.17606]→[7.17606:17632](∅→∅),[7.17632]→[7.19969:20024](∅→∅),[7.20024]→[7.17723:17771](∅→∅),[7.17723]→[7.17723:17771](∅→∅),[7.17819]→[7.17819:18231](∅→∅),[7.18231]→[7.3757:3823](∅→∅),[7.3823]→[7.20025:20135](∅→∅),[7.18284]→[7.20025:20135](∅→∅),[7.20135]→[7.18466:18508](∅→∅),[7.18466]→[7.18466:18508](∅→∅),[7.18508]→[7.20136:20246](∅→∅),[7.20246]→[7.18690:18716](∅→∅),[7.18690]→[7.18690:18716](∅→∅),[7.18716]→[7.20247:20302](∅→∅),[7.20302]→[7.18807:18812](∅→∅),[7.18807]→[7.18807:18812](∅→∅),[7.18812]→[7.77346:77379](∅→∅),[7.77346]→[7.77346:77379](∅→∅),[7.77417]→[7.77417:77676](∅→∅),[7.77711]→[7.77711:77761](∅→∅),[7.77761]→[7.5074:5121](∅→∅),[7.5121]→[7.20303:20567](∅→∅),[7.19001]→[7.77987:78016](∅→∅),[7.20567]→[7.77987:78016](∅→∅),[7.77987]→[7.77987:78016](∅→∅),[7.78016]→[7.20568:20618](∅→∅),[7.20618]→[7.78092:78127](∅→∅),[7.78092]→[7.78092:78127](∅→∅),[7.78127]→[7.20619:20670](∅→∅),[7.20670]→[7.78204:78239](∅→∅),[7.78204]→[7.78204:78239](∅→∅),[7.78239]→[7.20671:20721](∅→∅),[7.20721]→[7.78315:78325](∅→∅),[7.78315]→[7.78315:78325](∅→∅),[7.78325]→[7.3824:3876](∅→∅),[7.3876]→[7.20722:20950](∅→∅),[7.78372]→[7.20722:20950](∅→∅),[7.19172]→[7.78534:78557](∅→∅),[7.20950]→[7.78534:78557](∅→∅),[7.78534]→[7.78534:78557](∅→∅),[7.78557]→[7.20951:20992](∅→∅),[7.20992]→[7.78624:78659](∅→∅),[7.78624]→[7.78624:78659](∅→∅),[7.78659]→[7.20993:21034](∅→∅),[7.21034]→[7.78726:78761](∅→∅),[7.78726]→[7.78726:78761](∅→∅),[7.78761]→[7.21035:21076](∅→∅),[7.21076]→[7.78828:78866](∅→∅),[7.78828]→[7.78828:78866](∅→∅),[7.78904]→[7.78904:79164](∅→∅),[7.79199]→[7.79199:79223](∅→∅),[7.79223]→[7.3877:3943](∅→∅),[7.3943]→[7.21077:21341](∅→∅),[7.79276]→[7.21077:21341](∅→∅),[7.19361]→[7.79456:79485](∅→∅),[7.21341]→[7.79456:79485](∅→∅),[7.79456]→[7.79456:79485](∅→∅),[7.79485]→[7.21342:21392](∅→∅),[7.21392]→[7.79561:79596](∅→∅),[7.79561]→[7.79561:79596](∅→∅),[7.79596]→[7.21393:21443](∅→∅),[7.21443]→[7.79672:79707](∅→∅),[7.79672]→[7.79672:79707](∅→∅),[7.79707]→[7.21444:21494](∅→∅),[7.21494]→[7.79783:79845](∅→∅),[7.79783]→[7.79783:79845](∅→∅),[7.79845]→[7.3944:3996](∅→∅),[7.3996]→[7.21495:21853](∅→∅),[7.79892]→[7.21495:21853](∅→∅),[7.19714]→[7.80054:80077](∅→∅),[7.21853]→[7.80054:80077](∅→∅),[7.80054]→[7.80054:80077](∅→∅),[7.80077]→[7.21854:21895](∅→∅),[7.21895]→[7.80144:80179](∅→∅),[7.80144]→[7.80144:80179](∅→∅),[7.80179]→[7.21896:21938](∅→∅),[7.21938]→[7.80247:80282](∅→∅),[7.80247]→[7.80247:80282](∅→∅),[7.80282]→[7.21939:21980](∅→∅),[7.21980]→[7.80349:80354](∅→∅),[7.80349]→[7.80349:80354](∅→∅),[7.80354]→[7.19715:19755](∅→∅),[7.19800]→[7.19800:20140](∅→∅),[7.20175]→[7.20175:20227](∅→∅),[7.20227]→[7.5122:5169](∅→∅),[7.5169]→[7.21981:22103](∅→∅),[7.22103]→[7.20461:20471](∅→∅),[7.20461]→[7.20461:20471](∅→∅),[7.20471]→[7.3997:4101](∅→∅),[7.4101]→[7.20565:20592](∅→∅),[7.20565]→[7.20565:20592](∅→∅),[7.20592]→[7.22104:22206](∅→∅),[7.22206]→[7.20760:20765](∅→∅),[7.20760]→[7.20760:20765](∅→∅),[7.20765]→[7.80354:80377](∅→∅),[7.80354]→[7.80354:80377](∅→∅),[7.80405]→[7.80405:80491](∅→∅),[7.80491]→[7.113:231](∅→∅),[7.231]→[7.80553:80671](∅→∅),[7.3881]→[7.80553:80671](∅→∅),[7.80553]→[7.80553:80671](∅→∅),[7.80706]→[7.80706:80757](∅→∅),[7.80757]→[7.4102:4154](∅→∅),[7.4154]→[7.5170:5217](∅→∅),[7.80804]→[7.5170:5217](∅→∅),[7.5217]→[7.4155:4215](∅→∅),[7.4215]→[7.22207:22321](∅→∅),[7.80900]→[7.22207:22321](∅→∅),[7.22321]→[7.81046:81184](∅→∅),[7.81046]→[7.81046:81184](∅→∅),[7.81184]→[7.4216:4268](∅→∅),[7.4268]→[7.5218:5266](∅→∅),[7.81231]→[7.5218:5266](∅→∅),[7.5266]→[7.4269:4385](∅→∅),[7.4385]→[7.22322:22380](∅→∅),[7.81376]→[7.22322:22380](∅→∅),[7.22380]→[7.232:288](∅→∅),[7.288]→[7.81522:81558](∅→∅),[7.22436]→[7.81522:81558](∅→∅),[7.81522]→[7.81522:81558](∅→∅),[7.81594]→[7.81594:81680](∅→∅),[7.81680]→[7.289:367](∅→∅),[7.367]→[7.81725:81757](∅→∅),[7.81725]→[7.81725:81757](∅→∅),[7.81757]→[7.368:409](∅→∅),[7.409]→[7.81798:81843](∅→∅),[7.81798]→[7.81798:81843](∅→∅),[7.81878]→[7.81878:81929](∅→∅),[7.81929]→[7.4386:4438](∅→∅),[7.4438]→[7.5267:5314](∅→∅),[7.81976]→[7.5267:5314](∅→∅),[7.5314]→[7.82022:82058](∅→∅),[7.82022]→[7.82022:82058](∅→∅),[7.82058]→[7.4439:4491](∅→∅),[7.4491]→[7.22437:22495](∅→∅),[7.82104]→[7.22437:22495](∅→∅),[7.22495]→[7.410:466](∅→∅),[7.466]→[7.82266:82299](∅→∅),[7.22551]→[7.82266:82299](∅→∅),[7.82266]→[7.82266:82299](∅→∅),[7.82332]→[7.82332:82418](∅→∅),[7.82418]→[7.467:559](∅→∅),[7.559]→[7.82459:82491](∅→∅),[7.82459]→[7.82459:82491](∅→∅),[7.82491]→[7.560:601](∅→∅),[7.601]→[7.82532:82577](∅→∅),[7.82532]→[7.82532:82577](∅→∅),[7.82612]→[7.82612:82663](∅→∅),[7.82663]→[7.4492:4544](∅→∅),[7.4544]→[7.5315:5362](∅→∅),[7.82710]→[7.5315:5362](∅→∅),[7.5362]→[7.4545:4605](∅→∅),[7.4605]→[7.82806:82824](∅→∅),[7.82806]→[7.82806:82824](∅→∅),[7.82824]→[7.22552:22610](∅→∅),[7.22610]→[7.602:658](∅→∅),[7.658]→[7.82980:83021](∅→∅),[7.22666]→[7.82980:83021](∅→∅),[7.82980]→[7.82980:83021](∅→∅),[7.83062]→[7.83062:83148](∅→∅),[7.83148]→[7.659:734](∅→∅),[7.734]→[7.83193:83311](∅→∅),[7.83193]→[7.83193:83311](∅→∅),[7.83346]→[7.83346:83405](∅→∅),[7.83405]→[7.4606:4658](∅→∅),[7.4658]→[7.5363:5410](∅→∅),[7.83452]→[7.5363:5410](∅→∅),[7.5410]→[7.4659:4711](∅→∅),[7.4711]→[7.83544:83562](∅→∅),[7.83544]→[7.83544:83562](∅→∅),[7.83562]→[7.22667:22725](∅→∅),[7.22725]→[7.735:791](∅→∅),[7.791]→[7.7:995](∅→∅),[7.791]→[7.83734:83738](∅→∅),[7.995]→[7.83734:83738](∅→∅),[7.22781]→[7.83734:83738](∅→∅),[7.83734]→[7.83734:83738](∅→∅)
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 - edit in source_text.lua at line 71[7.1470][116.238]
--? -- render screen line--? App.color(Text_color)--? App.screen.print(screen_line, State.left,y) - replacement in source_text.lua at line 122[7.2428]→[7.2428:2472](∅→∅),[7.2472]→[7.729:778](∅→∅),[7.778]→[7.2511:2815](∅→∅),[7.2511]→[7.2511:2815](∅→∅),[7.2815]→[7.779:862](∅→∅),[7.862]→[7.8:200](∅→∅),[7.2886]→[7.8:200](∅→∅),[7.200]→[7.2956:3180](∅→∅),[7.2956]→[7.2956:3180](∅→∅),[7.3180]→[7.863:910](∅→∅),[7.910]→[7.3217:3264](∅→∅),[7.3217]→[7.3217:3264](∅→∅)
for frag in line.data:gmatch('%S*%s*') dolocal frag_width = State.font:getWidth(frag)--? print('-- frag:', frag, pos, x, frag_width, State.width)while x + frag_width > State.width do--? print('frag:', frag, pos, x, frag_width, State.width)if x < 0.8 * State.width then-- long word; chop it at some letter-- We're not going to reimplement TeX here.local bpos = Text.nearest_pos_less_than(State.font, frag, State.width - x)if x == 0 and bpos == 0 thenassert(false, ("Infinite loop while line-wrapping. Editor is %dpx wide; window is %dpx wide"):format(State.width, App.screen.width))endpos = pos + bposlocal boffset = Text.offset(frag, bpos+1) -- byte _after_ bposfrag = string.sub(frag, boffset)--? if bpos > 0 then--? print('after chop:', frag)--? endfrag_width = State.font:getWidth(frag)end--? print('screen line:', pos)for pos,char in utf8chars(line.data) dolocal w = State.font:getWidth(char)if Text.should_word_wrap(State, line.data, pos, char, x)or x+w > State.width -- truncate within a wordthen - replacement in source_text.lua at line 128
x = 0 -- new screen linex = 0 - replacement in source_text.lua at line 130
x = x + frag_widthpos = pos + utf8.len(frag)x = x + wendend-- Check whether to word-wrap line at pos which will be positioned at x.---- We wrap at the start of a word (non-space just after space) if the word-- (non-spaces followed by spaces) wouldn't fit in the rest of the line.---- x lies between 0 and editor.width.---- Postcondition:-- Current line is not wider than editor.width---- Desired properties in priority order:-- Next line doesn't start with whitespace-- Current line ends with whitespace (a.k.a. word wrap)-- Current line is close to full-- None of these is guaranteed. But we should never satisfy a lower priority-- before a higher one.function Text.should_word_wrap(editor, line, pos, char, x)if char:match('%s') then return false endif pos == 1 then return false endif Text.match(line, pos-1, '%S') then return false endlocal offset = Text.offset(line, pos)-- most of the time a word is printable chars + whitespacelocal s = line:match('%S+%s*', offset)assert(s)local w = editor.font:getWidth(s)if x+w < editor.width then return false endif w > editor.width then return false end -- we're going to need to truncate the next word anywayif x < 0.8*editor.width thenlocal s2 = line:match('%S+', offset)local w2 = editor.font:getWidth(s2)if x+w2 > editor.width then-- there'll be some non-whitespace left over for the next linereturn falseend - edit in source_text.lua at line 169
return true - replacement in source_text.lua at line 252
--== shortcuts that mutate text--== shortcuts that mutate text (must schedule_save) - edit in source_text.lua at line 1196
end-- create a new iterator for s which provides the index and UTF-8 bytes corresponding to each codepointfunction utf8chars(s, startpos)local next_pos = startpos or 1 -- in code pointslocal next_offset = utf8.offset(s, next_pos) -- in bytesreturn function()assert(next_offset) -- never call the iterator after it returns nillocal curr_pos = next_posnext_pos = next_pos+1local curr_offset = next_offsetnext_offset = utf8.offset(s, 2, next_offset)if next_offset == nil then return endlocal curr_char = s:sub(curr_offset, next_offset-1)return curr_pos, curr_charend - replacement in source_edit.lua at line 545
function edit.update_font_settings(State, font_height)function edit.update_font_settings(State, font_height, font) - replacement in source_edit.lua at line 547
State.font = love.graphics.newFont(State.font_height)State.font = font or love.graphics.newFont(State.font_height) - resurrect zombie in conf.lua at line 3
t.window.usedpiscale = false - resolve order conflict in conf.lua at line 3