diff options
author | Kartik K. Agaram <vc@akkartik.com> | 2022-06-03 14:14:53 -0700 |
---|---|---|
committer | Kartik K. Agaram <vc@akkartik.com> | 2022-06-03 14:17:41 -0700 |
commit | 0d52962b3e6f91e458fbf858e62e684494373af8 (patch) | |
tree | dfb7a44f830e595a585bfbdd17d4789c8f046fb7 /text.lua | |
parent | 06e6ecdf8f8f8f8489b2e0d1145698a52a7c3ae5 (diff) | |
download | text.love-0d52962b3e6f91e458fbf858e62e684494373af8.tar.gz |
extract a couple of files
Diffstat (limited to 'text.lua')
-rw-r--r-- | text.lua | 1197 |
1 files changed, 4 insertions, 1193 deletions
diff --git a/text.lua b/text.lua index 8412cfb..e57a019 100644 --- a/text.lua +++ b/text.lua @@ -1,9 +1,12 @@ --- primitives for editing text +-- text editor, particularly text drawing, horizontal wrap, vertical scrolling Text = {} local utf8 = require 'utf8' +require 'search' +require 'select' require 'undo' +require 'text_tests' -- return values: -- y coordinate drawn until in px @@ -104,269 +107,6 @@ end -- manual tests: -- draw with small line_width of 100 -function Text.draw_search_bar() - local h = Line_height+2 - local y = App.screen.height-h - love.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 then - Search_text = App.newText(love.graphics.getFont(), Search_term) - end - love.graphics.circle('fill', 25+App.width(Search_text),y-5+h, 2) - love.graphics.setColor(0,0,0) -end - -function Text.search_next() - -- search current line - local pos = Lines[Cursor1.line].data:find(Search_term, Cursor1.pos) - if pos then - Cursor1.pos = pos - end - if pos == nil then - for i=Cursor1.line+1,#Lines do - pos = Lines[i].data:find(Search_term) - if pos then - Cursor1.line = i - Cursor1.pos = pos - break - end - end - end - if pos == nil then - -- wrap around - for i=1,Cursor1.line-1 do - pos = Lines[i].data:find(Search_term) - if pos then - Cursor1.line = i - Cursor1.pos = pos - break - end - end - end - if pos == nil then - Cursor1.line = Search_backup.cursor.line - Cursor1.pos = Search_backup.cursor.pos - Screen_top1.line = Search_backup.screen_top.line - Screen_top1.pos = Search_backup.screen_top.pos - end - if Text.lt1(Cursor1, Screen_top1) or Text.lt1(Screen_bottom1, Cursor1) then - Screen_top1.line = Cursor1.line - local _, pos = Text.pos_at_start_of_cursor_screen_line() - Screen_top1.pos = pos - end -end - -function Text.search_previous() - -- search current line - local pos = rfind(Lines[Cursor1.line].data, Search_term, Cursor1.pos) - if pos then - Cursor1.pos = pos - end - if pos == nil then - for i=Cursor1.line-1,1,-1 do - pos = rfind(Lines[i].data, Search_term) - if pos then - Cursor1.line = i - Cursor1.pos = pos - break - end - end - end - if pos == nil then - -- wrap around - for i=#Lines,Cursor1.line+1,-1 do - pos = rfind(Lines[i].data, Search_term) - if pos then - Cursor1.line = i - Cursor1.pos = pos - break - end - end - end - if pos == nil then - Cursor1.line = Search_backup.cursor.line - Cursor1.pos = Search_backup.cursor.pos - Screen_top1.line = Search_backup.screen_top.line - Screen_top1.pos = Search_backup.screen_top.pos - end - if Text.lt1(Cursor1, Screen_top1) or Text.lt1(Screen_bottom1, Cursor1) then - Screen_top1.line = Cursor1.line - local _, pos = Text.pos_at_start_of_cursor_screen_line() - Screen_top1.pos = pos - end -end - -function rfind(s, pat, i) - local rs = s:reverse() - local rpat = pat:reverse() - if i == nil then i = #s end - local ri = #s - i + 1 - local rendpos = rs:find(rpat, ri) - if rendpos == nil then return nil end - local endpos = #s - rendpos + 1 - assert (endpos >= #pat) - return endpos-#pat+1 -end - --- 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.pos - local maxl,maxp - if love.mouse.isDown('1') then - maxl,maxp = Text.mouse_pos() - else - maxl,maxp = Cursor1.line,Cursor1.pos - end - if minl > maxl then - minl,maxl = maxl,minl - minp,maxp = maxp,minp - elseif minl == maxl then - if minp > maxp then - minp,maxp = maxp,minp - end - end - -- check if intervals are disjoint - if line_index < minl then return nil,nil end - if line_index > maxl then return nil,nil end - if line_index == minl and bpos <= minp then return nil,nil end - if 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 contained - return apos,bpos - elseif a_ge then - assert(maxl == line_index) - return apos,maxp - elseif b_lt then - assert(minl == line_index) - return minp,bpos - else - assert(minl == maxl and minl == line_index) - return minp,maxp - end -end - --- inefficient for some reason, so don't do it on every frame -function Text.mouse_pos() - local time = love.timer.getTime() - if Recent_mouse.time and Recent_mouse.time > time-0.1 then - return Recent_mouse.line, Recent_mouse.pos - end - Recent_mouse.time = time - local line,pos = Text.to_pos(love.mouse.getX(), love.mouse.getY()) - if line then - Recent_mouse.line = line - Recent_mouse.pos = pos - end - return Recent_mouse.line, Recent_mouse.pos -end - -function Text.to_pos(x,y) - for line_index,line in ipairs(Lines) do - if line.mode == 'text' then - if Text.in_line(line, x,y) then - return line_index, Text.to_pos_on_line(line, x,y) - end - end - end -end - -function 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)}) -end - -function Text.delete_selection_without_undo() - if Selection1.line == nil then return end - -- min,max = sorted(Selection1,Cursor1) - local minl,minp = Selection1.line,Selection1.pos - local maxl,maxp = Cursor1.line,Cursor1.pos - if minl > maxl then - minl,maxl = maxl,minl - minp,maxp = maxp,minp - elseif minl == maxl then - if minp > maxp then - minp,maxp = maxp,minp - end - end - -- update Cursor1 and Selection1 - Cursor1.line = minl - Cursor1.pos = minp - Selection1 = {} - -- delete everything between min (inclusive) and max (exclusive) - Lines[minl].fragments = nil - Lines[minl].screen_line_starting_pos = nil - local 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) - return - end - assert(minl < maxl) - local rhs = Lines[maxl].data:sub(max_offset) - for i=maxl,minl+1,-1 do - table.remove(Lines, i) - end - Lines[minl].data = Lines[minl].data:sub(1, min_offset-1)..rhs -end - -function Text.selection() - if Selection1.line == nil then return end - -- min,max = sorted(Selection1,Cursor1) - local minl,minp = Selection1.line,Selection1.pos - local maxl,maxp = Cursor1.line,Cursor1.pos - if minl > maxl then - minl,maxl = maxl,minl - minp,maxp = maxp,minp - elseif minl == maxl then - if minp > maxp then - minp,maxp = maxp,minp - end - end - local min_offset = utf8.offset(Lines[minl].data, minp) - local max_offset = utf8.offset(Lines[maxl].data, maxp) - if minl == maxl then - return Lines[minl].data:sub(min_offset, max_offset-1) - end - assert(minl < maxl) - local result = Lines[minl].data:sub(min_offset)..'\n' - for i=minl+1,maxl-1 do - if Lines[i].mode == 'text' then - result = result..Lines[i].data..'\n' - end - end - result = result..Lines[maxl].data:sub(1, max_offset-1) - return result -end - -function Text.cut_selection() - local result = Text.selection() - Text.delete_selection() - return result -end - function Text.draw_cursor(x, y) love.graphics.setColor(1,0,0) love.graphics.circle('fill', x,y+Line_height, 2) @@ -375,935 +115,6 @@ function Text.draw_cursor(x, y) Cursor_y = y+Line_height end -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.width - Cursor1 = {line=1, pos=1} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_draw_text/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_draw_text/screen:2') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_draw_text/screen:3') -end - -function 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.width - Cursor1 = {line=1, pos=1} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_draw_wrapping_text/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_draw_wrapping_text/screen:2') - y = y + Line_height - App.screen.check(y, 'gh', 'F - test_draw_wrapping_text/screen:3') -end - -function 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.width - Cursor1 = {line=1, pos=1} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abc ', 'F - test_draw_word_wrapping_text/screen:1') - y = y + Line_height - App.screen.check(y, 'def ', 'F - test_draw_word_wrapping_text/screen:2') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_draw_word_wrapping_text/screen:3') -end - -function test_draw_text_wrapping_within_word() - -- arrange a screen line that needs to be split within a word - io.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.width - Cursor1 = {line=1, pos=1} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abcd ', 'F - test_draw_text_wrapping_within_word/screen:1') - y = y + Line_height - App.screen.check(y, 'e fghi', 'F - test_draw_text_wrapping_within_word/screen:2') - y = y + Line_height - App.screen.check(y, 'jk', 'F - test_draw_text_wrapping_within_word/screen:3') -end - -function 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.width - Cursor1 = {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_top - App.screen.check(y, 'abc', 'F - test_edit_wrapping_text/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_edit_wrapping_text/screen:2') - y = y + Line_height - App.screen.check(y, 'ghij', 'F - test_edit_wrapping_text/screen:3') -end - -function test_insert_newline() - io.write('\ntest_insert_newline') - -- display a few lines with cursor on bottom line - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc', 'def', 'ghi', 'jkl'} - Line_width = App.screen.width - Cursor1 = {line=1, pos=2} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_insert_newline/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_insert_newline/baseline/screen:2') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_insert_newline/baseline/screen:3') - -- after hitting the enter key the screen scrolls down - App.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_top - App.screen.check(y, 'a', 'F - test_insert_newline/screen:1') - y = y + Line_height - App.screen.check(y, 'bc', 'F - test_insert_newline/screen:2') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_insert_newline/screen:3') -end - -function test_insert_from_clipboard() - io.write('\ntest_insert_from_clipboard') - -- display a few lines with cursor on bottom line - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc', 'def', 'ghi', 'jkl'} - Line_width = App.screen.width - Cursor1 = {line=1, pos=2} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_insert_from_clipboard/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_insert_from_clipboard/baseline/screen:2') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_insert_from_clipboard/baseline/screen:3') - -- after hitting the enter key the screen scrolls down - App.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_top - App.screen.check(y, 'axy', 'F - test_insert_from_clipboard/screen:1') - y = y + Line_height - App.screen.check(y, 'zbc', 'F - test_insert_from_clipboard/screen:2') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_insert_from_clipboard/screen:3') -end - -function 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.width - Cursor1 = {line=1, pos=1} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() -- populate line.y for each line in Lines - local screen_left_margin = 25 -- pixels - App.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') -end - -function test_pagedown() - io.write('\ntest_pagedown') - App.screen.init{width=120, height=45} - Lines = load_array{'abc', 'def', 'ghi'} - Line_width = App.screen.width - Cursor1 = {line=1, pos=1} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - -- initially the first two lines are displayed - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_pagedown/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_pagedown/baseline/screen:2') - -- after pagedown the bottom line becomes the top - App.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_top - App.screen.check(y, 'def', 'F - test_pagedown/screen:1') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_pagedown/screen:2') -end - -function test_pagedown_skips_drawings() - io.write('\ntest_pagedown_skips_drawings') - -- some lines of text with a drawing intermixed - App.screen.init{width=50, height=80} - Lines = load_array{'abc', -- height 15 - '```lines', '```', -- height 25 - 'def', -- height 15 - 'ghi'} -- height 15 - check_eq(Lines[2].mode, 'drawing', 'F - test_pagedown_skips_drawings/baseline/lines') - Line_width = App.screen.width - Cursor1 = {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 80px - App.draw() - local y = Margin_top - App.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 80px - App.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_height - App.screen.check(y, 'def', 'F - test_pagedown_skips_drawings/screen:1') -end - -function 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 intermixed - App.screen.init{width=50, height=60} - Lines = load_array{'abc', 'def ghi jkl', 'mno'} - Line_width = App.screen.width - Cursor1 = {line=1, pos=1} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_pagedown_shows_one_screen_line_in_common/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'def ', 'F - test_pagedown_shows_one_screen_line_in_common/baseline/screen:2') - y = y + Line_height - App.screen.check(y, 'ghi ', 'F - test_pagedown_shows_one_screen_line_in_common/baseline/screen:3') - -- after pagedown the bottom screen line becomes the top - App.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_top - App.screen.check(y, 'ghi ', 'F - test_pagedown_shows_one_screen_line_in_common/screen:1') - y = y + Line_height - App.screen.check(y, 'jkl', 'F - test_pagedown_shows_one_screen_line_in_common/screen:2') - y = y + Line_height - App.screen.check(y, 'mn', 'F - test_pagedown_shows_one_screen_line_in_common/screen:3') -end - -function 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.width - Cursor1 = {line=1, pos=1} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - -- initially the first three lines are displayed - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_down_arrow_moves_cursor/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_down_arrow_moves_cursor/baseline/screen:2') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_down_arrow_moves_cursor/baseline/screen:3') - -- after hitting the down arrow, the cursor moves down by 1 line - App.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 unchanged - y = Margin_top - App.screen.check(y, 'abc', 'F - test_down_arrow_moves_cursor/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_down_arrow_moves_cursor/screen:2') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_down_arrow_moves_cursor/screen:3') -end - -function 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 line - App.screen.init{width=120, height=60} - Lines = load_array{'abc', 'def', 'ghi', 'jkl'} - Line_width = App.screen.width - Cursor1 = {line=3, pos=1} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_line/baseline/screen:2') - y = y + Line_height - App.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 line - App.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_top - App.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_line/screen:1') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_down_arrow_scrolls_down_by_one_line/screen:2') - y = y + Line_height - App.screen.check(y, 'jkl', 'F - test_down_arrow_scrolls_down_by_one_line/screen:3') -end - -function 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 line - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc', 'def', 'ghi jkl', 'mno'} - Line_width = App.screen.width - Cursor1 = {line=3, pos=1} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line/baseline/screen:2') - y = y + Line_height - App.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 line - App.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_top - App.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen:1') - y = y + Line_height - App.screen.check(y, 'ghi ', 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen:2') - y = y + Line_height - App.screen.check(y, 'jkl', 'F - test_down_arrow_scrolls_down_by_one_screen_line/screen:3') -end - -function 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 line - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc', 'def', 'ghijkl', 'mno'} - Line_width = App.screen.width - Cursor1 = {line=3, pos=1} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/baseline/screen:2') - y = y + Line_height - App.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 line - App.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_top - App.screen.check(y, 'def', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:1') - y = y + Line_height - App.screen.check(y, 'ghijk', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:2') - y = y + Line_height - App.screen.check(y, 'l', 'F - test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word/screen:3') -end - -function 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.width - Cursor1 = {line=3, pos=1} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/baseline/screen:2') - y = y + Line_height - App.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 line - App.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 up - App.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_top - App.screen.check(y, 'ghijk', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen:1') - y = y + Line_height - App.screen.check(y, 'l', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen:2') - y = y + Line_height - App.screen.check(y, 'mno', 'F - test_page_down_followed_by_down_arrow_does_not_scroll_screen_up/screen:3') -end - -function test_up_arrow_moves_cursor() - io.write('\ntest_up_arrow_moves_cursor') - -- display the first 3 lines with the cursor on the bottom line - App.screen.init{width=120, height=60} - Lines = load_array{'abc', 'def', 'ghi', 'jkl'} - Line_width = 120 - Cursor1 = {line=3, pos=1} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_up_arrow_moves_cursor/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_up_arrow_moves_cursor/baseline/screen:2') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_up_arrow_moves_cursor/baseline/screen:3') - -- after hitting the up arrow the cursor moves up by 1 line - App.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 unchanged - y = Margin_top - App.screen.check(y, 'abc', 'F - test_up_arrow_moves_cursor/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_up_arrow_moves_cursor/screen:2') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_up_arrow_moves_cursor/screen:3') -end - -function 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 2 - App.screen.init{width=120, height=60} - Lines = load_array{'abc', 'def', 'ghi', 'jkl'} - Line_width = 120 - Cursor1 = {line=2, pos=1} - Screen_top1 = {line=2, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_by_one_line/baseline/screen:2') - y = y + Line_height - App.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 line - App.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_top - App.screen.check(y, 'abc', 'F - test_up_arrow_scrolls_up_by_one_line/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_by_one_line/screen:2') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_by_one_line/screen:3') -end - -function 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 line - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc', 'def', 'ghi jkl', 'mno'} - Line_width = App.screen.width - Cursor1 = {line=3, pos=6} - Screen_top1 = {line=3, pos=5} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_by_one_screen_line/baseline/screen:1') - y = y + Line_height - App.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 line - App.run_after_keychord('up') - y = Margin_top - App.screen.check(y, 'ghi ', 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen:1') - y = y + Line_height - App.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_by_one_screen_line/screen:2') - y = y + Line_height - App.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') -end - -function 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 line - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc def', 'ghi', 'jkl', 'mno'} - Line_width = App.screen.width - Cursor1 = {line=2, pos=1} - Screen_top1 = {line=2, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'jkl', 'F - test_up_arrow_scrolls_up_to_final_screen_line/baseline/screen:2') - y = y + Line_height - App.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 line - App.run_after_keychord('up') - y = Margin_top - App.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen:1') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_up_arrow_scrolls_up_to_final_screen_line/screen:2') - y = y + Line_height - App.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') -end - -function 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 screen - App.screen.init{width=120, height=60} - Lines = load_array{'', 'abc', 'def', 'ghi', 'jkl'} - Line_width = 120 - Cursor1 = {line=2, pos=1} - Screen_top1 = {line=2, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_up_arrow_scrolls_up_to_empty_line/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_to_empty_line/baseline/screen:2') - y = y + Line_height - App.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 line - App.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 line - y = y + Line_height - App.screen.check(y, 'abc', 'F - test_up_arrow_scrolls_up_to_empty_line/screen:2') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_up_arrow_scrolls_up_to_empty_line/screen:3') -end - -function test_pageup() - io.write('\ntest_pageup') - App.screen.init{width=120, height=45} - Lines = load_array{'abc', 'def', 'ghi'} - Line_width = App.screen.width - Cursor1 = {line=2, pos=1} - Screen_top1 = {line=2, pos=1} - Screen_bottom1 = {} - -- initially the last two lines are displayed - App.draw() - local y = Margin_top - App.screen.check(y, 'def', 'F - test_pageup/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_pageup/baseline/screen:2') - -- after pageup the cursor goes to first line - App.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_top - App.screen.check(y, 'abc', 'F - test_pageup/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_pageup/screen:2') -end - -function 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 line - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc def', 'ghi', 'jkl', 'mno'} - Line_width = App.screen.width - Cursor1 = {line=2, pos=1} - Screen_top1 = {line=2, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'ghi', 'F - test_pageup_scrolls_up_by_screen_line/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'jkl', 'F - test_pageup_scrolls_up_by_screen_line/baseline/screen:2') - y = y + Line_height - App.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 top - App.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_top - App.screen.check(y, 'abc ', 'F - test_pageup_scrolls_up_by_screen_line/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_pageup_scrolls_up_by_screen_line/screen:2') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_pageup_scrolls_up_by_screen_line/screen:3') -end - -function 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.width - Cursor1 = {line=2, pos=5} - Screen_top1 = {line=2, pos=5} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'jkl', 'F - test_pageup_scrolls_up_from_middle_screen_line/baseline/screen:2') - y = y + Line_height - App.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 top - App.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_top - App.screen.check(y, 'abc ', 'F - test_pageup_scrolls_up_from_middle_screen_line/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_pageup_scrolls_up_from_middle_screen_line/screen:2') - y = y + Line_height - App.screen.check(y, 'ghi ', 'F - test_pageup_scrolls_up_from_middle_screen_line/screen:3') -end - -function test_enter_on_bottom_line_scrolls_down() - io.write('\ntest_enter_on_bottom_line_scrolls_down') - -- display a few lines with cursor on bottom line - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc', 'def', 'ghi', 'jkl'} - Line_width = App.screen.width - Cursor1 = {line=3, pos=2} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'abc', 'F - test_enter_on_bottom_line_scrolls_down/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_enter_on_bottom_line_scrolls_down/baseline/screen:2') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_enter_on_bottom_line_scrolls_down/baseline/screen:3') - -- after hitting the enter key the screen scrolls down - App.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_top - App.screen.check(y, 'def', 'F - test_enter_on_bottom_line_scrolls_down/screen:1') - y = y + Line_height - App.screen.check(y, 'g', 'F - test_enter_on_bottom_line_scrolls_down/screen:2') - y = y + Line_height - App.screen.check(y, 'hi', 'F - test_enter_on_bottom_line_scrolls_down/screen:3') -end - -function 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 screen - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc', 'def', 'ghi', 'jkl'} - Line_width = App.screen.width - Cursor1 = {line=4, pos=2} - Screen_top1 = {line=4, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.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 down - App.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_top - App.screen.check(y, 'j', 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/screen:1') - y = y + Line_height - App.screen.check(y, 'kl', 'F - test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom/screen:2') -end - -function test_position_cursor_on_recently_edited_wrapping_line() - -- draw a line wrapping over 2 screen lines - io.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 = 100 - Cursor1 = {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_height - App.screen.check(y, 'jkl mno pqr ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline1/screen:2') - y = y + Line_height - App.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 lines - App.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_top - App.screen.check(y, 'abc def ghi ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:1') - y = y + Line_height - App.screen.check(y, 'jkl mno pqr ', 'F - test_position_cursor_on_recently_edited_wrapping_line/baseline2/screen:2') - y = y + Line_height - App.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 mouse - local screen_left_margin = 25 -- pixels - App.run_after_mouserelease(screen_left_margin+8,Margin_top+Line_height*2+5, '1') - -- cursor should move - check_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') -end - -function test_backspace_can_scroll_up() - io.write('\ntest_backspace_can_scroll_up') - -- display the lines 2/3/4 with the cursor on line 2 - App.screen.init{width=120, height=60} - Lines = load_array{'abc', 'def', 'ghi', 'jkl'} - Line_width = 120 - Cursor1 = {line=2, pos=1} - Screen_top1 = {line=2, pos=1} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'def', 'F - test_backspace_can_scroll_up/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_backspace_can_scroll_up/baseline/screen:2') - y = y + Line_height - App.screen.check(y, 'jkl', 'F - test_backspace_can_scroll_up/baseline/screen:3') - -- after hitting backspace the screen scrolls up by one line - App.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_top - App.screen.check(y, 'abcdef', 'F - test_backspace_can_scroll_up/screen:1') - y = y + Line_height - App.screen.check(y, 'ghi', 'F - test_backspace_can_scroll_up/screen:2') - y = y + Line_height - App.screen.check(y, 'jkl', 'F - test_backspace_can_scroll_up/screen:3') -end - -function 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 line - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc', 'def', 'ghi jkl', 'mno'} - Line_width = App.screen.width - Cursor1 = {line=3, pos=5} - Screen_top1 = {line=3, pos=5} - Screen_bottom1 = {} - App.draw() - local y = Margin_top - App.screen.check(y, 'jkl', 'F - test_backspace_can_scroll_up_screen_line/baseline/screen:1') - y = y + Line_height - App.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 line - App.run_after_keychord('backspace') - y = Margin_top - App.screen.check(y, 'ghijk', 'F - test_backspace_can_scroll_up_screen_line/screen:1') - y = y + Line_height - App.screen.check(y, 'l', 'F - test_backspace_can_scroll_up_screen_line/screen:2') - y = y + Line_height - App.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 keys - -function test_backspace_over_selection() - io.write('\ntest_backspace_over_selection') - -- select just one character within a line with cursor before selection - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'} - Line_width = App.screen.width - Cursor1 = {line=1, pos=1} - Selection1 = {line=1, pos=2} - -- backspace deletes the selected character, even though it's after the cursor - App.run_after_keychord('backspace') - check_eq(Lines[1].data, 'bc', "F - test_backspace_over_selection/data") - -- cursor (remains) at start of selection - check_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 cleared - check_nil(Selection1.line, "F - test_backspace_over_selection/selection") -end - -function test_backspace_over_selection_reverse() - io.write('\ntest_backspace_over_selection_reverse') - -- select just one character within a line with cursor after selection - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'} - Line_width = App.screen.width - Cursor1 = {line=1, pos=2} - Selection1 = {line=1, pos=1} - -- backspace deletes the selected character - App.run_after_keychord('backspace') - check_eq(Lines[1].data, 'bc', "F - test_backspace_over_selection_reverse/data") - -- cursor moves to start of selection - check_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 cleared - check_nil(Selection1.line, "F - test_backspace_over_selection_reverse/selection") -end - -function test_backspace_over_multiple_lines() - io.write('\ntest_backspace_over_multiple_lines') - -- select just one character within a line with cursor after selection - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'} - Line_width = App.screen.width - Cursor1 = {line=1, pos=2} - Selection1 = {line=4, pos=2} - -- backspace deletes the region and joins the remaining portions of lines on either side - App.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 selection - check_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 cleared - check_nil(Selection1.line, "F - test_backspace_over_multiple_lines/selection") -end - -function test_backspace_to_end_of_line() - io.write('\ntest_backspace_to_end_of_line') - -- select region from cursor to end of line - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'} - Line_width = App.screen.width - Cursor1 = {line=1, pos=2} - Selection1 = {line=1, pos=4} - -- backspace deletes rest of line without joining to any other line - App.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 selection - check_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 cleared - check_nil(Selection1.line, "F - test_backspace_to_start_of_line/selection") -end - -function test_backspace_to_start_of_line() - io.write('\ntest_backspace_to_start_of_line') - -- select region from cursor to start of line - App.screen.init{width=25+30, height=60} - Lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'} - Line_width = App.screen.width - Cursor1 = {line=2, pos=1} - Selection1 = {line=2, pos=3} - -- backspace deletes beginning of line without joining to any other line - App.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 selection - check_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 cleared - check_nil(Selection1.line, "F - test_backspace_to_start_of_line/selection") -end - -function 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.width - Cursor1 = {line=2, pos=4} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - -- insert a character - App.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_top - App.screen.check(y, 'abc', 'F - test_undo_insert_text/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'defg', 'F - test_undo_insert_text/baseline/screen:2') - y = y + Line_height - App.screen.check(y, 'xyz', 'F - test_undo_insert_text/baseline/screen:3') - -- undo - App.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_top - App.screen.check(y, 'abc', 'F - test_undo_insert_text/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_undo_insert_text/screen:2') - y = y + Line_height - App.screen.check(y, 'xyz', 'F - test_undo_insert_text/screen:3') -end - -function 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.width - Cursor1 = {line=2, pos=5} - Screen_top1 = {line=1, pos=1} - Screen_bottom1 = {} - -- delete a character - App.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_top - App.screen.check(y, 'abc', 'F - test_undo_delete_text/baseline/screen:1') - y = y + Line_height - App.screen.check(y, 'def', 'F - test_undo_delete_text/baseline/screen:2') - y = y + Line_height - App.screen.check(y, 'xyz', 'F - test_undo_delete_text/baseline/screen:3') - -- undo ---? -- after undo, the backspaced key is selected - App.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_top - App.screen.check(y, 'abc', 'F - test_undo_delete_text/screen:1') - y = y + Line_height - App.screen.check(y, 'defg', 'F - test_undo_delete_text/screen:2') - y = y + Line_height - App.screen.check(y, 'xyz', 'F - test_undo_delete_text/screen:3') -end - function Text.compute_fragments(line, line_width) --? print('compute_fragments', line_width) line.fragments = {} |