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 | |
parent | 06e6ecdf8f8f8f8489b2e0d1145698a52a7c3ae5 (diff) | |
download | text.love-0d52962b3e6f91e458fbf858e62e684494373af8.tar.gz |
extract a couple of files
-rw-r--r-- | search.lua | 114 | ||||
-rw-r--r-- | select.lua | 153 | ||||
-rw-r--r-- | text.lua | 1197 | ||||
-rw-r--r-- | text_tests.lua | 931 |
4 files changed, 1202 insertions, 1193 deletions
diff --git a/search.lua b/search.lua new file mode 100644 index 0000000..29dd8dd --- /dev/null +++ b/search.lua @@ -0,0 +1,114 @@ +-- helpers for the search bar (C-f) + +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 diff --git a/select.lua b/select.lua new file mode 100644 index 0000000..fcef878 --- /dev/null +++ b/select.lua @@ -0,0 +1,153 @@ +-- helpers for selecting portions of text + +local 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.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 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 = {} diff --git a/text_tests.lua b/text_tests.lua new file mode 100644 index 0000000..91b3b46 --- /dev/null +++ b/text_tests.lua @@ -0,0 +1,931 @@ +-- 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.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 |