I'd always had a funny feeling there was something missing there but somehow never thought of the right failing test.
HTWAM4NZFOY463TNSKYIM2EWB7QNBGDRRTTGHF5N3Z4TGC7Q3SFAC NZKYPBSKYJ7NQU7ABRHLYZ2P2P5V2UF76OLRURGTGRUB54R4SPBQC EBBFOW4X72TN445NM5MJQPEKN3PIQMRCM3V56YEEFY6PBGLPJO3AC LXTTOB33N2HCUZFIUDRQGGBVHK2HODRG4NBLH6RXRQZDCHF27BSAC 4KOI3E6RUU7IZ3WFPHUVZUO2MANLMND3T7ZQENAV5ESCU7HDLYTAC CG3264MMJTTSCJWUA2EMTBOPTDB2NZIJ7XICKHWUTZ4UWLFP7POAC BULPIBEGL7TMK6CVIE7IS7WGAHGOSUJBGJSFQK542MOWGHP2ADQQC DHI6IJCNSTHGED67T6H5X6Y636C7PIDGIJD32HBEKLT5WIMRS5MAC X75QPYVWFSE7RVAJXRPA2I3AJOXOP653W7Y7NZG5XAEBR7MZU5QQC XNFTJHC4QSHNSIWNN7K6QZEZ37GTQYKHS4EPNSVPQCUSWREROGIQC ZPUQSPQPQFVRUIHGLAWW3IDBYODIWDHO62HAC3WWF5TM3CIJGHNQC KECEMMMRW2VVBZ567HJQPGLC57LTSBKWH7UFP32IW43D23X6WTEQC SVJZZDC3K6AKAXHGRNAZKRE2ZXEKJANNLG7LSSUZJARFBL5F7C4AC endfunction test_left_arrow_scrolls_up_in_wrapped_line()io.write('\ntest_left_arrow_scrolls_up_in_wrapped_line')-- display lines starting from second screen line of a lineApp.screen.init{width=Margin_left+30, height=60}Lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Line_width = App.screen.widthScreen_top1 = {line=3, pos=5}Screen_bottom1 = {}-- cursor is at top of screenCursor1 = {line=3, pos=5}App.draw()local y = Margin_topApp.screen.check(y, 'jkl', 'F - test_left_arrow_scrolls_up_in_wrapped_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_left_arrow_scrolls_up_in_wrapped_line/baseline/screen:2')-- after hitting the left arrow the screen scrolls up to first screen lineApp.run_after_keychord('left')y = Margin_topApp.screen.check(y, 'ghi ', 'F - test_left_arrow_scrolls_up_in_wrapped_line/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_left_arrow_scrolls_up_in_wrapped_line/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_left_arrow_scrolls_up_in_wrapped_line/screen:3')check_eq(Screen_top1.line, 3, 'F - test_left_arrow_scrolls_up_in_wrapped_line/screen_top')check_eq(Screen_top1.pos, 1, 'F - test_left_arrow_scrolls_up_in_wrapped_line/screen_top')check_eq(Cursor1.line, 3, 'F - test_left_arrow_scrolls_up_in_wrapped_line/cursor:line')check_eq(Cursor1.pos, 4, 'F - test_left_arrow_scrolls_up_in_wrapped_line/cursor:pos')endfunction test_right_arrow_scrolls_down_in_wrapped_line()io.write('\ntest_right_arrow_scrolls_down_in_wrapped_line')-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Margin_left+30, height=60}Lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Line_width = App.screen.widthScreen_top1 = {line=1, pos=1}Screen_bottom1 = {}-- cursor is at bottom right of screenCursor1 = {line=3, pos=5}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_right_arrow_scrolls_down_in_wrapped_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_right_arrow_scrolls_down_in_wrapped_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi ', 'F - test_right_arrow_scrolls_down_in_wrapped_line/baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting the right arrow the screen scrolls down by one lineApp.run_after_keychord('right')check_eq(Screen_top1.line, 2, 'F - test_right_arrow_scrolls_down_in_wrapped_line/screen_top')check_eq(Cursor1.line, 3, 'F - test_right_arrow_scrolls_down_in_wrapped_line/cursor:line')check_eq(Cursor1.pos, 6, 'F - test_right_arrow_scrolls_down_in_wrapped_line/cursor:pos')y = Margin_topApp.screen.check(y, 'def', 'F - test_right_arrow_scrolls_down_in_wrapped_line/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi ', 'F - test_right_arrow_scrolls_down_in_wrapped_line/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_right_arrow_scrolls_down_in_wrapped_line/screen:3')endfunction test_home_scrolls_up_in_wrapped_line()io.write('\ntest_home_scrolls_up_in_wrapped_line')-- display lines starting from second screen line of a lineApp.screen.init{width=Margin_left+30, height=60}Lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Line_width = App.screen.widthScreen_top1 = {line=3, pos=5}Screen_bottom1 = {}-- cursor is at top of screenCursor1 = {line=3, pos=5}App.draw()local y = Margin_topApp.screen.check(y, 'jkl', 'F - test_home_scrolls_up_in_wrapped_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_home_scrolls_up_in_wrapped_line/baseline/screen:2')-- after hitting home the screen scrolls up to first screen lineApp.run_after_keychord('home')y = Margin_topApp.screen.check(y, 'ghi ', 'F - test_home_scrolls_up_in_wrapped_line/screen:1')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_home_scrolls_up_in_wrapped_line/screen:2')y = y + Line_heightApp.screen.check(y, 'mno', 'F - test_home_scrolls_up_in_wrapped_line/screen:3')check_eq(Screen_top1.line, 3, 'F - test_home_scrolls_up_in_wrapped_line/screen_top')check_eq(Screen_top1.pos, 1, 'F - test_home_scrolls_up_in_wrapped_line/screen_top')check_eq(Cursor1.line, 3, 'F - test_home_scrolls_up_in_wrapped_line/cursor:line')check_eq(Cursor1.pos, 1, 'F - test_home_scrolls_up_in_wrapped_line/cursor:pos')
function test_end_scrolls_down_in_wrapped_line()io.write('\ntest_end_scrolls_down_in_wrapped_line')-- display the first three lines with the cursor on the bottom lineApp.screen.init{width=Margin_left+30, height=60}Lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}Line_width = App.screen.widthScreen_top1 = {line=1, pos=1}Screen_bottom1 = {}-- cursor is at bottom right of screenCursor1 = {line=3, pos=5}App.draw()local y = Margin_topApp.screen.check(y, 'abc', 'F - test_end_scrolls_down_in_wrapped_line/baseline/screen:1')y = y + Line_heightApp.screen.check(y, 'def', 'F - test_end_scrolls_down_in_wrapped_line/baseline/screen:2')y = y + Line_heightApp.screen.check(y, 'ghi ', 'F - test_end_scrolls_down_in_wrapped_line/baseline/screen:3') -- line wrapping includes trailing whitespace-- after hitting end the screen scrolls down by one lineApp.run_after_keychord('end')check_eq(Screen_top1.line, 2, 'F - test_end_scrolls_down_in_wrapped_line/screen_top')check_eq(Cursor1.line, 3, 'F - test_end_scrolls_down_in_wrapped_line/cursor:line')check_eq(Cursor1.pos, 8, 'F - test_end_scrolls_down_in_wrapped_line/cursor:pos')y = Margin_topApp.screen.check(y, 'def', 'F - test_end_scrolls_down_in_wrapped_line/screen:1')y = y + Line_heightApp.screen.check(y, 'ghi ', 'F - test_end_scrolls_down_in_wrapped_line/screen:2')y = y + Line_heightApp.screen.check(y, 'jkl', 'F - test_end_scrolls_down_in_wrapped_line/screen:3')end
endfunction Text.start_of_line()Cursor1.pos = 1if Text.lt1(Cursor1, Screen_top1) thenScreen_top1 = {line=Cursor1.line, pos=Cursor1.pos} -- copyendendfunction Text.end_of_line()Cursor1.pos = utf8.len(Lines[Cursor1.line].data) + 1local _,botpos = Text.pos_at_start_of_cursor_screen_line()local botline1 = {line=Cursor1.line, pos=botpos}if Text.lt1(Screen_bottom1, botline1) thenText.snap_cursor_to_bottom_of_screen()end