extract a couple of files
[?]
Jun 3, 2022, 9:14 PM
LXTTOB33N2HCUZFIUDRQGGBVHK2HODRG4NBLH6RXRQZDCHF27BSACDependencies
- [2]
EWQ2VRMSclean up test progress indicators - [3]
IRJKWZWN. - [4]
ORQKYYD7. - [5]
LS55YKGWswitch copy/paste to ctrl- hotkeys - [6]
5FW7YOFThighlight selection while dragging - [7]
NP7PIUBTbugfix: restore state after C-f (find) - [8]
6E3HVYWFtest and App helper for mouse clicks - [9]
XOAHJ6M3similar tests for cursor up - [10]
2ENZW7TVselect text using mouse drag - [11]
YPHKZVWMextract a new variable - [12]
H2DPLWMVsnapshot: wrapping long lines at word boundaries - [13]
BULPIBEGbeginnings of a module for the text editor - [14]
N6V6UJ3Pthis implementation undo load-tests quite poorly - [15]
NQWWTGXRswitch undo/redo to ctrl- hotkeys - [16]
PFT5Y2ZYmove - [17]
U7M4M2F7bugfix: don't rely on Screen_bottom1 while scrolling - [18]
VJ77YABHmore efficient undo/redo - [19]
5DOC2CBMextract a function - [20]
XNFTJHC4split keyboard handling between Text and Drawing - [21]
HOSPP2ANcrisp font rendering - [22]
DHI6IJCNselecting text and deleting selections - [23]
LERERVPHkeep one screen line of overlap on pagedown - [24]
S5VCAFKYcouple of tests for cursor down - [25]
R53OF3ONone bug I've repeatedly run into while testing with Moby Dick - [26]
A2NV3WVOscrolling with up arrow - [27]
73OCE2MCafter much struggle, a brute-force undo - [28]
IMEJA43Lsnapshot - [29]
YTSPVDZHfirst successful pagedown test, first bug found by test - [30]
DFSDPDO7bugfix - [31]
V5MJRFOZbugfix: down arrow doesn't scroll up unnecessarily - [32]
WY3JD6W6bugfix - [33]
4RUI5X52a few tests for pageup, and a bugfix - [34]
PHFWIFYKscroll on enter - [35]
R5OKMVVCfix a regression in line wrapping - [36]
BYG5CEMVsupport for naming points - [37]
YYUGIYFV. - [38]
QVDQMJXVavoid scrolling down if possible - [39]
JF5L2BBStest harness now supports copy/paste - [40]
KWOJ6XHEcut/copy selected text to clipboard - [41]
Z4XRNDTRfind text - [42]
AD34IX2Zcouple more tests - [43]
QYIFOHW3first test! - [44]
HMODUNJEscroll on backspace - [45]
TKFSYQ2Zup arrow to search previous - [*]
R5QXEHUIsomebody stop me
Change contents
- file addition: text_tests.lua[47.2]
-- major tests for text editing flows-- This still isn't quite as thorough as I'd like.function test_draw_text()io.write('\ntest_draw_text')App.screen.init{width=120, height=60}Lines = load_array{'abc', 'def', 'ghi'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_draw_text/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_draw_text/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_draw_text/screen:3')endfunction test_draw_wrapping_text()io.write('\ntest_draw_wrapping_text')App.screen.init{width=50, height=60}Lines = load_array{'abc', 'defgh', 'xyz'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_draw_wrapping_text/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_draw_wrapping_text/screen:2')y = y + Line_heightApp.screen.check(y, 'gh', 'F - test_draw_wrapping_text/screen:3')endfunction test_draw_word_wrapping_text()io.write('\ntest_draw_word_wrapping_text')App.screen.init{width=60, height=60}Lines = load_array{'abc def ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc ', 'F - test_draw_word_wrapping_text/screen:1')y = y + Line_heightApp.screen.check(y, 'def ', 'F - test_draw_word_wrapping_text/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_draw_word_wrapping_text/screen:3')endfunction test_draw_text_wrapping_within_word()-- arrange a screen line that needs to be split within a wordio.write('\ntest_draw_text_wrapping_within_word')App.screen.init{width=60, height=60}Lines = load_array{'abcd e fghijk', 'xyz'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abcd ', 'F - test_draw_text_wrapping_within_word/screen:1')y = y + Line_heightApp.screen.check(y, 'e fghi', 'F - test_draw_text_wrapping_within_word/screen:2')y = y + Line_heightApp.screen.check(y, 'jk', 'F - test_draw_text_wrapping_within_word/screen:3')endfunction test_edit_wrapping_text()io.write('\ntest_edit_wrapping_text')App.screen.init{width=50, height=60}Lines = load_array{'abc', 'def', 'xyz'}Line_width = App.screen.widthCursor1 = {line=2, pos=4}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.run_after_textinput('g')App.run_after_textinput('h')App.run_after_textinput('i')App.run_after_textinput('j')App.run_after_textinput('k')App.run_after_textinput('l')local y = Margin_topApp.screen.check(y, 'abc', 'F - test_edit_wrapping_text/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_edit_wrapping_text/screen:2')y = y + Line_heightApp.screen.check(y, 'ghij', 'F - test_edit_wrapping_text/screen:3')endfunction test_insert_newline()io.write('\ntest_insert_newline')-- display a few lines with cursor on bottom lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=1, pos=2}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_insert_newline/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_insert_newline/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_insert_newline/baseline/screen:3')-- after hitting the enter key the screen scrolls downApp.run_after_keychord('return')check_eq(Screen_top1.line, 1, 'F - test_insert_newline/screen_top')check_eq(Cursor1.line, 2, 'F - test_insert_newline/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_insert_newline/cursor:pos')y = Margin_topApp.screen.check(y, 'a', 'F - test_insert_newline/screen:1')y = y + Line_heightApp.screen.check(y, 'bc', 'F - test_insert_newline/screen:2')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_insert_newline/screen:3')endfunction test_insert_from_clipboard()io.write('\ntest_insert_from_clipboard')-- display a few lines with cursor on bottom lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=1, pos=2}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_insert_from_clipboard/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_insert_from_clipboard/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_insert_from_clipboard/baseline/screen:3')-- after hitting the enter key the screen scrolls downApp.clipboard = 'xy\nz'App.run_after_keychord('C-v')check_eq(Screen_top1.line, 1, 'F - test_insert_from_clipboard/screen_top')check_eq(Cursor1.line, 2, 'F - test_insert_from_clipboard/cursor:line')check_eq(Cursor1.pos, 2, 'F - test_insert_from_clipboard/cursor:pos')y = Margin_topApp.screen.check(y, 'axy', 'F - test_insert_from_clipboard/screen:1')y = y + Line_heightApp.screen.check(y, 'zbc', 'F - test_insert_from_clipboard/screen:2')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_insert_from_clipboard/screen:3')endfunction test_move_cursor_using_mouse()io.write('\ntest_move_cursor_using_mouse')App.screen.init{width=50, height=60}Lines = load_array{'abc', 'def', 'xyz'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw() -- populate line.y for each line in Lineslocal screen_left_margin = 25 -- pixelsApp.run_after_mouserelease(screen_left_margin+8,Margin_top+5, '1')check_eq(Cursor1.line, 1, 'F - test_move_cursor_using_mouse/cursor:line')check_eq(Cursor1.pos, 2, 'F - test_move_cursor_using_mouse/cursor:pos')endfunction test_pagedown()io.write('\ntest_pagedown')App.screen.init{width=120, height=45}Lines = load_array{'abc', 'def', 'ghi'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}-- initially the first two lines are displayedApp.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_pagedown/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_pagedown/baseline/screen:2')-- after pagedown the bottom line becomes the topApp.run_after_keychord('pagedown')check_eq(Screen_top1.line, 2, 'F - test_pagedown/screen_top')check_eq(Cursor1.line, 2, 'F - test_pagedown/cursor')y = Margin_topApp.screen.check(y, 'def', 'F - test_pagedown/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_pagedown/screen:2')endfunction test_pagedown_skips_drawings()io.write('\ntest_pagedown_skips_drawings')-- some lines of text with a drawing intermixedApp.screen.init{width=50, height=80}Lines = load_array{'abc', -- height 15'```lines', '```', -- height 25'def', -- height 15'ghi'} -- height 15check_eq(Lines[2].mode, 'drawing', 'F - test_pagedown_skips_drawings/baseline/lines')Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}local drawing_height = 20 + App.screen.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 80pxApp.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_pagedown_skips_drawings/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 80pxApp.run_after_keychord('pagedown')check_eq(Screen_top1.line, 2, 'F - test_pagedown_skips_drawings/screen_top')check_eq(Cursor1.line, 3, 'F - test_pagedown_skips_drawings/cursor')y = Margin_top + drawing_heightApp.screen.check(y, 'def', 'F - test_pagedown_skips_drawings/screen:1')endfunction test_pagedown_shows_one_screen_line_in_common()io.write('\ntest_pagedown_shows_one_screen_line_in_common')-- some lines of text with a drawing intermixedApp.screen.init{width=50, height=60}Lines = load_array{'abc', 'def ghi jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_pagedown_shows_one_screen_line_in_common/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def ', 'F - test_pagedown_shows_one_screen_line_in_common/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi ', 'F - test_pagedown_shows_one_screen_line_in_common/baseline/screen:3')-- after pagedown the bottom screen line becomes the topApp.run_after_keychord('pagedown')check_eq(Screen_top1.line, 2, 'F - test_pagedown_shows_one_screen_line_in_common/screen_top:line')check_eq(Screen_top1.pos, 5, 'F - test_pagedown_shows_one_screen_line_in_common/screen_top:pos')check_eq(Cursor1.line, 2, 'F - test_pagedown_shows_one_screen_line_in_common/cursor:line')check_eq(Cursor1.pos, 5, 'F - test_pagedown_shows_one_screen_line_in_common/cursor:pos')y = Margin_topApp.screen.check(y, 'ghi ', 'F - test_pagedown_shows_one_screen_line_in_common/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_pagedown_shows_one_screen_line_in_common/screen:2')y = y + Line_heightApp.screen.check(y, 'mn', 'F - test_pagedown_shows_one_screen_line_in_common/screen:3')endfunction test_down_arrow_moves_cursor()io.write('\ntest_down_arrow_moves_cursor')App.screen.init{width=120, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}-- initially the first three lines are displayedApp.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_down_arrow_moves_cursor/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_down_arrow_moves_cursor/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_down_arrow_moves_cursor/baseline/screen:3')-- after hitting the down arrow, the cursor moves down by 1 lineApp.run_after_keychord('down')check_eq(Screen_top1.line, 1, 'F - test_down_arrow_moves_cursor/screen_top')check_eq(Cursor1.line, 2, 'F - test_down_arrow_moves_cursor/cursor')-- the screen is unchangedy = Margin_topApp.screen.check(y, 'abc', 'F - test_down_arrow_moves_cursor/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_down_arrow_moves_cursor/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_down_arrow_moves_cursor/screen:3')endfunction test_down_arrow_scrolls_down_by_one_line()io.write('\ntest_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}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=3, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:3')-- after hitting the down arrow the screen scrolls down by one lineApp.run_after_keychord('down')check_eq(Screen_top1.line, 2, 'F - test_down_arrow_scrolls_down_by_one_line/screen_top')check_eq(Cursor1.line, 4, 'F - test_down_arrow_scrolls_down_by_one_line/cursor')y = Margin_topApp.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_line/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_down_arrow_scrolls_down_by_one_line/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_down_arrow_scrolls_down_by_one_line/screen:3')endfunction test_down_arrow_scrolls_down_by_one_screen_line()io.write('\ntest_down_arrow_scrolls_down_by_one_screen_line')-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=3, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi ', 'F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the down arrow the screen scrolls down by one lineApp.run_after_keychord('down')check_eq(Screen_top1.line, 2, 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen_top')check_eq(Cursor1.line, 3, 'F - test_down_arrow_scrolls_down_by_one_screen_line/cursor:line')check_eq(Cursor1.pos, 5, 'F - test_down_arrow_scrolls_down_by_one_screen_line/cursor:pos')y = Margin_topApp.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi ', 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen:3')endfunction test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word()io.write('\ntest_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=25+30, height=60}Lines = load_array{'abc', 'def', 'ghijkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=3, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghijk', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:3')-- after hitting the down arrow the screen scrolls down by one lineApp.run_after_keychord('down')check_eq(Screen_top1.line, 2, 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen_top')check_eq(Cursor1.line, 3, 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/cursor:line')check_eq(Cursor1.pos, 6, 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/cursor:pos')y = Margin_topApp.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:1')y = y + Line_heightApp.screen.check(y, 'ghijk', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:2')y = y + Line_heightApp.screen.check(y, 'l', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:3')endfunction test_page_down_followed_by_down_arrow_does_not_scroll_screen_up()io.write('\ntest_page_down_followed_by_down_arrow_does_not_scroll_screen_up')App.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghijkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=3, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghijk', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:3')-- after hitting pagedown the screen scrolls down to start of a long lineApp.run_after_keychord('pagedown')check_eq(Screen_top1.line, 3, 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline2/screen_top')check_eq(Cursor1.line, 3, 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline2/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline2/cursor:pos')-- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll upApp.run_after_keychord('down')check_eq(Screen_top1.line, 3, 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen_top')check_eq(Cursor1.line, 3, 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/cursor:line')check_eq(Cursor1.pos, 6, 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/cursor:pos')y = Margin_topApp.screen.check(y, 'ghijk', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen:1')y = y + Line_heightApp.screen.check(y, 'l', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen:3')endfunction test_up_arrow_moves_cursor()io.write('\ntest_up_arrow_moves_cursor')-- display the first 3 lines with the cursor on the bottom lineApp.screen.init{width=120, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = 120Cursor1 = {line=3, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_up_arrow_moves_cursor/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_up_arrow_moves_cursor/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_up_arrow_moves_cursor/baseline/screen:3')-- after hitting the up arrow the cursor moves up by 1 lineApp.run_after_keychord('up')check_eq(Screen_top1.line, 1, 'F - test_up_arrow_moves_cursor/screen_top')check_eq(Cursor1.line, 2, 'F - test_up_arrow_moves_cursor/cursor')-- the screen is unchangedy = Margin_topApp.screen.check(y, 'abc', 'F - test_up_arrow_moves_cursor/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_up_arrow_moves_cursor/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_up_arrow_moves_cursor/screen:3')endfunction test_up_arrow_scrolls_up_by_one_line()io.write('\ntest_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}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = 120Cursor1 = {line=2, pos=1}Screen_top1 = {line=2, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:3')-- after hitting the up arrow the screen scrolls up by one lineApp.run_after_keychord('up')check_eq(Screen_top1.line, 1, 'F - test_up_arrow_scrolls_up_by_one_line/screen_top')check_eq(Cursor1.line, 1, 'F - test_up_arrow_scrolls_up_by_one_line/cursor')y = Margin_topApp.screen.check(y, 'abc', 'F - test_up_arrow_scrolls_up_by_one_line/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_by_one_line/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_by_one_line/screen:3')endfunction test_up_arrow_scrolls_up_by_one_screen_line()io.write('\ntest_up_arrow_scrolls_up_by_one_screen_line')-- display lines starting from second screen line of a lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=3, pos=6}Screen_top1 = {line=3, pos=5}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_by_one_screen_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_up_arrow_scrolls_up_by_one_screen_line/baseline/screen:2')-- after hitting the up arrow the screen scrolls up to first screen lineApp.run_after_keychord('up')y = Margin_topApp.screen.check(y, 'ghi ', 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen:3')check_eq(Screen_top1.line, 3, 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen_top')check_eq(Screen_top1.pos, 1, 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen_top')check_eq(Cursor1.line, 3, 'F - test_up_arrow_scrolls_up_by_one_screen_line/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_up_arrow_scrolls_up_by_one_screen_line/cursor:pos')endfunction test_up_arrow_scrolls_up_to_final_screen_line()io.write('\ntest_up_arrow_scrolls_up_to_final_screen_line')-- display lines starting just after a long lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=2, pos=1}Screen_top1 = {line=2, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:3')-- after hitting the up arrow the screen scrolls up to final screen line of previous lineApp.run_after_keychord('up')y = Margin_topApp.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen:3')check_eq(Screen_top1.line, 1, 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen_top')check_eq(Screen_top1.pos, 5, 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen_top')check_eq(Cursor1.line, 1, 'F - test_up_arrow_scrolls_up_to_final_screen_line/cursor:line')check_eq(Cursor1.pos, 5, 'F - test_up_arrow_scrolls_up_to_final_screen_line/cursor:pos')endfunction test_up_arrow_scrolls_up_to_empty_line()io.write('\ntest_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}Lines = load_array{'', 'abc', 'def', 'ghi', 'jkl'}Line_width = 120Cursor1 = {line=2, pos=1}Screen_top1 = {line=2, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_up_arrow_scrolls_up_to_empty_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_to_empty_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_to_empty_line/baseline/screen:3')-- after hitting the up arrow the screen scrolls up by one lineApp.run_after_keychord('up')check_eq(Screen_top1.line, 1, 'F - test_up_arrow_scrolls_up_to_empty_line/screen_top')check_eq(Cursor1.line, 1, 'F - test_up_arrow_scrolls_up_to_empty_line/cursor')y = Margin_top-- empty first liney = y + Line_heightApp.screen.check(y, 'abc', 'F - test_up_arrow_scrolls_up_to_empty_line/screen:2')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_to_empty_line/screen:3')endfunction test_pageup()io.write('\ntest_pageup')App.screen.init{width=120, height=45}Lines = load_array{'abc', 'def', 'ghi'}Line_width = App.screen.widthCursor1 = {line=2, pos=1}Screen_top1 = {line=2, pos=1}Screen_bottom1 = {}-- initially the last two lines are displayedApp.draw()local y = Margin_topApp.screen.check(y, 'def', 'F - test_pageup/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_pageup/baseline/screen:2')-- after pageup the cursor goes to first lineApp.run_after_keychord('pageup')check_eq(Screen_top1.line, 1, 'F - test_pageup/screen_top')check_eq(Cursor1.line, 1, 'F - test_pageup/cursor')y = Margin_topApp.screen.check(y, 'abc', 'F - test_pageup/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_pageup/screen:2')endfunction test_pageup_scrolls_up_by_screen_line()io.write('\ntest_pageup_scrolls_up_by_screen_line')-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=2, pos=1}Screen_top1 = {line=2, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'ghi', 'F - test_pageup_scrolls_up_by_screen_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_pageup_scrolls_up_by_screen_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_pageup_scrolls_up_by_screen_line/baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the page-up key the screen scrolls up to topApp.run_after_keychord('pageup')check_eq(Screen_top1.line, 1, 'F - test_pageup_scrolls_up_by_screen_line/screen_top')check_eq(Cursor1.line, 1, 'F - test_pageup_scrolls_up_by_screen_line/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_pageup_scrolls_up_by_screen_line/cursor:pos')y = Margin_topApp.screen.check(y, 'abc ', 'F - test_pageup_scrolls_up_by_screen_line/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_pageup_scrolls_up_by_screen_line/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_pageup_scrolls_up_by_screen_line/screen:3')endfunction test_pageup_scrolls_up_from_middle_screen_line()io.write('\ntest_pageup_scrolls_up_from_middle_screen_line')-- display a few lines starting from the middle of a line (Cursor1.pos > 1)App.screen.init{width=25+30, height=60}Lines = load_array{'abc def', 'ghi jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=2, pos=5}Screen_top1 = {line=2, pos=5}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'jkl', 'F - test_pageup_scrolls_up_from_middle_screen_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_pageup_scrolls_up_from_middle_screen_line/baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the page-up key the screen scrolls up to topApp.run_after_keychord('pageup')check_eq(Screen_top1.line, 1, 'F - test_pageup_scrolls_up_from_middle_screen_line/screen_top')check_eq(Cursor1.line, 1, 'F - test_pageup_scrolls_up_from_middle_screen_line/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_pageup_scrolls_up_from_middle_screen_line/cursor:pos')y = Margin_topApp.screen.check(y, 'abc ', 'F - test_pageup_scrolls_up_from_middle_screen_line/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_pageup_scrolls_up_from_middle_screen_line/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi ', 'F - test_pageup_scrolls_up_from_middle_screen_line/screen:3')endfunction test_enter_on_bottom_line_scrolls_down()io.write('\ntest_enter_on_bottom_line_scrolls_down')-- display a few lines with cursor on bottom lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=3, pos=2}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_enter_on_bottom_line_scrolls_down/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_enter_on_bottom_line_scrolls_down/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_enter_on_bottom_line_scrolls_down/baseline/screen:3')-- after hitting the enter key the screen scrolls downApp.run_after_keychord('return')check_eq(Screen_top1.line, 2, 'F - test_enter_on_bottom_line_scrolls_down/screen_top')check_eq(Cursor1.line, 4, 'F - test_enter_on_bottom_line_scrolls_down/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_enter_on_bottom_line_scrolls_down/cursor:pos')y = Margin_topApp.screen.check(y, 'def', 'F - test_enter_on_bottom_line_scrolls_down/screen:1')y = y + Line_heightApp.screen.check(y, 'g', 'F - test_enter_on_bottom_line_scrolls_down/screen:2')y = y + Line_heightApp.screen.check(y, 'hi', 'F - test_enter_on_bottom_line_scrolls_down/screen:3')endfunction test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()io.write('\ntest_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom')-- display just the bottom line on screenApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=4, pos=2}Screen_top1 = {line=4, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'jkl', 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/baseline/screen:1')-- after hitting the enter key the screen does not scroll downApp.run_after_keychord('return')check_eq(Screen_top1.line, 4, 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/screen_top')check_eq(Cursor1.line, 5, 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/cursor:pos')y = Margin_topApp.screen.check(y, 'j', 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/screen:1')y = y + Line_heightApp.screen.check(y, 'kl', 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/screen:2')endfunction test_position_cursor_on_recently_edited_wrapping_line()-- draw a line wrapping over 2 screen linesio.write('\ntest_position_cursor_on_recently_edited_wrapping_line')App.screen.init{width=120, height=200}Lines = load_array{'abc def ghi jkl mno pqr ', 'xyz'}Line_width = 100Cursor1 = {line=1, pos=25}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_top-- I don't understand why 120px fits so much on a fake screen, but whatever..App.screen.check(y, 'abc def ghi ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline1/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl mno pqr ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline1/screen:2')y = y + Line_heightApp.screen.check(y, 'xyz', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline1/screen:3')-- add to the line until it's wrapping over 3 screen linesApp.run_after_textinput('s')App.run_after_textinput('t')App.run_after_textinput('u')check_eq(Cursor1.pos, 28, 'F - test_move_cursor_using_mouse/cursor:pos')y = Margin_topApp.screen.check(y, 'abc def ghi ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl mno pqr ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:2')y = y + Line_heightApp.screen.check(y, 'stu', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:3')-- try to move the cursor earlier in the third screen line by clicking the mouselocal screen_left_margin = 25 -- pixelsApp.run_after_mouserelease(screen_left_margin+8,Margin_top+Line_height*2+5, '1')-- cursor should movecheck_eq(Cursor1.line, 1, 'F - test_move_cursor_using_mouse/cursor:line')check_eq(Cursor1.pos, 26, 'F - test_move_cursor_using_mouse/cursor:pos')endfunction test_backspace_can_scroll_up()io.write('\ntest_backspace_can_scroll_up')-- display the lines 2/3/4 with the cursor on line 2App.screen.init{width=120, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = 120Cursor1 = {line=2, pos=1}Screen_top1 = {line=2, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'def', 'F - test_backspace_can_scroll_up/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_backspace_can_scroll_up/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_backspace_can_scroll_up/baseline/screen:3')-- after hitting backspace the screen scrolls up by one lineApp.run_after_keychord('backspace')check_eq(Screen_top1.line, 1, 'F - test_backspace_can_scroll_up/screen_top')check_eq(Cursor1.line, 1, 'F - test_backspace_can_scroll_up/cursor')y = Margin_topApp.screen.check(y, 'abcdef', 'F - test_backspace_can_scroll_up/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_backspace_can_scroll_up/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_backspace_can_scroll_up/screen:3')endfunction test_backspace_can_scroll_up_screen_line()io.write('\ntest_backspace_can_scroll_up_screen_line')-- display lines starting from second screen line of a lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=3, pos=5}Screen_top1 = {line=3, pos=5}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'jkl', 'F - test_backspace_can_scroll_up_screen_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_backspace_can_scroll_up_screen_line/baseline/screen:2')-- after hitting backspace the screen scrolls up by one screen lineApp.run_after_keychord('backspace')y = Margin_topApp.screen.check(y, 'ghijk', 'F - test_backspace_can_scroll_up_screen_line/screen:1')y = y + Line_heightApp.screen.check(y, 'l', 'F - test_backspace_can_scroll_up_screen_line/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_backspace_can_scroll_up_screen_line/screen:3')check_eq(Screen_top1.line, 3, 'F - test_backspace_can_scroll_up_screen_line/screen_top')check_eq(Screen_top1.pos, 1, 'F - test_backspace_can_scroll_up_screen_line/screen_top')check_eq(Cursor1.line, 3, 'F - test_backspace_can_scroll_up_screen_line/cursor:line')check_eq(Cursor1.pos, 4, 'F - test_backspace_can_scroll_up_screen_line/cursor:pos')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()io.write('\ntest_backspace_over_selection')-- select just one character within a line with cursor before selectionApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Selection1 = {line=1, pos=2}-- backspace deletes the selected character, even though it's after the cursorApp.run_after_keychord('backspace')check_eq(Lines[1].data, 'bc', "F - test_backspace_over_selection/data")-- cursor (remains) at start of selectioncheck_eq(Cursor1.line, 1, "F - test_backspace_over_selection/cursor:line")check_eq(Cursor1.pos, 1, "F - test_backspace_over_selection/cursor:pos")-- selection is clearedcheck_nil(Selection1.line, "F - test_backspace_over_selection/selection")endfunction test_backspace_over_selection_reverse()io.write('\ntest_backspace_over_selection_reverse')-- select just one character within a line with cursor after selectionApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=1, pos=2}Selection1 = {line=1, pos=1}-- backspace deletes the selected characterApp.run_after_keychord('backspace')check_eq(Lines[1].data, 'bc', "F - test_backspace_over_selection_reverse/data")-- cursor moves to start of selectioncheck_eq(Cursor1.line, 1, "F - test_backspace_over_selection_reverse/cursor:line")check_eq(Cursor1.pos, 1, "F - test_backspace_over_selection_reverse/cursor:pos")-- selection is clearedcheck_nil(Selection1.line, "F - test_backspace_over_selection_reverse/selection")endfunction test_backspace_over_multiple_lines()io.write('\ntest_backspace_over_multiple_lines')-- select just one character within a line with cursor after selectionApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=1, pos=2}Selection1 = {line=4, pos=2}-- backspace deletes the region and joins the remaining portions of lines on either sideApp.run_after_keychord('backspace')check_eq(Lines[1].data, 'akl', "F - test_backspace_over_multiple_lines/data:1")check_eq(Lines[2].data, 'mno', "F - test_backspace_over_multiple_lines/data:2")-- cursor remains at start of selectioncheck_eq(Cursor1.line, 1, "F - test_backspace_over_multiple_lines/cursor:line")check_eq(Cursor1.pos, 2, "F - test_backspace_over_multiple_lines/cursor:pos")-- selection is clearedcheck_nil(Selection1.line, "F - test_backspace_over_multiple_lines/selection")endfunction test_backspace_to_end_of_line()io.write('\ntest_backspace_to_end_of_line')-- select region from cursor to end of lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=1, pos=2}Selection1 = {line=1, pos=4}-- backspace deletes rest of line without joining to any other lineApp.run_after_keychord('backspace')check_eq(Lines[1].data, 'a', "F - test_backspace_to_start_of_line/data:1")check_eq(Lines[2].data, 'def', "F - test_backspace_to_start_of_line/data:2")-- cursor remains at start of selectioncheck_eq(Cursor1.line, 1, "F - test_backspace_to_start_of_line/cursor:line")check_eq(Cursor1.pos, 2, "F - test_backspace_to_start_of_line/cursor:pos")-- selection is clearedcheck_nil(Selection1.line, "F - test_backspace_to_start_of_line/selection")endfunction test_backspace_to_start_of_line()io.write('\ntest_backspace_to_start_of_line')-- select region from cursor to start of lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=2, pos=1}Selection1 = {line=2, pos=3}-- backspace deletes beginning of line without joining to any other lineApp.run_after_keychord('backspace')check_eq(Lines[1].data, 'abc', "F - test_backspace_to_start_of_line/data:1")check_eq(Lines[2].data, 'f', "F - test_backspace_to_start_of_line/data:2")-- cursor remains at start of selectioncheck_eq(Cursor1.line, 2, "F - test_backspace_to_start_of_line/cursor:line")check_eq(Cursor1.pos, 1, "F - test_backspace_to_start_of_line/cursor:pos")-- selection is clearedcheck_nil(Selection1.line, "F - test_backspace_to_start_of_line/selection")endfunction test_undo_insert_text()io.write('\ntest_undo_insert_text')App.screen.init{width=120, height=60}Lines = load_array{'abc', 'def', 'xyz'}Line_width = App.screen.widthCursor1 = {line=2, pos=4}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}-- insert a characterApp.run_after_textinput('g')check_eq(Cursor1.line, 2, 'F - test_undo_insert_text/baseline/cursor:line')check_eq(Cursor1.pos, 5, 'F - test_undo_insert_text/baseline/cursor:pos')check_nil(Selection1.line, 'F - test_undo_insert_text/baseline/selection:line')check_nil(Selection1.pos, 'F - test_undo_insert_text/baseline/selection:pos')local y = Margin_topApp.screen.check(y, 'abc', 'F - test_undo_insert_text/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'defg', 'F - test_undo_insert_text/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'xyz', 'F - test_undo_insert_text/baseline/screen:3')-- undoApp.run_after_keychord('C-z')check_eq(Cursor1.line, 2, 'F - test_undo_insert_text/cursor:line')check_eq(Cursor1.pos, 4, 'F - test_undo_insert_text/cursor:pos')check_nil(Selection1.line, 'F - test_undo_insert_text/selection:line')check_nil(Selection1.pos, 'F - test_undo_insert_text/selection:pos')y = Margin_topApp.screen.check(y, 'abc', 'F - test_undo_insert_text/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_undo_insert_text/screen:2')y = y + Line_heightApp.screen.check(y, 'xyz', 'F - test_undo_insert_text/screen:3')endfunction test_undo_delete_text()io.write('\ntest_undo_delete_text')App.screen.init{width=120, height=60}Lines = load_array{'abc', 'defg', 'xyz'}Line_width = App.screen.widthCursor1 = {line=2, pos=5}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}-- delete a characterApp.run_after_keychord('backspace')check_eq(Cursor1.line, 2, 'F - test_undo_delete_text/baseline/cursor:line')check_eq(Cursor1.pos, 4, 'F - test_undo_delete_text/baseline/cursor:pos')check_nil(Selection1.line, 'F - test_undo_delete_text/baseline/selection:line')check_nil(Selection1.pos, 'F - test_undo_delete_text/baseline/selection:pos')local y = Margin_topApp.screen.check(y, 'abc', 'F - test_undo_delete_text/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_undo_delete_text/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'xyz', 'F - test_undo_delete_text/baseline/screen:3')-- undo--? -- after undo, the backspaced key is selectedApp.run_after_keychord('C-z')check_eq(Cursor1.line, 2, 'F - test_undo_delete_text/cursor:line')check_eq(Cursor1.pos, 5, 'F - test_undo_delete_text/cursor:pos')check_nil(Selection1.line, 'F - test_undo_delete_text/selection:line')check_nil(Selection1.pos, 'F - test_undo_delete_text/selection:pos')--? check_eq(Selection1.line, 2, 'F - test_undo_delete_text/selection:line')--? check_eq(Selection1.pos, 4, 'F - test_undo_delete_text/selection:pos')y = Margin_topApp.screen.check(y, 'abc', 'F - test_undo_delete_text/screen:1')y = y + Line_heightApp.screen.check(y, 'defg', 'F - test_undo_delete_text/screen:2')y = y + Line_heightApp.screen.check(y, 'xyz', 'F - test_undo_delete_text/screen:3')end - replacement in text.lua at line 1
-- primitives for editing text-- text editor, particularly text drawing, horizontal wrap, vertical scrolling - edit in text.lua at line 6
require 'search'require 'select' - edit in text.lua at line 9
require 'text_tests' - edit in text.lua at line 109[8.2728]→[8.764:797](∅→∅),[8.797]→[8.472:498](∅→∅),[8.498]→[8.831:1313](∅→∅),[8.831]→[8.831:1313](∅→∅),[8.1313]→[8.499:539](∅→∅),[8.539]→[8.1362:1497](∅→∅),[8.1362]→[8.1362:1497](∅→∅),[8.1497]→[8.540:607](∅→∅),[8.607]→[8.1581:2104](∅→∅),[8.1581]→[8.1581:2104](∅→∅),[8.2104]→[8.2:130](∅→∅),[8.130]→[7.2:194](∅→∅),[7.194]→[8.220:709](∅→∅),[8.220]→[8.220:709](∅→∅),[8.709]→[8.2104:2232](∅→∅),[8.2104]→[8.2104:2232](∅→∅),[8.2232]→[8.710:941](∅→∅),[8.941]→[7.195:387](∅→∅),[7.387]→[8.2322:2539](∅→∅),[8.2322]→[8.2322:2539](∅→∅),[8.2539]→[8.1067:1068](∅→∅),[8.2728]→[8.1067:1068](∅→∅),[8.1068]→[8.942:1240](∅→∅),[8.1240]→[6.2:270](∅→∅),[6.270]→[8.1224:1556](∅→∅),[8.1224]→[8.1224:1556](∅→∅),[8.1556]→[6.271:409](∅→∅),[6.409]→[8.1601:2613](∅→∅),[8.1601]→[8.1601:2613](∅→∅),[8.2613]→[6.410:826](∅→∅),[6.826]→[8.2613:2619](∅→∅),[8.2613]→[8.2613:2619](∅→∅),[8.2619]→[6.827:872](∅→∅),[6.872]→[8.2619:2624](∅→∅),[8.2619]→[8.2619:2624](∅→∅),[8.2624]→[6.873:1098](∅→∅),[6.1098]→[8.2624:2657](∅→∅),[8.2624]→[8.2624:2657](∅→∅),[8.2657]→[8.920:1173](∅→∅),[8.1173]→[8.2657:3719](∅→∅),[8.2657]→[8.2657:3719](∅→∅),[8.3719]→[8.100:101](∅→∅),[8.2796]→[8.100:101](∅→∅),[8.101]→[8.1:850](∅→∅)
function Text.draw_search_bar()local h = Line_height+2local y = App.screen.height-hlove.graphics.setColor(0.9,0.9,0.9)love.graphics.rectangle('fill', 0, y-10, App.screen.width-1, h+8)love.graphics.setColor(0.6,0.6,0.6)love.graphics.line(0, y-10, App.screen.width-1, y-10)love.graphics.setColor(1,1,1)love.graphics.rectangle('fill', 20, y-6, App.screen.width-40, h+2, 2,2)love.graphics.setColor(0.6,0.6,0.6)love.graphics.rectangle('line', 20, y-6, App.screen.width-40, h+2, 2,2)love.graphics.setColor(0,0,0)App.screen.print(Search_term, 25,y-5)love.graphics.setColor(1,0,0)if Search_text == nil thenSearch_text = App.newText(love.graphics.getFont(), Search_term)endlove.graphics.circle('fill', 25+App.width(Search_text),y-5+h, 2)love.graphics.setColor(0,0,0)endfunction Text.search_next()-- search current linelocal pos = Lines[Cursor1.line].data:find(Search_term, Cursor1.pos)if pos thenCursor1.pos = posendif pos == nil thenfor i=Cursor1.line+1,#Lines dopos = Lines[i].data:find(Search_term)if pos thenCursor1.line = iCursor1.pos = posbreakendendendif pos == nil then-- wrap aroundfor i=1,Cursor1.line-1 dopos = Lines[i].data:find(Search_term)if pos thenCursor1.line = iCursor1.pos = posbreakendendendif pos == nil thenCursor1.line = Search_backup.cursor.lineCursor1.pos = Search_backup.cursor.posScreen_top1.line = Search_backup.screen_top.lineScreen_top1.pos = Search_backup.screen_top.posendif Text.lt1(Cursor1, Screen_top1) or Text.lt1(Screen_bottom1, Cursor1) thenScreen_top1.line = Cursor1.linelocal _, pos = Text.pos_at_start_of_cursor_screen_line()Screen_top1.pos = posendendfunction Text.search_previous()-- search current linelocal pos = rfind(Lines[Cursor1.line].data, Search_term, Cursor1.pos)if pos thenCursor1.pos = posendif pos == nil thenfor i=Cursor1.line-1,1,-1 dopos = rfind(Lines[i].data, Search_term)if pos thenCursor1.line = iCursor1.pos = posbreakendendendif pos == nil then-- wrap aroundfor i=#Lines,Cursor1.line+1,-1 dopos = rfind(Lines[i].data, Search_term)if pos thenCursor1.line = iCursor1.pos = posbreakendendendif pos == nil thenCursor1.line = Search_backup.cursor.lineCursor1.pos = Search_backup.cursor.posScreen_top1.line = Search_backup.screen_top.lineScreen_top1.pos = Search_backup.screen_top.posendif Text.lt1(Cursor1, Screen_top1) or Text.lt1(Screen_bottom1, Cursor1) thenScreen_top1.line = Cursor1.linelocal _, pos = Text.pos_at_start_of_cursor_screen_line()Screen_top1.pos = posendendfunction rfind(s, pat, i)local rs = s:reverse()local rpat = pat:reverse()if i == nil then i = #s endlocal ri = #s - i + 1local rendpos = rs:find(rpat, ri)if rendpos == nil then return nil endlocal endpos = #s - rendpos + 1assert (endpos >= #pat)return endpos-#pat+1end-- Return any intersection of the region from Selection1 to Cursor1 (or-- current mouse, if mouse is pressed; or recent mouse if mouse is pressed and-- currently over a drawing) with the region between {line=line_index, pos=apos}-- and {line=line_index, pos=bpos}.-- apos must be less than bpos. However Selection1 and Cursor1 can be in any order.-- Result: positions spos,epos between apos,bpos.function Text.clip_selection(line_index, apos, bpos)if Selection1.line == nil then return nil,nil end-- min,max = sorted(Selection1,Cursor1)local minl,minp = Selection1.line,Selection1.poslocal maxl,maxpif love.mouse.isDown('1') thenmaxl,maxp = Text.mouse_pos()elsemaxl,maxp = Cursor1.line,Cursor1.posendif minl > maxl thenminl,maxl = maxl,minlminp,maxp = maxp,minpelseif minl == maxl thenif minp > maxp thenminp,maxp = maxp,minpendend-- check if intervals are disjointif line_index < minl then return nil,nil endif line_index > maxl then return nil,nil endif line_index == minl and bpos <= minp then return nil,nil endif line_index == maxl and apos >= maxp then return nil,nil end-- compare bounds more carefully (start inclusive, end exclusive)local a_ge = Text.le1({line=minl, pos=minp}, {line=line_index, pos=apos})local b_lt = Text.lt1({line=line_index, pos=bpos}, {line=maxl, pos=maxp})--? print(minl,line_index,maxl, '--', minp,apos,bpos,maxp, '--', a_ge,b_lt)if a_ge and b_lt then-- fully containedreturn apos,bposelseif a_ge thenassert(maxl == line_index)return apos,maxpelseif b_lt thenassert(minl == line_index)return minp,bposelseassert(minl == maxl and minl == line_index)return minp,maxpendend-- inefficient for some reason, so don't do it on every framefunction Text.mouse_pos()local time = love.timer.getTime()if Recent_mouse.time and Recent_mouse.time > time-0.1 thenreturn Recent_mouse.line, Recent_mouse.posendRecent_mouse.time = timelocal line,pos = Text.to_pos(love.mouse.getX(), love.mouse.getY())if line thenRecent_mouse.line = lineRecent_mouse.pos = posendreturn Recent_mouse.line, Recent_mouse.posendfunction Text.to_pos(x,y)for line_index,line in ipairs(Lines) doif line.mode == 'text' thenif Text.in_line(line, x,y) thenreturn line_index, Text.to_pos_on_line(line, x,y)endendendendfunction Text.delete_selection()local minl,maxl = minmax(Selection1.line, Cursor1.line)local before = snapshot(minl, maxl)Text.delete_selection_without_undo()record_undo_event({before=before, after=snapshot(Cursor1.line)})endfunction Text.delete_selection_without_undo()if Selection1.line == nil then return end-- min,max = sorted(Selection1,Cursor1)local minl,minp = Selection1.line,Selection1.poslocal maxl,maxp = Cursor1.line,Cursor1.posif minl > maxl thenminl,maxl = maxl,minlminp,maxp = maxp,minpelseif minl == maxl thenif minp > maxp thenminp,maxp = maxp,minpendend-- update Cursor1 and Selection1Cursor1.line = minlCursor1.pos = minpSelection1 = {}-- delete everything between min (inclusive) and max (exclusive)Lines[minl].fragments = nilLines[minl].screen_line_starting_pos = nillocal min_offset = utf8.offset(Lines[minl].data, minp)local max_offset = utf8.offset(Lines[maxl].data, maxp)if minl == maxl then--? print('minl == maxl')Lines[minl].data = Lines[minl].data:sub(1, min_offset-1)..Lines[minl].data:sub(max_offset)returnendassert(minl < maxl)local rhs = Lines[maxl].data:sub(max_offset)for i=maxl,minl+1,-1 dotable.remove(Lines, i)endLines[minl].data = Lines[minl].data:sub(1, min_offset-1)..rhsendfunction Text.selection()if Selection1.line == nil then return end-- min,max = sorted(Selection1,Cursor1)local minl,minp = Selection1.line,Selection1.poslocal maxl,maxp = Cursor1.line,Cursor1.posif minl > maxl thenminl,maxl = maxl,minlminp,maxp = maxp,minpelseif minl == maxl thenif minp > maxp thenminp,maxp = maxp,minpendendlocal min_offset = utf8.offset(Lines[minl].data, minp)local max_offset = utf8.offset(Lines[maxl].data, maxp)if minl == maxl thenreturn Lines[minl].data:sub(min_offset, max_offset-1)endassert(minl < maxl)local result = Lines[minl].data:sub(min_offset)..'\n'for i=minl+1,maxl-1 doif Lines[i].mode == 'text' thenresult = result..Lines[i].data..'\n'endendresult = result..Lines[maxl].data:sub(1, max_offset-1)return resultend - edit in text.lua at line 110
function Text.cut_selection()local result = Text.selection()Text.delete_selection()return resultend - edit in text.lua at line 118[8.42]→[8.111:137](∅→∅),[8.137]→[2.1:32](∅→∅),[2.32]→[8.137:219](∅→∅),[8.197]→[8.137:219](∅→∅),[8.137]→[8.137:219](∅→∅),[8.219]→[8.265:297](∅→∅),[8.297]→[8.238:320](∅→∅),[8.238]→[8.238:320](∅→∅),[8.331]→[8.331:344](∅→∅),[8.344]→[8.2:25](∅→∅),[8.25]→[8.452:512](∅→∅),[8.452]→[8.452:512](∅→∅),[8.512]→[8.688:710](∅→∅),[8.710]→[8.534:594](∅→∅),[8.534]→[8.534:594](∅→∅),[8.594]→[8.711:733](∅→∅),[8.733]→[8.616:681](∅→∅),[8.616]→[8.616:681](∅→∅),[8.681]→[8.1:273](∅→∅),[8.284]→[8.284:297](∅→∅),[8.297]→[8.26:49](∅→∅),[8.49]→[8.405:474](∅→∅),[8.405]→[8.405:474](∅→∅),[8.474]→[8.734:756](∅→∅),[8.756]→[8.496:565](∅→∅),[8.496]→[8.496:565](∅→∅),[8.565]→[8.757:779](∅→∅),[8.779]→[8.587:660](∅→∅),[8.587]→[8.587:660](∅→∅),[8.660]→[8.1:282](∅→∅),[8.293]→[8.293:306](∅→∅),[8.306]→[8.50:73](∅→∅),[8.73]→[8.414:489](∅→∅),[8.414]→[8.414:489](∅→∅),[8.489]→[8.780:802](∅→∅),[8.802]→[8.511:586](∅→∅),[8.511]→[8.511:586](∅→∅),[8.586]→[8.803:825](∅→∅),[8.825]→[8.608:1048](∅→∅),[8.608]→[8.608:1048](∅→∅),[8.1059]→[8.1059:1072](∅→∅),[8.1072]→[8.74:97](∅→∅),[8.97]→[8.1180:1263](∅→∅),[8.1180]→[8.1180:1263](∅→∅),[8.1263]→[8.826:848](∅→∅),[8.848]→[8.1285:1369](∅→∅),[8.1285]→[8.1285:1369](∅→∅),[8.1369]→[8.849:871](∅→∅),[8.871]→[8.1391:1476](∅→∅),[8.1391]→[8.1391:1476](∅→∅),[8.1476]→[8.660:930](∅→∅),[8.660]→[8.660:930](∅→∅),[8.941]→[8.941:1127](∅→∅),[8.1127]→[8.98:121](∅→∅),[8.121]→[8.1235:1304](∅→∅),[8.1235]→[8.1235:1304](∅→∅),[8.1304]→[8.872:894](∅→∅),[8.894]→[8.1326:1395](∅→∅),[8.1326]→[8.1326:1395](∅→∅),[8.1395]→[8.895:917](∅→∅),[8.917]→[8.1417:1487](∅→∅),[8.1417]→[8.1417:1487](∅→∅),[8.1487]→[8.2:331](∅→∅),[8.437]→[8.437:450](∅→∅),[8.450]→[8.122:145](∅→∅),[8.145]→[8.480:554](∅→∅),[8.480]→[8.480:554](∅→∅),[8.554]→[8.918:940](∅→∅),[8.940]→[8.576:650](∅→∅),[8.576]→[8.576:650](∅→∅),[8.650]→[8.941:963](∅→∅),[8.963]→[8.672:1040](∅→∅),[8.672]→[8.672:1040](∅→∅),[8.1040]→[8.146:163](∅→∅),[8.163]→[8.1064:1127](∅→∅),[8.1064]→[8.1064:1127](∅→∅),[8.1127]→[8.964:986](∅→∅),[8.986]→[8.1149:1213](∅→∅),[8.1149]→[8.1149:1213](∅→∅),[8.1213]→[8.987:1009](∅→∅),[8.1009]→[8.1235:1300](∅→∅),[8.1235]→[8.1235:1300](∅→∅),[8.1300]→[8.1487:1492](∅→∅),[8.1487]→[8.1487:1492](∅→∅),[8.1492]→[8.1301:1639](∅→∅),[8.1745]→[8.1745:1758](∅→∅),[8.1758]→[8.164:187](∅→∅),[8.187]→[8.1788:1869](∅→∅),[8.1788]→[8.1788:1869](∅→∅),[8.1869]→[8.1010:1032](∅→∅),[8.1032]→[8.1891:1972](∅→∅),[8.1891]→[8.1891:1972](∅→∅),[8.1972]→[8.1033:1055](∅→∅),[8.1055]→[8.1994:2158](∅→∅),[8.1994]→[8.1994:2158](∅→∅),[8.2158]→[5.2:34](∅→∅),[5.34]→[8.2190:2413](∅→∅),[8.2190]→[8.2190:2413](∅→∅),[8.2413]→[8.188:205](∅→∅),[8.205]→[8.2437:2509](∅→∅),[8.2437]→[8.2437:2509](∅→∅),[8.2509]→[8.1056:1078](∅→∅),[8.1078]→[8.2531:2603](∅→∅),[8.2531]→[8.2531:2603](∅→∅),[8.2603]→[8.1079:1101](∅→∅),[8.1101]→[8.2625:2702](∅→∅),[8.2625]→[8.2625:2702](∅→∅),[8.2702]→[8.1:281](∅→∅),[8.1492]→[8.1:281](∅→∅),[8.292]→[8.292:348](∅→∅),[8.390]→[8.390:433](∅→∅),[8.433]→[8.2:71](∅→∅),[8.71]→[8.507:662](∅→∅),[8.273]→[8.507:662](∅→∅),[8.507]→[8.507:662](∅→∅),[8.662]→[8.198:223](∅→∅),[8.1492]→[8.198:223](∅→∅),[8.681]→[8.198:223](∅→∅),[8.223]→[2.33:63](∅→∅),[2.63]→[8.248:330](∅→∅),[8.248]→[8.248:330](∅→∅),[8.330]→[8.298:330](∅→∅),[8.330]→[8.349:431](∅→∅),[8.349]→[8.349:431](∅→∅),[8.537]→[8.537:599](∅→∅),[8.599]→[8.274:297](∅→∅),[8.297]→[8.629:697](∅→∅),[8.629]→[8.629:697](∅→∅),[8.697]→[8.1102:1124](∅→∅),[8.1124]→[8.719:876](∅→∅),[8.719]→[8.719:876](∅→∅),[8.876]→[8.331:451](∅→∅),[8.451]→[8.298:315](∅→∅),[8.315]→[8.900:959](∅→∅),[8.900]→[8.900:959](∅→∅),[8.959]→[8.1125:1147](∅→∅),[8.1147]→[8.981:1045](∅→∅),[8.981]→[8.981:1045](∅→∅),[8.1045]→[8.1:41](∅→∅),[8.41]→[2.64:109](∅→∅),[8.81]→[8.1123:1173](∅→∅),[2.109]→[8.1123:1173](∅→∅),[8.1123]→[8.1123:1173](∅→∅),[8.1173]→[8.23:282](∅→∅),[8.282]→[8.82:170](∅→∅),[8.170]→[8.1423:1537](∅→∅),[8.1423]→[8.1423:1537](∅→∅),[8.308]→[8.308:538](∅→∅),[8.538]→[8.1722:1735](∅→∅),[8.1722]→[8.1722:1735](∅→∅),[8.1735]→[8.316:339](∅→∅),[8.339]→[8.171:254](∅→∅),[8.1765]→[8.171:254](∅→∅),[8.254]→[8.452:508](∅→∅),[8.508]→[8.539:640](∅→∅),[8.1902]→[8.539:640](∅→∅),[8.640]→[8.1902:1939](∅→∅),[8.1902]→[8.1902:1939](∅→∅),[8.1939]→[8.509:659](∅→∅),[8.659]→[8.340:374](∅→∅),[8.374]→[8.300:379](∅→∅),[8.1980]→[8.300:379](∅→∅),[8.379]→[8.706:1078](∅→∅),[8.1184]→[8.1184:1197](∅→∅),[8.1197]→[8.375:398](∅→∅),[8.398]→[8.1227:1327](∅→∅),[8.1227]→[8.1227:1327](∅→∅),[8.1327]→[8.1148:1170](∅→∅),[8.1170]→[8.1349:1450](∅→∅),[8.1349]→[8.1349:1450](∅→∅),[8.1450]→[8.1171:1193](∅→∅),[8.1193]→[8.1472:2053](∅→∅),[8.1472]→[8.1472:2053](∅→∅),[8.2053]→[8.399:416](∅→∅),[8.416]→[8.2077:2169](∅→∅),[8.2077]→[8.2077:2169](∅→∅),[8.2169]→[8.1194:1216](∅→∅),[8.1216]→[8.2191:2282](∅→∅),[8.2191]→[8.2191:2282](∅→∅),[8.2282]→[8.1217:1239](∅→∅),[8.1239]→[8.2304:2399](∅→∅),[8.2304]→[8.2304:2399](∅→∅),[8.2399]→[8.379:419](∅→∅),[8.379]→[8.379:419](∅→∅),[8.419]→[2.110:155](∅→∅),[2.155]→[8.459:548](∅→∅),[8.459]→[8.459:548](∅→∅),[8.548]→[8.660:692](∅→∅),[8.692]→[8.567:649](∅→∅),[8.567]→[8.567:649](∅→∅),[8.755]→[8.755:819](∅→∅),[8.819]→[8.417:440](∅→∅),[8.440]→[8.849:932](∅→∅),[8.849]→[8.849:932](∅→∅),[8.932]→[8.1240:1262](∅→∅),[8.1262]→[8.954:1037](∅→∅),[8.954]→[8.954:1037](∅→∅),[8.1037]→[8.1263:1285](∅→∅),[8.1285]→[8.1059:1142](∅→∅),[8.1059]→[8.1059:1142](∅→∅),[8.1142]→[8.693:760](∅→∅),[8.760]→[8.1200:1233](∅→∅),[8.1200]→[8.1200:1233](∅→∅),[8.1233]→[8.1:80](∅→∅),[8.80]→[8.838:938](∅→∅),[8.838]→[8.838:938](∅→∅),[8.938]→[8.441:458](∅→∅),[8.458]→[8.1257:1331](∅→∅),[8.1257]→[8.1257:1331](∅→∅),[8.1331]→[8.1286:1308](∅→∅),[8.1308]→[8.1353:1427](∅→∅),[8.1353]→[8.1353:1427](∅→∅),[8.1427]→[8.1309:1331](∅→∅),[8.1331]→[8.1449:1523](∅→∅),[8.1449]→[8.1449:1523](∅→∅),[8.1635]→[8.1635:1692](∅→∅),[8.1692]→[2.156:213](∅→∅),[2.213]→[8.1744:1903](∅→∅),[8.1744]→[8.1744:1903](∅→∅),[8.1903]→[8.939:971](∅→∅),[8.971]→[8.1922:2004](∅→∅),[8.1922]→[8.1922:2004](∅→∅),[8.2110]→[8.2110:2123](∅→∅),[8.2123]→[8.459:482](∅→∅),[8.482]→[8.2153:2248](∅→∅),[8.2153]→[8.2153:2248](∅→∅),[8.2248]→[8.1332:1354](∅→∅),[8.1354]→[8.2270:2365](∅→∅),[8.2270]→[8.2270:2365](∅→∅),[8.2365]→[8.1355:1377](∅→∅),[8.1377]→[8.2387:2585](∅→∅),[8.2387]→[8.2387:2585](∅→∅),[8.2585]→[8.972:1146](∅→∅),[8.1146]→[8.483:500](∅→∅),[8.500]→[8.2609:2695](∅→∅),[8.2609]→[8.2609:2695](∅→∅),[8.2695]→[8.1378:1400](∅→∅),[8.1400]→[8.2717:2803](∅→∅),[8.2717]→[8.2717:2803](∅→∅),[8.2803]→[8.1401:1423](∅→∅),[8.1423]→[8.2825:2911](∅→∅),[8.2825]→[8.2825:2911](∅→∅),[8.2911]→[8.1147:1211](∅→∅),[8.1211]→[2.214:278](∅→∅),[2.278]→[8.1270:1549](∅→∅),[8.1270]→[8.1270:1549](∅→∅),[8.1655]→[8.1655:1668](∅→∅),[8.1668]→[8.501:524](∅→∅),[8.524]→[8.1698:1800](∅→∅),[8.1698]→[8.1698:1800](∅→∅),[8.1800]→[8.1424:1446](∅→∅),[8.1446]→[8.1822:1924](∅→∅),[8.1822]→[8.1822:1924](∅→∅),[8.1924]→[8.1447:1469](∅→∅),[8.1469]→[8.1946:2485](∅→∅),[8.1946]→[8.1946:2485](∅→∅),[8.2485]→[8.525:542](∅→∅),[8.542]→[8.2509:2602](∅→∅),[8.2509]→[8.2509:2602](∅→∅),[8.2602]→[8.1470:1492](∅→∅),[8.1492]→[8.2624:2718](∅→∅),[8.2624]→[8.2624:2718](∅→∅),[8.2718]→[8.1493:1515](∅→∅),[8.1515]→[8.2750:2935](∅→∅),[8.2750]→[8.2750:2935](∅→∅),[8.2935]→[2.279:371](∅→∅),[2.371]→[8.3022:3300](∅→∅),[8.3022]→[8.3022:3300](∅→∅),[8.3406]→[8.3406:3419](∅→∅),[8.3419]→[8.543:566](∅→∅),[8.566]→[8.3449:3579](∅→∅),[8.3449]→[8.3449:3579](∅→∅),[8.3579]→[8.1516:1538](∅→∅),[8.1538]→[8.3601:3731](∅→∅),[8.3601]→[8.3601:3731](∅→∅),[8.3731]→[8.1539:1561](∅→∅),[8.1561]→[8.3753:4358](∅→∅),[8.3753]→[8.3753:4358](∅→∅),[8.4358]→[8.567:584](∅→∅),[8.584]→[8.4382:4503](∅→∅),[8.4382]→[8.4382:4503](∅→∅),[8.4503]→[8.1562:1584](∅→∅),[8.1584]→[8.4525:4648](∅→∅),[8.4525]→[8.4525:4648](∅→∅),[8.4648]→[8.1585:1607](∅→∅),[8.1607]→[8.4670:4789](∅→∅),[8.4670]→[8.4670:4789](∅→∅),[8.4789]→[8.1:369](∅→∅),[8.475]→[8.475:488](∅→∅),[8.488]→[8.585:608](∅→∅),[8.608]→[8.518:636](∅→∅),[8.518]→[8.518:636](∅→∅),[8.636]→[8.1608:1630](∅→∅),[8.1630]→[8.658:776](∅→∅),[8.658]→[8.658:776](∅→∅),[8.776]→[8.1631:1653](∅→∅),[8.1653]→[8.798:1864](∅→∅),[8.798]→[8.798:1864](∅→∅),[8.1864]→[8.609:626](∅→∅),[8.626]→[8.1888:1999](∅→∅),[8.1888]→[8.1888:1999](∅→∅),[8.1999]→[8.1654:1676](∅→∅),[8.1676]→[8.2021:2128](∅→∅),[8.2021]→[8.2021:2128](∅→∅),[8.2128]→[8.1677:1699](∅→∅),[8.1699]→[8.2150:2259](∅→∅),[8.2150]→[8.2150:2259](∅→∅),[8.2259]→[8.279:322](∅→∅),[8.4789]→[8.279:322](∅→∅),[8.279]→[8.279:322](∅→∅),[8.322]→[2.372:415](∅→∅),[2.415]→[8.360:616](∅→∅),[8.360]→[8.360:616](∅→∅),[8.722]→[8.722:735](∅→∅),[8.735]→[8.627:650](∅→∅),[8.650]→[8.765:846](∅→∅),[8.765]→[8.765:846](∅→∅),[8.846]→[8.1700:1722](∅→∅),[8.1722]→[8.868:949](∅→∅),[8.868]→[8.868:949](∅→∅),[8.949]→[8.1723:1745](∅→∅),[8.1745]→[8.971:1052](∅→∅),[8.971]→[8.971:1052](∅→∅),[8.1052]→[8.4790:4852](∅→∅),[8.4852]→[8.1108:1139](∅→∅),[8.1108]→[8.1108:1139](∅→∅),[8.1139]→[8.4853:5028](∅→∅),[8.5028]→[8.651:668](∅→∅),[8.668]→[8.1163:1235](∅→∅),[8.1163]→[8.1163:1235](∅→∅),[8.1235]→[8.1746:1768](∅→∅),[8.1768]→[8.1257:1329](∅→∅),[8.1257]→[8.1257:1329](∅→∅),[8.1329]→[8.1769:1791](∅→∅),[8.1791]→[8.1351:1423](∅→∅),[8.1351]→[8.1351:1423](∅→∅),[8.1608]→[8.1608:1661](∅→∅),[8.1661]→[2.416:469](∅→∅),[2.469]→[8.1709:1954](∅→∅),[8.1709]→[8.1709:1954](∅→∅),[8.2060]→[8.2060:2073](∅→∅),[8.2073]→[8.669:692](∅→∅),[8.692]→[8.2103:2194](∅→∅),[8.2103]→[8.2103:2194](∅→∅),[8.2194]→[8.1792:1814](∅→∅),[8.1814]→[8.2216:2307](∅→∅),[8.2216]→[8.2216:2307](∅→∅),[8.2307]→[8.1815:1837](∅→∅),[8.1837]→[8.2329:2517](∅→∅),[8.2329]→[8.2329:2517](∅→∅),[8.2517]→[8.5029:5195](∅→∅),[8.5195]→[8.693:710](∅→∅),[8.710]→[8.2541:2623](∅→∅),[8.2541]→[8.2541:2623](∅→∅),[8.2623]→[8.1838:1860](∅→∅),[8.1860]→[8.2645:2727](∅→∅),[8.2645]→[8.2645:2727](∅→∅),[8.2727]→[8.1861:1883](∅→∅),[8.1883]→[8.2749:2831](∅→∅),[8.2749]→[8.2749:2831](∅→∅),[8.2831]→[8.5196:5256](∅→∅),[8.5256]→[2.470:530](∅→∅),[2.530]→[8.1:63](∅→∅),[8.5311]→[8.1:63](∅→∅),[8.63]→[8.552:647](∅→∅),[8.552]→[8.552:647](∅→∅),[8.647]→[8.5455:5487](∅→∅),[8.5455]→[8.5455:5487](∅→∅),[8.5487]→[8.648:708](∅→∅),[8.708]→[8.5547:5569](∅→∅),[8.5547]→[8.5547:5569](∅→∅),[8.5675]→[8.5675:5688](∅→∅),[8.5688]→[8.711:734](∅→∅),[8.734]→[8.709:807](∅→∅),[8.5718]→[8.709:807](∅→∅),[8.807]→[8.1884:1906](∅→∅),[8.1906]→[8.808:906](∅→∅),[8.5958]→[8.808:906](∅→∅),[8.906]→[8.64:139](∅→∅),[8.139]→[8.6122:6153](∅→∅),[8.6122]→[8.6122:6153](∅→∅),[8.6153]→[8.735:752](∅→∅),[8.752]→[8.907:997](∅→∅),[8.6177]→[8.907:997](∅→∅),[8.997]→[8.1907:1929](∅→∅),[8.1929]→[8.998:1087](∅→∅),[8.6288]→[8.998:1087](∅→∅),[8.1087]→[8.1930:1952](∅→∅),[8.1952]→[8.1088:1364](∅→∅),[8.6399]→[8.1088:1364](∅→∅),[8.1364]→[3.1:181](∅→∅),[3.181]→[8.2148:2153](∅→∅),[8.1535]→[8.2148:2153](∅→∅),[8.2911]→[8.2148:2153](∅→∅),[8.2997]→[8.2148:2153](∅→∅),[8.6668]→[8.2148:2153](∅→∅),[8.2148]→[8.2148:2153](∅→∅),[8.2153]→[8.140:197](∅→∅),[8.197]→[2.531:593](∅→∅),[2.593]→[8.254:514](∅→∅),[8.254]→[8.254:514](∅→∅),[8.620]→[8.620:633](∅→∅),[8.633]→[8.753:776](∅→∅),[8.776]→[8.663:763](∅→∅),[8.663]→[8.663:763](∅→∅),[8.763]→[8.1953:1975](∅→∅),[8.1975]→[8.785:885](∅→∅),[8.785]→[8.785:885](∅→∅),[8.885]→[8.1976:1998](∅→∅),[8.1998]→[8.907:1130](∅→∅),[8.907]→[8.907:1130](∅→∅),[8.1130]→[8.777:794](∅→∅),[8.794]→[8.1154:1245](∅→∅),[8.1154]→[8.1154:1245](∅→∅),[8.1245]→[8.1999:2021](∅→∅),[8.2021]→[8.1267:1358](∅→∅),[8.1267]→[8.1267:1358](∅→∅),[8.1358]→[8.2022:2044](∅→∅),[8.2044]→[8.1380:1662](∅→∅),[8.1380]→[8.1380:1662](∅→∅),[8.1662]→[3.182:366](∅→∅),[3.366]→[8.1837:1842](∅→∅),[8.1837]→[8.1837:1842](∅→∅),[8.1842]→[8.81:131](∅→∅),[8.131]→[8.1:56](∅→∅),[8.56]→[8.188:467](∅→∅),[8.188]→[8.188:467](∅→∅),[8.573]→[8.573:586](∅→∅),[8.586]→[8.795:818](∅→∅),[8.818]→[8.57:150](∅→∅),[8.616]→[8.57:150](∅→∅),[8.150]→[8.2045:2067](∅→∅),[8.2067]→[8.151:244](∅→∅),[8.733]→[8.151:244](∅→∅),[8.244]→[8.2068:2090](∅→∅),[8.2090]→[8.245:338](∅→∅),[8.850]→[8.245:338](∅→∅),[8.338]→[8.945:1042](∅→∅),[8.945]→[8.945:1042](∅→∅),[8.1042]→[8.339:509](∅→∅),[8.509]→[8.819:836](∅→∅),[8.836]→[8.1240:1262](∅→∅),[8.1240]→[8.1240:1262](∅→∅),[8.1262]→[8.2091:2113](∅→∅),[8.2113]→[8.510:594](∅→∅),[8.1284]→[8.510:594](∅→∅),[8.594]→[8.2114:2136](∅→∅),[8.2136]→[8.595:679](∅→∅),[8.1392]→[8.595:679](∅→∅),[8.679]→[8.1478:1483](∅→∅),[8.1478]→[8.1478:1483](∅→∅),[8.1483]→[8.1:248](∅→∅),[8.1842]→[8.1:248](∅→∅),[8.354]→[8.354:415](∅→∅),[8.415]→[8.837:860](∅→∅),[8.860]→[8.445:511](∅→∅),[8.445]→[8.445:511](∅→∅),[8.511]→[8.2137:2159](∅→∅),[8.2159]→[8.533:798](∅→∅),[8.533]→[8.533:798](∅→∅),[8.798]→[8.861:878](∅→∅),[8.878]→[8.822:879](∅→∅),[8.822]→[8.822:879](∅→∅),[8.879]→[8.2160:2182](∅→∅),[8.2182]→[8.901:1345](∅→∅),[8.901]→[8.901:1345](∅→∅),[8.1451]→[8.1451:1464](∅→∅),[8.1464]→[8.879:902](∅→∅),[8.902]→[8.1494:1586](∅→∅),[8.1494]→[8.1494:1586](∅→∅),[8.1586]→[8.2183:2205](∅→∅),[8.2205]→[8.1608:1700](∅→∅),[8.1608]→[8.1608:1700](∅→∅),[8.1700]→[8.2206:2228](∅→∅),[8.2228]→[8.1722:2216](∅→∅),[8.1722]→[8.1722:2216](∅→∅),[8.2216]→[8.903:920](∅→∅),[8.920]→[8.2240:2324](∅→∅),[8.2240]→[8.2240:2324](∅→∅),[8.2324]→[8.2229:2251](∅→∅),[8.2251]→[8.2346:2429](∅→∅),[8.2346]→[8.2346:2429](∅→∅),[8.2429]→[8.2252:2274](∅→∅),[8.2274]→[8.2451:2944](∅→∅),[8.2451]→[8.2451:2944](∅→∅),[8.3050]→[8.3050:3063](∅→∅),[8.3063]→[8.921:944](∅→∅),[8.944]→[8.3093:3194](∅→∅),[8.3093]→[8.3093:3194](∅→∅),[8.3194]→[8.2275:2297](∅→∅),[8.2297]→[8.3216:3746](∅→∅),[8.3216]→[8.3216:3746](∅→∅),[8.3746]→[8.945:962](∅→∅),[8.962]→[8.3770:3863](∅→∅),[8.3770]→[8.3770:3863](∅→∅),[8.3863]→[8.2298:2320](∅→∅),[8.2320]→[8.3885:3977](∅→∅),[8.3885]→[8.3885:3977](∅→∅),[8.3977]→[8.2321:2343](∅→∅),[8.2343]→[8.3999:4092](∅→∅),[8.3999]→[8.3999:4092](∅→∅),[8.4092]→[8.1:368](∅→∅),[8.474]→[8.474:487](∅→∅),[8.487]→[8.963:986](∅→∅),[8.986]→[8.517:610](∅→∅),[8.517]→[8.517:610](∅→∅),[8.610]→[8.2344:2366](∅→∅),[8.2366]→[8.632:725](∅→∅),[8.632]→[8.632:725](∅→∅),[8.725]→[8.2367:2389](∅→∅),[8.2389]→[8.747:1191](∅→∅),[8.747]→[8.747:1191](∅→∅),[8.1191]→[8.987:1004](∅→∅),[8.1004]→[8.1215:1299](∅→∅),[8.1215]→[8.1215:1299](∅→∅),[8.1299]→[8.2390:2412](∅→∅),[8.2412]→[8.1321:1403](∅→∅),[8.1321]→[8.1321:1403](∅→∅),[8.1403]→[8.2413:2435](∅→∅),[8.2435]→[8.1425:1508](∅→∅),[8.1425]→[8.1425:1508](∅→∅),[8.1508]→[8.4092:4097](∅→∅),[8.4092]→[8.4092:4097](∅→∅),[8.4097]→[8.1:409](∅→∅),[8.515]→[8.515:528](∅→∅),[8.528]→[8.1005:1028](∅→∅),[8.1028]→[8.558:1118](∅→∅),[8.558]→[8.558:1118](∅→∅),[8.1118]→[8.1029:1046](∅→∅),[8.1046]→[8.1142:1251](∅→∅),[8.1142]→[8.1142:1251](∅→∅),[8.1251]→[8.2436:2458](∅→∅),[8.2458]→[8.1273:1388](∅→∅),[8.1273]→[8.1273:1388](∅→∅),[8.1388]→[8.1:381](∅→∅),[8.4097]→[8.1:381](∅→∅),[8.392]→[8.392:405](∅→∅),[8.405]→[8.1047:1070](∅→∅),[8.1070]→[8.513:711](∅→∅),[8.513]→[8.513:711](∅→∅),[8.711]→[8.2459:2481](∅→∅),[8.2481]→[8.733:851](∅→∅),[8.733]→[8.733:851](∅→∅),[8.851]→[8.2482:2504](∅→∅),[8.2504]→[8.873:1211](∅→∅),[8.873]→[8.873:1211](∅→∅),[8.1211]→[8.1071:1088](∅→∅),[8.1088]→[8.1235:1353](∅→∅),[8.1235]→[8.1235:1353](∅→∅),[8.1353]→[8.2505:2527](∅→∅),[8.2527]→[8.1375:1493](∅→∅),[8.1375]→[8.1375:1493](∅→∅),[8.1493]→[8.2528:2550](∅→∅),[8.2550]→[8.1515:1707](∅→∅),[8.1515]→[8.1515:1707](∅→∅),[8.1749]→[8.1749:1792](∅→∅),[8.1792]→[8.72:155](∅→∅),[8.155]→[8.1871:2046](∅→∅),[8.1170]→[8.1871:2046](∅→∅),[8.1871]→[8.1871:2046](∅→∅),[8.2046]→[8.1:336](∅→∅),[8.442]→[8.442:455](∅→∅),[8.455]→[8.1171:1194](∅→∅),[8.1194]→[8.485:568](∅→∅),[8.485]→[8.485:568](∅→∅),[8.568]→[8.2551:2573](∅→∅),[8.2573]→[8.590:673](∅→∅),[8.590]→[8.590:673](∅→∅),[8.673]→[8.2574:2596](∅→∅),[8.2596]→[8.695:1029](∅→∅),[8.695]→[8.695:1029](∅→∅),[8.1029]→[8.1195:1212](∅→∅),[8.1212]→[8.1053:1130](∅→∅),[8.1053]→[8.1053:1130](∅→∅),[8.1130]→[8.2597:2619](∅→∅),[8.2619]→[8.1152:1226](∅→∅),[8.1152]→[8.1152:1226](∅→∅),[8.1226]→[8.2620:2642](∅→∅),[8.2642]→[8.1248:1707](∅→∅),[8.1248]→[8.1248:1707](∅→∅),[8.1813]→[8.1813:1826](∅→∅),[8.1826]→[8.1213:1236](∅→∅),[8.1236]→[8.1856:1951](∅→∅),[8.1856]→[8.1856:1951](∅→∅),[8.1951]→[8.2643:2665](∅→∅),[8.2665]→[8.1973:2176](∅→∅),[8.1973]→[8.1973:2176](∅→∅),[8.2176]→[8.1237:1254](∅→∅),[8.1254]→[8.2200:2288](∅→∅),[8.2200]→[8.2200:2288](∅→∅),[8.2288]→[8.2666:2688](∅→∅),[8.2688]→[8.2310:2394](∅→∅),[8.2310]→[8.2310:2394](∅→∅),[8.2394]→[8.2689:2711](∅→∅),[8.2711]→[8.2416:2857](∅→∅),[8.2416]→[8.2416:2857](∅→∅),[8.2857]→[8.3720:4223](∅→∅),[8.4234]→[8.4234:4725](∅→∅),[8.2857]→[8.2046:2051](∅→∅),[8.4725]→[8.2046:2051](∅→∅),[8.2046]→[8.2046:2051](∅→∅),[8.2051]→[8.4726:4775](∅→∅),[8.4775]→[4.1:55](∅→∅),[4.55]→[8.4821:5083](∅→∅),[8.4821]→[8.4821:5083](∅→∅),[8.5094]→[8.5094:5629](∅→∅),[8.5629]→[4.56:107](∅→∅),[4.107]→[8.5675:5937](∅→∅),[8.5675]→[8.5675:5937](∅→∅),[8.5948]→[8.5948:6598](∅→∅),[8.6598]→[4.108:154](∅→∅),[4.154]→[8.6644:6879](∅→∅),[8.6644]→[8.6644:6879](∅→∅),[8.6890]→[8.6890:7504](∅→∅),[8.7504]→[4.155:203](∅→∅),[4.203]→[8.7550:7787](∅→∅),[8.7550]→[8.7550:7787](∅→∅),[8.7798]→[8.7798:8374](∅→∅),[8.8374]→[8.2791:3058](∅→∅),[8.3069]→[8.3069:3440](∅→∅),[8.3440]→[8.1255:1278](∅→∅),[8.1278]→[8.3548:3624](∅→∅),[8.3548]→[8.3548:3624](∅→∅),[8.3624]→[8.2712:2734](∅→∅),[8.2734]→[8.3646:3723](∅→∅),[8.3646]→[8.3646:3723](∅→∅),[8.3723]→[8.2735:2757](∅→∅),[8.2757]→[8.3745:3831](∅→∅),[8.3745]→[8.3745:3831](∅→∅),[8.3831]→[8.2:34](∅→∅),[8.34]→[8.3863:4143](∅→∅),[8.3863]→[8.3863:4143](∅→∅),[8.4143]→[8.1279:1296](∅→∅),[8.1296]→[8.4167:4234](∅→∅),[8.4167]→[8.4167:4234](∅→∅),[8.4234]→[8.2758:2780](∅→∅),[8.2780]→[8.4256:4323](∅→∅),[8.4256]→[8.4256:4323](∅→∅),[8.4323]→[8.2781:2803](∅→∅),[8.2803]→[8.4345:4685](∅→∅),[8.4345]→[8.4345:4685](∅→∅),[8.4696]→[8.4696:5074](∅→∅),[8.5074]→[8.1297:1320](∅→∅),[8.1320]→[8.5182:5258](∅→∅),[8.5182]→[8.5182:5258](∅→∅),[8.5258]→[8.2804:2826](∅→∅),[8.2826]→[8.5280:5356](∅→∅),[8.5280]→[8.5280:5356](∅→∅),[8.5356]→[8.2827:2849](∅→∅),[8.2849]→[8.5378:5516](∅→∅),[8.5378]→[8.5378:5516](∅→∅),[8.5516]→[8.35:67](∅→∅),[8.67]→[8.5548:5984](∅→∅),[8.5548]→[8.5548:5984](∅→∅),[8.5984]→[8.1321:1338](∅→∅),[8.1338]→[8.6008:6075](∅→∅),[8.6008]→[8.6008:6075](∅→∅),[8.6075]→[8.2850:2872](∅→∅),[8.2872]→[8.6097:6165](∅→∅),[8.6097]→[8.6097:6165](∅→∅),[8.6165]→[8.2873:2895](∅→∅),[8.2895]→[8.6187:6254](∅→∅),[8.6187]→[8.6187:6254](∅→∅),[8.614]→[8.6254:6259](∅→∅),[8.6254]→[8.6254:6259](∅→∅)
function test_draw_text()io.write('\ntest_draw_text')App.screen.init{width=120, height=60}Lines = load_array{'abc', 'def', 'ghi'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_draw_text/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_draw_text/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_draw_text/screen:3')endfunction test_draw_wrapping_text()io.write('\ntest_draw_wrapping_text')App.screen.init{width=50, height=60}Lines = load_array{'abc', 'defgh', 'xyz'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_draw_wrapping_text/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_draw_wrapping_text/screen:2')y = y + Line_heightApp.screen.check(y, 'gh', 'F - test_draw_wrapping_text/screen:3')endfunction test_draw_word_wrapping_text()io.write('\ntest_draw_word_wrapping_text')App.screen.init{width=60, height=60}Lines = load_array{'abc def ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc ', 'F - test_draw_word_wrapping_text/screen:1')y = y + Line_heightApp.screen.check(y, 'def ', 'F - test_draw_word_wrapping_text/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_draw_word_wrapping_text/screen:3')endfunction test_draw_text_wrapping_within_word()-- arrange a screen line that needs to be split within a wordio.write('\ntest_draw_text_wrapping_within_word')App.screen.init{width=60, height=60}Lines = load_array{'abcd e fghijk', 'xyz'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abcd ', 'F - test_draw_text_wrapping_within_word/screen:1')y = y + Line_heightApp.screen.check(y, 'e fghi', 'F - test_draw_text_wrapping_within_word/screen:2')y = y + Line_heightApp.screen.check(y, 'jk', 'F - test_draw_text_wrapping_within_word/screen:3')endfunction test_edit_wrapping_text()io.write('\ntest_edit_wrapping_text')App.screen.init{width=50, height=60}Lines = load_array{'abc', 'def', 'xyz'}Line_width = App.screen.widthCursor1 = {line=2, pos=4}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.run_after_textinput('g')App.run_after_textinput('h')App.run_after_textinput('i')App.run_after_textinput('j')App.run_after_textinput('k')App.run_after_textinput('l')local y = Margin_topApp.screen.check(y, 'abc', 'F - test_edit_wrapping_text/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_edit_wrapping_text/screen:2')y = y + Line_heightApp.screen.check(y, 'ghij', 'F - test_edit_wrapping_text/screen:3')endfunction test_insert_newline()io.write('\ntest_insert_newline')-- display a few lines with cursor on bottom lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=1, pos=2}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_insert_newline/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_insert_newline/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_insert_newline/baseline/screen:3')-- after hitting the enter key the screen scrolls downApp.run_after_keychord('return')check_eq(Screen_top1.line, 1, 'F - test_insert_newline/screen_top')check_eq(Cursor1.line, 2, 'F - test_insert_newline/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_insert_newline/cursor:pos')y = Margin_topApp.screen.check(y, 'a', 'F - test_insert_newline/screen:1')y = y + Line_heightApp.screen.check(y, 'bc', 'F - test_insert_newline/screen:2')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_insert_newline/screen:3')endfunction test_insert_from_clipboard()io.write('\ntest_insert_from_clipboard')-- display a few lines with cursor on bottom lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=1, pos=2}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_insert_from_clipboard/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_insert_from_clipboard/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_insert_from_clipboard/baseline/screen:3')-- after hitting the enter key the screen scrolls downApp.clipboard = 'xy\nz'App.run_after_keychord('C-v')check_eq(Screen_top1.line, 1, 'F - test_insert_from_clipboard/screen_top')check_eq(Cursor1.line, 2, 'F - test_insert_from_clipboard/cursor:line')check_eq(Cursor1.pos, 2, 'F - test_insert_from_clipboard/cursor:pos')y = Margin_topApp.screen.check(y, 'axy', 'F - test_insert_from_clipboard/screen:1')y = y + Line_heightApp.screen.check(y, 'zbc', 'F - test_insert_from_clipboard/screen:2')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_insert_from_clipboard/screen:3')endfunction test_move_cursor_using_mouse()io.write('\ntest_move_cursor_using_mouse')App.screen.init{width=50, height=60}Lines = load_array{'abc', 'def', 'xyz'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw() -- populate line.y for each line in Lineslocal screen_left_margin = 25 -- pixelsApp.run_after_mouserelease(screen_left_margin+8,Margin_top+5, '1')check_eq(Cursor1.line, 1, 'F - test_move_cursor_using_mouse/cursor:line')check_eq(Cursor1.pos, 2, 'F - test_move_cursor_using_mouse/cursor:pos')endfunction test_pagedown()io.write('\ntest_pagedown')App.screen.init{width=120, height=45}Lines = load_array{'abc', 'def', 'ghi'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}-- initially the first two lines are displayedApp.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_pagedown/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_pagedown/baseline/screen:2')-- after pagedown the bottom line becomes the topApp.run_after_keychord('pagedown')check_eq(Screen_top1.line, 2, 'F - test_pagedown/screen_top')check_eq(Cursor1.line, 2, 'F - test_pagedown/cursor')y = Margin_topApp.screen.check(y, 'def', 'F - test_pagedown/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_pagedown/screen:2')endfunction test_pagedown_skips_drawings()io.write('\ntest_pagedown_skips_drawings')-- some lines of text with a drawing intermixedApp.screen.init{width=50, height=80}Lines = load_array{'abc', -- height 15'```lines', '```', -- height 25'def', -- height 15'ghi'} -- height 15check_eq(Lines[2].mode, 'drawing', 'F - test_pagedown_skips_drawings/baseline/lines')Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}local drawing_height = 20 + App.screen.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 80pxApp.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_pagedown_skips_drawings/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 80pxApp.run_after_keychord('pagedown')check_eq(Screen_top1.line, 2, 'F - test_pagedown_skips_drawings/screen_top')check_eq(Cursor1.line, 3, 'F - test_pagedown_skips_drawings/cursor')y = Margin_top + drawing_heightApp.screen.check(y, 'def', 'F - test_pagedown_skips_drawings/screen:1')endfunction test_pagedown_shows_one_screen_line_in_common()io.write('\ntest_pagedown_shows_one_screen_line_in_common')-- some lines of text with a drawing intermixedApp.screen.init{width=50, height=60}Lines = load_array{'abc', 'def ghi jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_pagedown_shows_one_screen_line_in_common/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def ', 'F - test_pagedown_shows_one_screen_line_in_common/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi ', 'F - test_pagedown_shows_one_screen_line_in_common/baseline/screen:3')-- after pagedown the bottom screen line becomes the topApp.run_after_keychord('pagedown')check_eq(Screen_top1.line, 2, 'F - test_pagedown_shows_one_screen_line_in_common/screen_top:line')check_eq(Screen_top1.pos, 5, 'F - test_pagedown_shows_one_screen_line_in_common/screen_top:pos')check_eq(Cursor1.line, 2, 'F - test_pagedown_shows_one_screen_line_in_common/cursor:line')check_eq(Cursor1.pos, 5, 'F - test_pagedown_shows_one_screen_line_in_common/cursor:pos')y = Margin_topApp.screen.check(y, 'ghi ', 'F - test_pagedown_shows_one_screen_line_in_common/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_pagedown_shows_one_screen_line_in_common/screen:2')y = y + Line_heightApp.screen.check(y, 'mn', 'F - test_pagedown_shows_one_screen_line_in_common/screen:3')endfunction test_down_arrow_moves_cursor()io.write('\ntest_down_arrow_moves_cursor')App.screen.init{width=120, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}-- initially the first three lines are displayedApp.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_down_arrow_moves_cursor/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_down_arrow_moves_cursor/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_down_arrow_moves_cursor/baseline/screen:3')-- after hitting the down arrow, the cursor moves down by 1 lineApp.run_after_keychord('down')check_eq(Screen_top1.line, 1, 'F - test_down_arrow_moves_cursor/screen_top')check_eq(Cursor1.line, 2, 'F - test_down_arrow_moves_cursor/cursor')-- the screen is unchangedy = Margin_topApp.screen.check(y, 'abc', 'F - test_down_arrow_moves_cursor/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_down_arrow_moves_cursor/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_down_arrow_moves_cursor/screen:3')endfunction test_down_arrow_scrolls_down_by_one_line()io.write('\ntest_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}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=3, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:3')-- after hitting the down arrow the screen scrolls down by one lineApp.run_after_keychord('down')check_eq(Screen_top1.line, 2, 'F - test_down_arrow_scrolls_down_by_one_line/screen_top')check_eq(Cursor1.line, 4, 'F - test_down_arrow_scrolls_down_by_one_line/cursor')y = Margin_topApp.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_line/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_down_arrow_scrolls_down_by_one_line/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_down_arrow_scrolls_down_by_one_line/screen:3')endfunction test_down_arrow_scrolls_down_by_one_screen_line()io.write('\ntest_down_arrow_scrolls_down_by_one_screen_line')-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=3, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi ', 'F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the down arrow the screen scrolls down by one lineApp.run_after_keychord('down')check_eq(Screen_top1.line, 2, 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen_top')check_eq(Cursor1.line, 3, 'F - test_down_arrow_scrolls_down_by_one_screen_line/cursor:line')check_eq(Cursor1.pos, 5, 'F - test_down_arrow_scrolls_down_by_one_screen_line/cursor:pos')y = Margin_topApp.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi ', 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen:3')endfunction test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word()io.write('\ntest_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=25+30, height=60}Lines = load_array{'abc', 'def', 'ghijkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=3, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghijk', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:3')-- after hitting the down arrow the screen scrolls down by one lineApp.run_after_keychord('down')check_eq(Screen_top1.line, 2, 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen_top')check_eq(Cursor1.line, 3, 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/cursor:line')check_eq(Cursor1.pos, 6, 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/cursor:pos')y = Margin_topApp.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:1')y = y + Line_heightApp.screen.check(y, 'ghijk', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:2')y = y + Line_heightApp.screen.check(y, 'l', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:3')endfunction test_page_down_followed_by_down_arrow_does_not_scroll_screen_up()io.write('\ntest_page_down_followed_by_down_arrow_does_not_scroll_screen_up')App.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghijkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=3, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghijk', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:3')-- after hitting pagedown the screen scrolls down to start of a long lineApp.run_after_keychord('pagedown')check_eq(Screen_top1.line, 3, 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline2/screen_top')check_eq(Cursor1.line, 3, 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline2/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline2/cursor:pos')-- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll upApp.run_after_keychord('down')check_eq(Screen_top1.line, 3, 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen_top')check_eq(Cursor1.line, 3, 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/cursor:line')check_eq(Cursor1.pos, 6, 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/cursor:pos')y = Margin_topApp.screen.check(y, 'ghijk', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen:1')y = y + Line_heightApp.screen.check(y, 'l', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen:3')endfunction test_up_arrow_moves_cursor()io.write('\ntest_up_arrow_moves_cursor')-- display the first 3 lines with the cursor on the bottom lineApp.screen.init{width=120, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = 120Cursor1 = {line=3, pos=1}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_up_arrow_moves_cursor/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_up_arrow_moves_cursor/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_up_arrow_moves_cursor/baseline/screen:3')-- after hitting the up arrow the cursor moves up by 1 lineApp.run_after_keychord('up')check_eq(Screen_top1.line, 1, 'F - test_up_arrow_moves_cursor/screen_top')check_eq(Cursor1.line, 2, 'F - test_up_arrow_moves_cursor/cursor')-- the screen is unchangedy = Margin_topApp.screen.check(y, 'abc', 'F - test_up_arrow_moves_cursor/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_up_arrow_moves_cursor/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_up_arrow_moves_cursor/screen:3')endfunction test_up_arrow_scrolls_up_by_one_line()io.write('\ntest_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}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = 120Cursor1 = {line=2, pos=1}Screen_top1 = {line=2, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:3')-- after hitting the up arrow the screen scrolls up by one lineApp.run_after_keychord('up')check_eq(Screen_top1.line, 1, 'F - test_up_arrow_scrolls_up_by_one_line/screen_top')check_eq(Cursor1.line, 1, 'F - test_up_arrow_scrolls_up_by_one_line/cursor')y = Margin_topApp.screen.check(y, 'abc', 'F - test_up_arrow_scrolls_up_by_one_line/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_by_one_line/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_by_one_line/screen:3')endfunction test_up_arrow_scrolls_up_by_one_screen_line()io.write('\ntest_up_arrow_scrolls_up_by_one_screen_line')-- display lines starting from second screen line of a lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=3, pos=6}Screen_top1 = {line=3, pos=5}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_by_one_screen_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_up_arrow_scrolls_up_by_one_screen_line/baseline/screen:2')-- after hitting the up arrow the screen scrolls up to first screen lineApp.run_after_keychord('up')y = Margin_topApp.screen.check(y, 'ghi ', 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen:3')check_eq(Screen_top1.line, 3, 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen_top')check_eq(Screen_top1.pos, 1, 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen_top')check_eq(Cursor1.line, 3, 'F - test_up_arrow_scrolls_up_by_one_screen_line/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_up_arrow_scrolls_up_by_one_screen_line/cursor:pos')endfunction test_up_arrow_scrolls_up_to_final_screen_line()io.write('\ntest_up_arrow_scrolls_up_to_final_screen_line')-- display lines starting just after a long lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=2, pos=1}Screen_top1 = {line=2, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:3')-- after hitting the up arrow the screen scrolls up to final screen line of previous lineApp.run_after_keychord('up')y = Margin_topApp.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen:3')check_eq(Screen_top1.line, 1, 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen_top')check_eq(Screen_top1.pos, 5, 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen_top')check_eq(Cursor1.line, 1, 'F - test_up_arrow_scrolls_up_to_final_screen_line/cursor:line')check_eq(Cursor1.pos, 5, 'F - test_up_arrow_scrolls_up_to_final_screen_line/cursor:pos')endfunction test_up_arrow_scrolls_up_to_empty_line()io.write('\ntest_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}Lines = load_array{'', 'abc', 'def', 'ghi', 'jkl'}Line_width = 120Cursor1 = {line=2, pos=1}Screen_top1 = {line=2, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_up_arrow_scrolls_up_to_empty_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_to_empty_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_to_empty_line/baseline/screen:3')-- after hitting the up arrow the screen scrolls up by one lineApp.run_after_keychord('up')check_eq(Screen_top1.line, 1, 'F - test_up_arrow_scrolls_up_to_empty_line/screen_top')check_eq(Cursor1.line, 1, 'F - test_up_arrow_scrolls_up_to_empty_line/cursor')y = Margin_top-- empty first liney = y + Line_heightApp.screen.check(y, 'abc', 'F - test_up_arrow_scrolls_up_to_empty_line/screen:2')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_to_empty_line/screen:3')endfunction test_pageup()io.write('\ntest_pageup')App.screen.init{width=120, height=45}Lines = load_array{'abc', 'def', 'ghi'}Line_width = App.screen.widthCursor1 = {line=2, pos=1}Screen_top1 = {line=2, pos=1}Screen_bottom1 = {}-- initially the last two lines are displayedApp.draw()local y = Margin_topApp.screen.check(y, 'def', 'F - test_pageup/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_pageup/baseline/screen:2')-- after pageup the cursor goes to first lineApp.run_after_keychord('pageup')check_eq(Screen_top1.line, 1, 'F - test_pageup/screen_top')check_eq(Cursor1.line, 1, 'F - test_pageup/cursor')y = Margin_topApp.screen.check(y, 'abc', 'F - test_pageup/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_pageup/screen:2')endfunction test_pageup_scrolls_up_by_screen_line()io.write('\ntest_pageup_scrolls_up_by_screen_line')-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=2, pos=1}Screen_top1 = {line=2, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'ghi', 'F - test_pageup_scrolls_up_by_screen_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_pageup_scrolls_up_by_screen_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_pageup_scrolls_up_by_screen_line/baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the page-up key the screen scrolls up to topApp.run_after_keychord('pageup')check_eq(Screen_top1.line, 1, 'F - test_pageup_scrolls_up_by_screen_line/screen_top')check_eq(Cursor1.line, 1, 'F - test_pageup_scrolls_up_by_screen_line/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_pageup_scrolls_up_by_screen_line/cursor:pos')y = Margin_topApp.screen.check(y, 'abc ', 'F - test_pageup_scrolls_up_by_screen_line/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_pageup_scrolls_up_by_screen_line/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_pageup_scrolls_up_by_screen_line/screen:3')endfunction test_pageup_scrolls_up_from_middle_screen_line()io.write('\ntest_pageup_scrolls_up_from_middle_screen_line')-- display a few lines starting from the middle of a line (Cursor1.pos > 1)App.screen.init{width=25+30, height=60}Lines = load_array{'abc def', 'ghi jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=2, pos=5}Screen_top1 = {line=2, pos=5}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'jkl', 'F - test_pageup_scrolls_up_from_middle_screen_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_pageup_scrolls_up_from_middle_screen_line/baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the page-up key the screen scrolls up to topApp.run_after_keychord('pageup')check_eq(Screen_top1.line, 1, 'F - test_pageup_scrolls_up_from_middle_screen_line/screen_top')check_eq(Cursor1.line, 1, 'F - test_pageup_scrolls_up_from_middle_screen_line/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_pageup_scrolls_up_from_middle_screen_line/cursor:pos')y = Margin_topApp.screen.check(y, 'abc ', 'F - test_pageup_scrolls_up_from_middle_screen_line/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_pageup_scrolls_up_from_middle_screen_line/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi ', 'F - test_pageup_scrolls_up_from_middle_screen_line/screen:3')endfunction test_enter_on_bottom_line_scrolls_down()io.write('\ntest_enter_on_bottom_line_scrolls_down')-- display a few lines with cursor on bottom lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=3, pos=2}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_enter_on_bottom_line_scrolls_down/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_enter_on_bottom_line_scrolls_down/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_enter_on_bottom_line_scrolls_down/baseline/screen:3')-- after hitting the enter key the screen scrolls downApp.run_after_keychord('return')check_eq(Screen_top1.line, 2, 'F - test_enter_on_bottom_line_scrolls_down/screen_top')check_eq(Cursor1.line, 4, 'F - test_enter_on_bottom_line_scrolls_down/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_enter_on_bottom_line_scrolls_down/cursor:pos')y = Margin_topApp.screen.check(y, 'def', 'F - test_enter_on_bottom_line_scrolls_down/screen:1')y = y + Line_heightApp.screen.check(y, 'g', 'F - test_enter_on_bottom_line_scrolls_down/screen:2')y = y + Line_heightApp.screen.check(y, 'hi', 'F - test_enter_on_bottom_line_scrolls_down/screen:3')endfunction test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()io.write('\ntest_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom')-- display just the bottom line on screenApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = App.screen.widthCursor1 = {line=4, pos=2}Screen_top1 = {line=4, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'jkl', 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/baseline/screen:1')-- after hitting the enter key the screen does not scroll downApp.run_after_keychord('return')check_eq(Screen_top1.line, 4, 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/screen_top')check_eq(Cursor1.line, 5, 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/cursor:pos')y = Margin_topApp.screen.check(y, 'j', 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/screen:1')y = y + Line_heightApp.screen.check(y, 'kl', 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/screen:2')endfunction test_position_cursor_on_recently_edited_wrapping_line()-- draw a line wrapping over 2 screen linesio.write('\ntest_position_cursor_on_recently_edited_wrapping_line')App.screen.init{width=120, height=200}Lines = load_array{'abc def ghi jkl mno pqr ', 'xyz'}Line_width = 100Cursor1 = {line=1, pos=25}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_top-- I don't understand why 120px fits so much on a fake screen, but whatever..App.screen.check(y, 'abc def ghi ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline1/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl mno pqr ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline1/screen:2')y = y + Line_heightApp.screen.check(y, 'xyz', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline1/screen:3')-- add to the line until it's wrapping over 3 screen linesApp.run_after_textinput('s')App.run_after_textinput('t')App.run_after_textinput('u')check_eq(Cursor1.pos, 28, 'F - test_move_cursor_using_mouse/cursor:pos')y = Margin_topApp.screen.check(y, 'abc def ghi ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl mno pqr ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:2')y = y + Line_heightApp.screen.check(y, 'stu', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:3')-- try to move the cursor earlier in the third screen line by clicking the mouselocal screen_left_margin = 25 -- pixelsApp.run_after_mouserelease(screen_left_margin+8,Margin_top+Line_height*2+5, '1')-- cursor should movecheck_eq(Cursor1.line, 1, 'F - test_move_cursor_using_mouse/cursor:line')check_eq(Cursor1.pos, 26, 'F - test_move_cursor_using_mouse/cursor:pos')endfunction test_backspace_can_scroll_up()io.write('\ntest_backspace_can_scroll_up')-- display the lines 2/3/4 with the cursor on line 2App.screen.init{width=120, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl'}Line_width = 120Cursor1 = {line=2, pos=1}Screen_top1 = {line=2, pos=1}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'def', 'F - test_backspace_can_scroll_up/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_backspace_can_scroll_up/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_backspace_can_scroll_up/baseline/screen:3')-- after hitting backspace the screen scrolls up by one lineApp.run_after_keychord('backspace')check_eq(Screen_top1.line, 1, 'F - test_backspace_can_scroll_up/screen_top')check_eq(Cursor1.line, 1, 'F - test_backspace_can_scroll_up/cursor')y = Margin_topApp.screen.check(y, 'abcdef', 'F - test_backspace_can_scroll_up/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi', 'F - test_backspace_can_scroll_up/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_backspace_can_scroll_up/screen:3')endfunction test_backspace_can_scroll_up_screen_line()io.write('\ntest_backspace_can_scroll_up_screen_line')-- display lines starting from second screen line of a lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=3, pos=5}Screen_top1 = {line=3, pos=5}Screen_bottom1 = {}App.draw()local y = Margin_topApp.screen.check(y, 'jkl', 'F - test_backspace_can_scroll_up_screen_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_backspace_can_scroll_up_screen_line/baseline/screen:2')-- after hitting backspace the screen scrolls up by one screen lineApp.run_after_keychord('backspace')y = Margin_topApp.screen.check(y, 'ghijk', 'F - test_backspace_can_scroll_up_screen_line/screen:1')y = y + Line_heightApp.screen.check(y, 'l', 'F - test_backspace_can_scroll_up_screen_line/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_backspace_can_scroll_up_screen_line/screen:3')check_eq(Screen_top1.line, 3, 'F - test_backspace_can_scroll_up_screen_line/screen_top')check_eq(Screen_top1.pos, 1, 'F - test_backspace_can_scroll_up_screen_line/screen_top')check_eq(Cursor1.line, 3, 'F - test_backspace_can_scroll_up_screen_line/cursor:line')check_eq(Cursor1.pos, 4, 'F - test_backspace_can_scroll_up_screen_line/cursor:pos')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()io.write('\ntest_backspace_over_selection')-- select just one character within a line with cursor before selectionApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=1, pos=1}Selection1 = {line=1, pos=2}-- backspace deletes the selected character, even though it's after the cursorApp.run_after_keychord('backspace')check_eq(Lines[1].data, 'bc', "F - test_backspace_over_selection/data")-- cursor (remains) at start of selectioncheck_eq(Cursor1.line, 1, "F - test_backspace_over_selection/cursor:line")check_eq(Cursor1.pos, 1, "F - test_backspace_over_selection/cursor:pos")-- selection is clearedcheck_nil(Selection1.line, "F - test_backspace_over_selection/selection")endfunction test_backspace_over_selection_reverse()io.write('\ntest_backspace_over_selection_reverse')-- select just one character within a line with cursor after selectionApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=1, pos=2}Selection1 = {line=1, pos=1}-- backspace deletes the selected characterApp.run_after_keychord('backspace')check_eq(Lines[1].data, 'bc', "F - test_backspace_over_selection_reverse/data")-- cursor moves to start of selectioncheck_eq(Cursor1.line, 1, "F - test_backspace_over_selection_reverse/cursor:line")check_eq(Cursor1.pos, 1, "F - test_backspace_over_selection_reverse/cursor:pos")-- selection is clearedcheck_nil(Selection1.line, "F - test_backspace_over_selection_reverse/selection")endfunction test_backspace_over_multiple_lines()io.write('\ntest_backspace_over_multiple_lines')-- select just one character within a line with cursor after selectionApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=1, pos=2}Selection1 = {line=4, pos=2}-- backspace deletes the region and joins the remaining portions of lines on either sideApp.run_after_keychord('backspace')check_eq(Lines[1].data, 'akl', "F - test_backspace_over_multiple_lines/data:1")check_eq(Lines[2].data, 'mno', "F - test_backspace_over_multiple_lines/data:2")-- cursor remains at start of selectioncheck_eq(Cursor1.line, 1, "F - test_backspace_over_multiple_lines/cursor:line")check_eq(Cursor1.pos, 2, "F - test_backspace_over_multiple_lines/cursor:pos")-- selection is clearedcheck_nil(Selection1.line, "F - test_backspace_over_multiple_lines/selection")endfunction test_backspace_to_end_of_line()io.write('\ntest_backspace_to_end_of_line')-- select region from cursor to end of lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=1, pos=2}Selection1 = {line=1, pos=4}-- backspace deletes rest of line without joining to any other lineApp.run_after_keychord('backspace')check_eq(Lines[1].data, 'a', "F - test_backspace_to_start_of_line/data:1")check_eq(Lines[2].data, 'def', "F - test_backspace_to_start_of_line/data:2")-- cursor remains at start of selectioncheck_eq(Cursor1.line, 1, "F - test_backspace_to_start_of_line/cursor:line")check_eq(Cursor1.pos, 2, "F - test_backspace_to_start_of_line/cursor:pos")-- selection is clearedcheck_nil(Selection1.line, "F - test_backspace_to_start_of_line/selection")endfunction test_backspace_to_start_of_line()io.write('\ntest_backspace_to_start_of_line')-- select region from cursor to start of lineApp.screen.init{width=25+30, height=60}Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}Line_width = App.screen.widthCursor1 = {line=2, pos=1}Selection1 = {line=2, pos=3}-- backspace deletes beginning of line without joining to any other lineApp.run_after_keychord('backspace')check_eq(Lines[1].data, 'abc', "F - test_backspace_to_start_of_line/data:1")check_eq(Lines[2].data, 'f', "F - test_backspace_to_start_of_line/data:2")-- cursor remains at start of selectioncheck_eq(Cursor1.line, 2, "F - test_backspace_to_start_of_line/cursor:line")check_eq(Cursor1.pos, 1, "F - test_backspace_to_start_of_line/cursor:pos")-- selection is clearedcheck_nil(Selection1.line, "F - test_backspace_to_start_of_line/selection")endfunction test_undo_insert_text()io.write('\ntest_undo_insert_text')App.screen.init{width=120, height=60}Lines = load_array{'abc', 'def', 'xyz'}Line_width = App.screen.widthCursor1 = {line=2, pos=4}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}-- insert a characterApp.run_after_textinput('g')check_eq(Cursor1.line, 2, 'F - test_undo_insert_text/baseline/cursor:line')check_eq(Cursor1.pos, 5, 'F - test_undo_insert_text/baseline/cursor:pos')check_nil(Selection1.line, 'F - test_undo_insert_text/baseline/selection:line')check_nil(Selection1.pos, 'F - test_undo_insert_text/baseline/selection:pos')local y = Margin_topApp.screen.check(y, 'abc', 'F - test_undo_insert_text/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'defg', 'F - test_undo_insert_text/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'xyz', 'F - test_undo_insert_text/baseline/screen:3')-- undoApp.run_after_keychord('C-z')check_eq(Cursor1.line, 2, 'F - test_undo_insert_text/cursor:line')check_eq(Cursor1.pos, 4, 'F - test_undo_insert_text/cursor:pos')check_nil(Selection1.line, 'F - test_undo_insert_text/selection:line')check_nil(Selection1.pos, 'F - test_undo_insert_text/selection:pos')y = Margin_topApp.screen.check(y, 'abc', 'F - test_undo_insert_text/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_undo_insert_text/screen:2')y = y + Line_heightApp.screen.check(y, 'xyz', 'F - test_undo_insert_text/screen:3')endfunction test_undo_delete_text()io.write('\ntest_undo_delete_text')App.screen.init{width=120, height=60}Lines = load_array{'abc', 'defg', 'xyz'}Line_width = App.screen.widthCursor1 = {line=2, pos=5}Screen_top1 = {line=1, pos=1}Screen_bottom1 = {}-- delete a characterApp.run_after_keychord('backspace')check_eq(Cursor1.line, 2, 'F - test_undo_delete_text/baseline/cursor:line')check_eq(Cursor1.pos, 4, 'F - test_undo_delete_text/baseline/cursor:pos')check_nil(Selection1.line, 'F - test_undo_delete_text/baseline/selection:line')check_nil(Selection1.pos, 'F - test_undo_delete_text/baseline/selection:pos')local y = Margin_topApp.screen.check(y, 'abc', 'F - test_undo_delete_text/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_undo_delete_text/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'xyz', 'F - test_undo_delete_text/baseline/screen:3')-- undo--? -- after undo, the backspaced key is selectedApp.run_after_keychord('C-z')check_eq(Cursor1.line, 2, 'F - test_undo_delete_text/cursor:line')check_eq(Cursor1.pos, 5, 'F - test_undo_delete_text/cursor:pos')check_nil(Selection1.line, 'F - test_undo_delete_text/selection:line')check_nil(Selection1.pos, 'F - test_undo_delete_text/selection:pos')--? check_eq(Selection1.line, 2, 'F - test_undo_delete_text/selection:line')--? check_eq(Selection1.pos, 4, 'F - test_undo_delete_text/selection:pos')y = Margin_topApp.screen.check(y, 'abc', 'F - test_undo_delete_text/screen:1')y = y + Line_heightApp.screen.check(y, 'defg', 'F - test_undo_delete_text/screen:2')y = y + Line_heightApp.screen.check(y, 'xyz', 'F - test_undo_delete_text/screen:3')end - file addition: select.lua[47.2]
-- helpers for selecting portions of textlocal utf8 = require 'utf8'-- Return any intersection of the region from Selection1 to Cursor1 (or-- current mouse, if mouse is pressed; or recent mouse if mouse is pressed and-- currently over a drawing) with the region between {line=line_index, pos=apos}-- and {line=line_index, pos=bpos}.-- apos must be less than bpos. However Selection1 and Cursor1 can be in any order.-- Result: positions spos,epos between apos,bpos.function Text.clip_selection(line_index, apos, bpos)if Selection1.line == nil then return nil,nil end-- min,max = sorted(Selection1,Cursor1)local minl,minp = Selection1.line,Selection1.poslocal maxl,maxpif love.mouse.isDown('1') thenmaxl,maxp = Text.mouse_pos()elsemaxl,maxp = Cursor1.line,Cursor1.posendif minl > maxl thenminl,maxl = maxl,minlminp,maxp = maxp,minpelseif minl == maxl thenif minp > maxp thenminp,maxp = maxp,minpendend-- check if intervals are disjointif line_index < minl then return nil,nil endif line_index > maxl then return nil,nil endif line_index == minl and bpos <= minp then return nil,nil endif line_index == maxl and apos >= maxp then return nil,nil end-- compare bounds more carefully (start inclusive, end exclusive)local a_ge = Text.le1({line=minl, pos=minp}, {line=line_index, pos=apos})local b_lt = Text.lt1({line=line_index, pos=bpos}, {line=maxl, pos=maxp})--? print(minl,line_index,maxl, '--', minp,apos,bpos,maxp, '--', a_ge,b_lt)if a_ge and b_lt then-- fully containedreturn apos,bposelseif a_ge thenassert(maxl == line_index)return apos,maxpelseif b_lt thenassert(minl == line_index)return minp,bposelseassert(minl == maxl and minl == line_index)return minp,maxpendend-- inefficient for some reason, so don't do it on every framefunction Text.mouse_pos()local time = love.timer.getTime()if Recent_mouse.time and Recent_mouse.time > time-0.1 thenreturn Recent_mouse.line, Recent_mouse.posendRecent_mouse.time = timelocal line,pos = Text.to_pos(love.mouse.getX(), love.mouse.getY())if line thenRecent_mouse.line = lineRecent_mouse.pos = posendreturn Recent_mouse.line, Recent_mouse.posendfunction Text.to_pos(x,y)for line_index,line in ipairs(Lines) doif line.mode == 'text' thenif Text.in_line(line, x,y) thenreturn line_index, Text.to_pos_on_line(line, x,y)endendendendfunction Text.delete_selection()local minl,maxl = minmax(Selection1.line, Cursor1.line)local before = snapshot(minl, maxl)Text.delete_selection_without_undo()record_undo_event({before=before, after=snapshot(Cursor1.line)})endfunction Text.delete_selection_without_undo()if Selection1.line == nil then return end-- min,max = sorted(Selection1,Cursor1)local minl,minp = Selection1.line,Selection1.poslocal maxl,maxp = Cursor1.line,Cursor1.posif minl > maxl thenminl,maxl = maxl,minlminp,maxp = maxp,minpelseif minl == maxl thenif minp > maxp thenminp,maxp = maxp,minpendend-- update Cursor1 and Selection1Cursor1.line = minlCursor1.pos = minpSelection1 = {}-- delete everything between min (inclusive) and max (exclusive)Lines[minl].fragments = nilLines[minl].screen_line_starting_pos = nillocal min_offset = utf8.offset(Lines[minl].data, minp)local max_offset = utf8.offset(Lines[maxl].data, maxp)if minl == maxl then--? print('minl == maxl')Lines[minl].data = Lines[minl].data:sub(1, min_offset-1)..Lines[minl].data:sub(max_offset)returnendassert(minl < maxl)local rhs = Lines[maxl].data:sub(max_offset)for i=maxl,minl+1,-1 dotable.remove(Lines, i)endLines[minl].data = Lines[minl].data:sub(1, min_offset-1)..rhsendfunction Text.selection()if Selection1.line == nil then return end-- min,max = sorted(Selection1,Cursor1)local minl,minp = Selection1.line,Selection1.poslocal maxl,maxp = Cursor1.line,Cursor1.posif minl > maxl thenminl,maxl = maxl,minlminp,maxp = maxp,minpelseif minl == maxl thenif minp > maxp thenminp,maxp = maxp,minpendendlocal min_offset = utf8.offset(Lines[minl].data, minp)local max_offset = utf8.offset(Lines[maxl].data, maxp)if minl == maxl thenreturn Lines[minl].data:sub(min_offset, max_offset-1)endassert(minl < maxl)local result = Lines[minl].data:sub(min_offset)..'\n'for i=minl+1,maxl-1 doif Lines[i].mode == 'text' thenresult = result..Lines[i].data..'\n'endendresult = result..Lines[maxl].data:sub(1, max_offset-1)return resultendfunction Text.cut_selection()local result = Text.selection()Text.delete_selection()return resultend - file addition: search.lua[47.2]
-- helpers for the search bar (C-f)function Text.draw_search_bar()local h = Line_height+2local y = App.screen.height-hlove.graphics.setColor(0.9,0.9,0.9)love.graphics.rectangle('fill', 0, y-10, App.screen.width-1, h+8)love.graphics.setColor(0.6,0.6,0.6)love.graphics.line(0, y-10, App.screen.width-1, y-10)love.graphics.setColor(1,1,1)love.graphics.rectangle('fill', 20, y-6, App.screen.width-40, h+2, 2,2)love.graphics.setColor(0.6,0.6,0.6)love.graphics.rectangle('line', 20, y-6, App.screen.width-40, h+2, 2,2)love.graphics.setColor(0,0,0)App.screen.print(Search_term, 25,y-5)love.graphics.setColor(1,0,0)if Search_text == nil thenSearch_text = App.newText(love.graphics.getFont(), Search_term)endlove.graphics.circle('fill', 25+App.width(Search_text),y-5+h, 2)love.graphics.setColor(0,0,0)endfunction Text.search_next()-- search current linelocal pos = Lines[Cursor1.line].data:find(Search_term, Cursor1.pos)if pos thenCursor1.pos = posendif pos == nil thenfor i=Cursor1.line+1,#Lines dopos = Lines[i].data:find(Search_term)if pos thenCursor1.line = iCursor1.pos = posbreakendendendif pos == nil then-- wrap aroundfor i=1,Cursor1.line-1 dopos = Lines[i].data:find(Search_term)if pos thenCursor1.line = iCursor1.pos = posbreakendendendif pos == nil thenCursor1.line = Search_backup.cursor.lineCursor1.pos = Search_backup.cursor.posScreen_top1.line = Search_backup.screen_top.lineScreen_top1.pos = Search_backup.screen_top.posendif Text.lt1(Cursor1, Screen_top1) or Text.lt1(Screen_bottom1, Cursor1) thenScreen_top1.line = Cursor1.linelocal _, pos = Text.pos_at_start_of_cursor_screen_line()Screen_top1.pos = posendendfunction Text.search_previous()-- search current linelocal pos = rfind(Lines[Cursor1.line].data, Search_term, Cursor1.pos)if pos thenCursor1.pos = posendif pos == nil thenfor i=Cursor1.line-1,1,-1 dopos = rfind(Lines[i].data, Search_term)if pos thenCursor1.line = iCursor1.pos = posbreakendendendif pos == nil then-- wrap aroundfor i=#Lines,Cursor1.line+1,-1 dopos = rfind(Lines[i].data, Search_term)if pos thenCursor1.line = iCursor1.pos = posbreakendendendif pos == nil thenCursor1.line = Search_backup.cursor.lineCursor1.pos = Search_backup.cursor.posScreen_top1.line = Search_backup.screen_top.lineScreen_top1.pos = Search_backup.screen_top.posendif Text.lt1(Cursor1, Screen_top1) or Text.lt1(Screen_bottom1, Cursor1) thenScreen_top1.line = Cursor1.linelocal _, pos = Text.pos_at_start_of_cursor_screen_line()Screen_top1.pos = posendendfunction rfind(s, pat, i)local rs = s:reverse()local rpat = pat:reverse()if i == nil then i = #s endlocal ri = #s - i + 1local rendpos = rs:find(rpat, ri)if rendpos == nil then return nil endlocal endpos = #s - rendpos + 1assert (endpos >= #pat)return endpos-#pat+1end