6UM4I6WIKWM7H45OQUDFMU2D6H5DZEIKM2FCZ76EXLADEA54F7FAC
AA4YTQ4LZ2AV7SUI4ZLLH2UWYUGGTZIHWDN7W3DNCZD3RI7776CQC
NLKCEYW3UILNF3BSADTZP5TGIYS535WUNGZ7ATOXGOLR5BIFLKUQC
NZFPRI5PD5IHUB7TIJWDEL53QGS7POWJES7JPMET6KFCP3RWBFRQC
EAVMEZ7JEWJV5TAS46GYDFT6F6DG5D2ZY2ARYPXCYKIWE6PMQZMQC
FYJXSWXVSHXNII6UEG77UKQ6OSOMY2FRTAIROVJHUGAXN2HK5PPQC
F4RUTONDM6GET6RT6ZBJKHJWJZWHJLCI2NM5ZMOFJGWAOEUCBDQQC
RLCO2SNKO5OJBEQFRXTB7TCAES6KML4IBOCW5SA7Q24RALW6JCWQC
4WAFGF4ZMUQOLBWRZ2SI6RWEBKMFNFZQJMPECT25C2VPYHNDK2JQC
MYTMNN426OSZY4XF4QK3A3WLFODOISC4YZC2QHYB4BCNRBREHCDQC
LXTTOB33N2HCUZFIUDRQGGBVHK2HODRG4NBLH6RXRQZDCHF27BSAC
OZWUUHENVHX2LAF4XXGZPDUS6OPLU6H67JMQEKDAUWL2U3OLZYYQC
LF7BWEG4DKQI7NMXMZC4LC2BE5PB42HK5PD6OYBNIDMAZBJASOKQC
JTSWQ6PDARW5B2SFB4SQX6D5XSS7B5WVXZ43E3O5OSUK6S7T24UQC
ORRSP7FVCHI2TF5GXBRGQYYJAA3JFYXZBM3T663BKSBV22FCZVCAC
A3XJXFLE7IX524G4HWJKMQYKJCMR3N2MVFFSW7ILAEDROTOUNF3QC
HTWAM4NZFOY463TNSKYIM2EWB7QNBGDRRTTGHF5N3Z4TGC7Q3SFAC
AMOPICKVRHMQERJLFPMAAEBV7TL5QACGGSBJWRCMV5R5O3KDVETAC
JZG2P7IW56SVZ6FOJ4M4SMEXA47LCVGSADZI73CDSTBFZHDV46UAC
T42Y5MLOV7WMURTBEZTUVMYEVG72UMN6BPG2QHOYW7G2CC75ZNGQC
B4USRORMW7EQXOV4TOHRCMMWUURWAMJRDITEKA3YAV2SFUIFBXLQC
end
function test_backspace_from_start_of_final_line()
-- display final line of text with cursor at start of it
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def'}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.cursor1 = {line=2, pos=1}
Text.redraw_all(Editor_state)
-- backspace scrolls up
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(#Editor_state.lines, 1, '#lines')
check_eq(Editor_state.cursor1.line, 1, 'cursor')
check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
end
function test_insert_first_character()
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{}
Text.redraw_all(Editor_state)
edit.draw(Editor_state)
edit.run_after_text_input(Editor_state, 'a')
local y = Editor_state.top
App.screen.check(y, 'a', 'screen:1')
end
function test_press_ctrl()
-- press ctrl while the cursor is on text
App.screen.init{width=50, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{''}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
edit.run_after_keychord(Editor_state, 'C-m', 'm')
function test_edit_deletes_selection()
-- display a line of text with some part selected
App.screen.init{width=75, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- press a key
edit.run_after_text_input(Editor_state, 'x')
-- selected text is deleted and replaced with the key
check_eq(Editor_state.lines[1].data, 'xbc', 'check')
end
function test_edit_with_shift_key_deletes_selection()
-- display a line of text with some part selected
App.screen.init{width=75, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- mimic precise keypresses for a capital letter
App.fake_key_press('lshift')
edit.keychord_press(Editor_state, 'd', 'd')
edit.text_input(Editor_state, 'D')
edit.key_release(Editor_state, 'd')
App.fake_key_release('lshift')
-- selected text is deleted and replaced with the key
check_nil(Editor_state.selection1.line, 'check')
check_eq(Editor_state.lines[1].data, 'Dbc', 'data')
end
function test_cut()
-- display a line of text with some part selected
App.screen.init{width=75, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- press a key
edit.run_after_keychord(Editor_state, 'C-x', 'x')
check_eq(App.clipboard, 'a', 'clipboard')
-- selected text is deleted
check_eq(Editor_state.lines[1].data, 'bc', 'data')
end
function test_paste_replaces_selection()
-- display a line of text with a selection
App.screen.init{width=75, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.selection1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- set clipboard
App.clipboard = 'xyz'
-- paste selection
edit.run_after_keychord(Editor_state, 'C-v', 'v')
-- selection is reset since shift key is not pressed
-- selection includes the newline, so it's also deleted
check_eq(Editor_state.lines[1].data, 'xyzdef', 'check')
end
function test_deleting_selection_may_scroll()
-- display lines 2/3/4
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=2}
Editor_state.screen_top1 = {line=2, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'def', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'jkl', 'baseline/screen:3')
-- set up a selection starting above the currently displayed page
Editor_state.selection1 = {line=1, pos=2}
-- delete selection
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
-- page scrolls up
check_eq(Editor_state.screen_top1.line, 1, 'check')
check_eq(Editor_state.lines[1].data, 'ahi', 'data')
end
function test_edit_wrapping_text()
function test_move_cursor_using_mouse()
Editor_state.cursor1 = {line=2, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
edit.run_after_text_input(Editor_state, 'g')
local y = Editor_state.top
App.screen.check(y, 'abc', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'g', 'screen:3')
end
function test_insert_newline()
-- display a few lines
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'baseline/screen:3')
-- hitting the enter key splits the line
edit.run_after_keychord(Editor_state, 'return', 'return')
check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
y = Editor_state.top
App.screen.check(y, 'a', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'bc', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'screen:3')
end
function test_insert_newline_at_start_of_line()
-- display a line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc'}
Text.redraw_all(Editor_state)
Editor_state.screen_top1 = {line=1, pos=1}
-- hitting the enter key splits the line
edit.run_after_keychord(Editor_state, 'return', 'return')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
check_eq(Editor_state.lines[1].data, '', 'data:1')
check_eq(Editor_state.lines[2].data, 'abc', 'data:2')
end
function test_insert_from_clipboard()
-- display a few lines
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'baseline/screen:3')
-- paste some text including a newline, check that new line is created
App.clipboard = 'xy\nz'
edit.run_after_keychord(Editor_state, 'C-v', 'v')
check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
Editor_state.selection1 = {}
edit.draw(Editor_state) -- populate line_cache.startpos for each line
edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
y = Editor_state.top
App.screen.check(y, 'axy', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'zbc', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'screen:3')
check_nil(Editor_state.selection1.line, 'selection:line')
check_nil(Editor_state.selection1.pos, 'selection:pos')
end
function test_cut_without_selection()
-- display a few lines
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.selection1 = {}
edit.draw(Editor_state)
-- try to cut without selecting text
edit.run_after_keychord(Editor_state, 'C-x', 'x')
-- no crash
check_nil(Editor_state.selection1.line, 'check')
end
function test_enter_on_bottom_line_scrolls_down()
-- display a few lines with cursor on bottom line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'baseline/screen:3')
-- after hitting the enter key the screen scrolls down
edit.run_after_keychord(Editor_state, 'return', 'return')
check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
check_eq(Editor_state.cursor1.line, 4, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
y = Editor_state.top
App.screen.check(y, 'def', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'g', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'hi', 'screen:3')
end
function test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()
-- display just the bottom line on screen
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=4, pos=2}
Editor_state.screen_top1 = {line=4, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'jkl', 'baseline/screen:1')
-- after hitting the enter key the screen does not scroll down
edit.run_after_keychord(Editor_state, 'return', 'return')
check_eq(Editor_state.screen_top1.line, 4, 'screen_top')
check_eq(Editor_state.cursor1.line, 5, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
y = Editor_state.top
App.screen.check(y, 'j', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'kl', 'screen:2')
end
function test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom()
-- display just an empty bottom line on screen
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', ''}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
edit.draw(Editor_state)
-- after hitting the inserting_text key the screen does not scroll down
edit.run_after_text_input(Editor_state, 'a')
check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
local y = Editor_state.top
App.screen.check(y, 'a', 'screen:1')
end
function test_typing_on_bottom_line_scrolls_down()
-- display a few lines with cursor on bottom line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'pqr'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'baseline/screen:3')
-- after typing something the line wraps and the screen scrolls down
edit.run_after_text_input(Editor_state, 'j')
edit.run_after_text_input(Editor_state, 'k')
edit.run_after_text_input(Editor_state, 'l')
check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 7, 'cursor:pos')
y = Editor_state.top
App.screen.check(y, 'def', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'ghijk', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'l', 'screen:3')
function test_position_cursor_on_recently_edited_wrapping_line()
-- draw a line wrapping over 2 screen lines
App.screen.init{width=100, height=200}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc def ghi jkl mno pqr ', 'xyz'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=25}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc def ghi ', 'baseline1/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'jkl mno pqr ', 'baseline1/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'xyz', 'baseline1/screen:3')
-- add to the line until it's wrapping over 3 screen lines
edit.run_after_text_input(Editor_state, 's')
edit.run_after_text_input(Editor_state, 't')
edit.run_after_text_input(Editor_state, 'u')
check_eq(Editor_state.cursor1.pos, 28, 'cursor:pos')
y = Editor_state.top
App.screen.check(y, 'abc def ghi ', 'baseline2/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'jkl mno pqr ', 'baseline2/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'stu', 'baseline2/screen:3')
-- try to move the cursor earlier in the third screen line by clicking the mouse
edit.run_after_mouse_release(Editor_state, Editor_state.left+2,Editor_state.top+Editor_state.line_height*2+5, 1)
-- cursor should move
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 25, 'cursor:pos')
end
function test_backspace_can_scroll_up()
-- display the lines 2/3/4 with the cursor on line 2
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'def', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'jkl', 'baseline/screen:3')
-- after hitting backspace the screen scrolls up by one line
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
check_eq(Editor_state.cursor1.line, 1, 'cursor')
y = Editor_state.top
App.screen.check(y, 'abcdef', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'jkl', 'screen:3')
end
function test_backspace_can_scroll_up_screen_line()
-- display lines starting from second screen line of a line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=6}
Editor_state.screen_top1 = {line=3, pos=6}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'kl', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'mno', 'baseline/screen:2')
-- after hitting backspace the screen scrolls up by one screen line
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
y = Editor_state.top
App.screen.check(y, 'ghi k', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'l', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'mno', 'screen:3')
check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')
check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
end
function test_backspace_past_line_boundary()
-- position cursor at start of a (non-first) line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
-- backspace joins with previous line
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.lines[1].data, 'abcdef', 'check')
end
-- some tests for operating over selections created using Shift- chords
-- we're just testing delete_selection, and it works the same for all keys
function test_backspace_over_selection()
-- select just one character within a line with cursor before selection
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
-- backspace deletes the selected character, even though it's after the cursor
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.lines[1].data, 'bc', 'data')
-- cursor (remains) at start of selection
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
-- selection is cleared
check_nil(Editor_state.selection1.line, 'selection')
end
function test_backspace_over_selection_reverse()
-- select just one character within a line with cursor after selection
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.selection1 = {line=1, pos=1}
-- backspace deletes the selected character
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.lines[1].data, 'bc', 'data')
-- cursor moves to start of selection
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
-- selection is cleared
check_nil(Editor_state.selection1.line, 'selection')
end
function test_backspace_over_multiple_lines()
-- select just one character within a line with cursor after selection
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.selection1 = {line=4, pos=2}
-- backspace deletes the region and joins the remaining portions of lines on either side
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.lines[1].data, 'akl', 'data:1')
check_eq(Editor_state.lines[2].data, 'mno', 'data:2')
-- cursor remains at start of selection
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
-- selection is cleared
check_nil(Editor_state.selection1.line, 'selection')
end
function test_backspace_to_end_of_line()
-- select region from cursor to end of line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.selection1 = {line=1, pos=4}
-- backspace deletes rest of line without joining to any other line
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.lines[1].data, 'a', 'data:1')
check_eq(Editor_state.lines[2].data, 'def', 'data:2')
-- cursor remains at start of selection
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
-- selection is cleared
check_nil(Editor_state.selection1.line, 'selection')
end
function test_backspace_to_start_of_line()
-- select region from cursor to start of line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.selection1 = {line=2, pos=3}
-- backspace deletes beginning of line without joining to any other line
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.lines[1].data, 'abc', 'data:1')
check_eq(Editor_state.lines[2].data, 'f', 'data:2')
-- cursor remains at start of selection
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
-- selection is cleared
check_nil(Editor_state.selection1.line, 'selection')
end
function test_undo_insert_text()
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'xyz'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
-- insert a character
edit.draw(Editor_state)
edit.run_after_text_input(Editor_state, 'g')
check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')
check_eq(Editor_state.cursor1.pos, 5, 'baseline/cursor:pos')
check_nil(Editor_state.selection1.line, 'baseline/selection:line')
check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'defg', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'xyz', 'baseline/screen:3')
-- undo
edit.run_after_keychord(Editor_state, 'C-z', 'z')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
check_nil(Editor_state.selection1.line, 'selection:line')
check_nil(Editor_state.selection1.pos, 'selection:pos')
y = Editor_state.top
App.screen.check(y, 'abc', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'xyz', 'screen:3')
end
function test_undo_delete_text()
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'defg', 'xyz'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=5}
Editor_state.screen_top1 = {line=1, pos=1}
-- delete a character
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')
check_eq(Editor_state.cursor1.pos, 4, 'baseline/cursor:pos')
check_nil(Editor_state.selection1.line, 'baseline/selection:line')
check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'xyz', 'baseline/screen:3')
-- undo
--? -- after undo, the backspaced key is selected
edit.run_after_keychord(Editor_state, 'C-z', 'z')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
check_nil(Editor_state.selection1.line, 'selection:line')
check_nil(Editor_state.selection1.pos, 'selection:pos')
--? check_eq(Editor_state.selection1.line, 2, 'selection:line')
--? check_eq(Editor_state.selection1.pos, 4, 'selection:pos')
y = Editor_state.top
App.screen.check(y, 'abc', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'defg', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'xyz', 'screen:3')
end
function test_undo_restores_selection()
-- display a line of text with some part selected
App.screen.init{width=75, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- delete selected text
edit.run_after_text_input(Editor_state, 'x')
check_eq(Editor_state.lines[1].data, 'xbc', 'baseline')
check_nil(Editor_state.selection1.line, 'baseline:selection')
-- undo
edit.run_after_keychord(Editor_state, 'C-z', 'z')
edit.run_after_keychord(Editor_state, 'C-z', 'z')
-- selection is restored
check_eq(Editor_state.selection1.line, 1, 'line')
check_eq(Editor_state.selection1.pos, 2, 'pos')
end
end
function test_backspace_from_start_of_final_line()
-- display final line of text with cursor at start of it
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def'}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.cursor1 = {line=2, pos=1}
Text.redraw_all(Editor_state)
-- backspace scrolls up
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(#Editor_state.lines, 1, '#lines')
check_eq(Editor_state.cursor1.line, 1, 'cursor')
check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
function test_insert_first_character()
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{}
Text.redraw_all(Editor_state)
edit.draw(Editor_state)
edit.run_after_text_input(Editor_state, 'a')
local y = Editor_state.top
App.screen.check(y, 'a', 'screen:1')
end
function test_press_ctrl()
-- press ctrl while the cursor is on text
App.screen.init{width=50, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{''}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
edit.run_after_keychord(Editor_state, 'C-m', 'm')
end
end
function test_edit_deletes_selection()
-- display a line of text with some part selected
App.screen.init{width=75, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- press a key
edit.run_after_text_input(Editor_state, 'x')
-- selected text is deleted and replaced with the key
check_eq(Editor_state.lines[1].data, 'xbc', 'check')
function test_edit_with_shift_key_deletes_selection()
-- display a line of text with some part selected
App.screen.init{width=75, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- mimic precise keypresses for a capital letter
App.fake_key_press('lshift')
edit.keychord_press(Editor_state, 'd', 'd')
edit.text_input(Editor_state, 'D')
edit.key_release(Editor_state, 'd')
App.fake_key_release('lshift')
-- selected text is deleted and replaced with the key
check_nil(Editor_state.selection1.line, 'check')
check_eq(Editor_state.lines[1].data, 'Dbc', 'data')
end
end
function test_cut()
-- display a line of text with some part selected
App.screen.init{width=75, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- press a key
edit.run_after_keychord(Editor_state, 'C-x', 'x')
check_eq(App.clipboard, 'a', 'clipboard')
-- selected text is deleted
check_eq(Editor_state.lines[1].data, 'bc', 'data')
end
function test_paste_replaces_selection()
-- display a line of text with a selection
App.screen.init{width=75, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.selection1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- set clipboard
App.clipboard = 'xyz'
-- paste selection
edit.run_after_keychord(Editor_state, 'C-v', 'v')
-- selection is reset since shift key is not pressed
-- selection includes the newline, so it's also deleted
check_eq(Editor_state.lines[1].data, 'xyzdef', 'check')
end
function test_deleting_selection_may_scroll()
-- display lines 2/3/4
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=2}
Editor_state.screen_top1 = {line=2, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'def', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'jkl', 'baseline/screen:3')
-- set up a selection starting above the currently displayed page
Editor_state.selection1 = {line=1, pos=2}
-- delete selection
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
-- page scrolls up
check_eq(Editor_state.screen_top1.line, 1, 'check')
check_eq(Editor_state.lines[1].data, 'ahi', 'data')
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
edit.run_after_text_input(Editor_state, 'g')
local y = Editor_state.top
App.screen.check(y, 'abc', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'g', 'screen:3')
end
function test_insert_newline()
-- display a few lines
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'baseline/screen:3')
-- hitting the enter key splits the line
edit.run_after_keychord(Editor_state, 'return', 'return')
check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
y = Editor_state.top
App.screen.check(y, 'a', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'bc', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'screen:3')
end
function test_insert_newline_at_start_of_line()
-- display a line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc'}
Editor_state.screen_top1 = {line=1, pos=1}
-- hitting the enter key splits the line
edit.run_after_keychord(Editor_state, 'return', 'return')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
check_eq(Editor_state.lines[1].data, '', 'data:1')
check_eq(Editor_state.lines[2].data, 'abc', 'data:2')
end
function test_insert_from_clipboard()
-- display a few lines
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'baseline/screen:3')
-- paste some text including a newline, check that new line is created
App.clipboard = 'xy\nz'
edit.run_after_keychord(Editor_state, 'C-v', 'v')
check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
Editor_state.selection1 = {}
edit.draw(Editor_state) -- populate line_cache.startpos for each line
edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
y = Editor_state.top
App.screen.check(y, 'axy', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'zbc', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'screen:3')
check_nil(Editor_state.selection1.line, 'selection:line')
check_nil(Editor_state.selection1.pos, 'selection:pos')
end
function test_cut_without_selection()
-- display a few lines
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.selection1 = {}
edit.draw(Editor_state)
-- try to cut without selecting text
edit.run_after_keychord(Editor_state, 'C-x', 'x')
-- no crash
check_nil(Editor_state.selection1.line, 'check')
end
function test_enter_on_bottom_line_scrolls_down()
-- display a few lines with cursor on bottom line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'baseline/screen:3')
-- after hitting the enter key the screen scrolls down
edit.run_after_keychord(Editor_state, 'return', 'return')
check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
check_eq(Editor_state.cursor1.line, 4, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
y = Editor_state.top
App.screen.check(y, 'def', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'g', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'hi', 'screen:3')
end
function test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()
-- display just the bottom line on screen
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=4, pos=2}
Editor_state.screen_top1 = {line=4, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'jkl', 'baseline/screen:1')
-- after hitting the enter key the screen does not scroll down
edit.run_after_keychord(Editor_state, 'return', 'return')
check_eq(Editor_state.screen_top1.line, 4, 'screen_top')
check_eq(Editor_state.cursor1.line, 5, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
y = Editor_state.top
App.screen.check(y, 'j', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'kl', 'screen:2')
function test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bottom()
-- display just an empty bottom line on screen
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', ''}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
edit.draw(Editor_state)
-- after hitting the inserting_text key the screen does not scroll down
edit.run_after_text_input(Editor_state, 'a')
check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
local y = Editor_state.top
App.screen.check(y, 'a', 'screen:1')
end
function test_typing_on_bottom_line_scrolls_down()
-- display a few lines with cursor on bottom line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'pqr'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'baseline/screen:3')
-- after typing something the line wraps and the screen scrolls down
edit.run_after_text_input(Editor_state, 'j')
edit.run_after_text_input(Editor_state, 'k')
edit.run_after_text_input(Editor_state, 'l')
check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 7, 'cursor:pos')
y = Editor_state.top
App.screen.check(y, 'def', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'ghijk', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'l', 'screen:3')
end
end
function test_position_cursor_on_recently_edited_wrapping_line()
-- draw a line wrapping over 2 screen lines
App.screen.init{width=100, height=200}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc def ghi jkl mno pqr ', 'xyz'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=25}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc def ghi ', 'baseline1/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'jkl mno pqr ', 'baseline1/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'xyz', 'baseline1/screen:3')
-- add to the line until it's wrapping over 3 screen lines
edit.run_after_text_input(Editor_state, 's')
edit.run_after_text_input(Editor_state, 't')
edit.run_after_text_input(Editor_state, 'u')
check_eq(Editor_state.cursor1.pos, 28, 'cursor:pos')
y = Editor_state.top
App.screen.check(y, 'abc def ghi ', 'baseline2/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'jkl mno pqr ', 'baseline2/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'stu', 'baseline2/screen:3')
-- try to move the cursor earlier in the third screen line by clicking the mouse
edit.run_after_mouse_release(Editor_state, Editor_state.left+2,Editor_state.top+Editor_state.line_height*2+5, 1)
-- cursor should move
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 25, 'cursor:pos')
end
function test_backspace_can_scroll_up()
-- display the lines 2/3/4 with the cursor on line 2
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'def', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'jkl', 'baseline/screen:3')
-- after hitting backspace the screen scrolls up by one line
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
check_eq(Editor_state.cursor1.line, 1, 'cursor')
y = Editor_state.top
App.screen.check(y, 'abcdef', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'jkl', 'screen:3')
end
function test_backspace_can_scroll_up_screen_line()
-- display lines starting from second screen line of a line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=6}
Editor_state.screen_top1 = {line=3, pos=6}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'kl', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'mno', 'baseline/screen:2')
-- after hitting backspace the screen scrolls up by one screen line
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
y = Editor_state.top
App.screen.check(y, 'ghi k', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'l', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'mno', 'screen:3')
check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')
check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
end
function test_backspace_past_line_boundary()
-- position cursor at start of a (non-first) line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
-- backspace joins with previous line
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.lines[1].data, 'abcdef', 'check')
-- 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()
-- select just one character within a line with cursor before selection
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
-- backspace deletes the selected character, even though it's after the cursor
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.lines[1].data, 'bc', 'data')
-- cursor (remains) at start of selection
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
-- selection is cleared
check_nil(Editor_state.selection1.line, 'selection')
end
function test_backspace_over_selection_reverse()
-- select just one character within a line with cursor after selection
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.selection1 = {line=1, pos=1}
-- backspace deletes the selected character
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.lines[1].data, 'bc', 'data')
-- cursor moves to start of selection
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
-- selection is cleared
check_nil(Editor_state.selection1.line, 'selection')
end
function test_backspace_over_multiple_lines()
-- select just one character within a line with cursor after selection
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.selection1 = {line=4, pos=2}
-- backspace deletes the region and joins the remaining portions of lines on either side
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.lines[1].data, 'akl', 'data:1')
check_eq(Editor_state.lines[2].data, 'mno', 'data:2')
-- cursor remains at start of selection
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
-- selection is cleared
check_nil(Editor_state.selection1.line, 'selection')
end
function test_backspace_to_end_of_line()
-- select region from cursor to end of line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.selection1 = {line=1, pos=4}
-- backspace deletes rest of line without joining to any other line
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.lines[1].data, 'a', 'data:1')
check_eq(Editor_state.lines[2].data, 'def', 'data:2')
-- cursor remains at start of selection
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
-- selection is cleared
check_nil(Editor_state.selection1.line, 'selection')
end
function test_backspace_to_start_of_line()
-- select region from cursor to start of line
App.screen.init{width=Editor_state.left+30, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.selection1 = {line=2, pos=3}
-- backspace deletes beginning of line without joining to any other line
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.lines[1].data, 'abc', 'data:1')
check_eq(Editor_state.lines[2].data, 'f', 'data:2')
-- cursor remains at start of selection
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
-- selection is cleared
check_nil(Editor_state.selection1.line, 'selection')
end
function test_undo_insert_text()
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'xyz'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
-- insert a character
edit.draw(Editor_state)
edit.run_after_text_input(Editor_state, 'g')
check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')
check_eq(Editor_state.cursor1.pos, 5, 'baseline/cursor:pos')
check_nil(Editor_state.selection1.line, 'baseline/selection:line')
check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'defg', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'xyz', 'baseline/screen:3')
-- undo
edit.run_after_keychord(Editor_state, 'C-z', 'z')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
check_nil(Editor_state.selection1.line, 'selection:line')
check_nil(Editor_state.selection1.pos, 'selection:pos')
y = Editor_state.top
App.screen.check(y, 'abc', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'xyz', 'screen:3')
end
function test_undo_delete_text()
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'defg', 'xyz'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=5}
Editor_state.screen_top1 = {line=1, pos=1}
-- delete a character
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')
check_eq(Editor_state.cursor1.pos, 4, 'baseline/cursor:pos')
check_nil(Editor_state.selection1.line, 'baseline/selection:line')
check_nil(Editor_state.selection1.pos, 'baseline/selection:pos')
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'xyz', 'baseline/screen:3')
-- undo
--? -- after undo, the backspaced key is selected
edit.run_after_keychord(Editor_state, 'C-z', 'z')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
check_nil(Editor_state.selection1.line, 'selection:line')
check_nil(Editor_state.selection1.pos, 'selection:pos')
--? check_eq(Editor_state.selection1.line, 2, 'selection:line')
--? check_eq(Editor_state.selection1.pos, 4, 'selection:pos')
y = Editor_state.top
App.screen.check(y, 'abc', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'defg', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'xyz', 'screen:3')
end
function test_undo_restores_selection()
-- display a line of text with some part selected
App.screen.init{width=75, height=80}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- delete selected text
edit.run_after_text_input(Editor_state, 'x')
check_eq(Editor_state.lines[1].data, 'xbc', 'baseline')
check_nil(Editor_state.selection1.line, 'baseline:selection')
-- undo
edit.run_after_keychord(Editor_state, 'C-z', 'z')
edit.run_after_keychord(Editor_state, 'C-z', 'z')
-- selection is restored
check_eq(Editor_state.selection1.line, 1, 'line')
check_eq(Editor_state.selection1.pos, 2, 'pos')
end
check(Editor_state.selection1.line, 'check')
end
function test_move_cursor_using_mouse()
App.screen.init{width=50, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'xyz'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.selection1 = {}
edit.draw(Editor_state) -- populate line_cache.startpos for each line
edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
check_nil(Editor_state.selection1.line, 'selection:line')
check_nil(Editor_state.selection1.pos, 'selection:pos')
App.screen.check(y, 'ghi ', 'screen:3')
--
-- I'm checking the precise state of the screen in this file, an inherently
-- brittle approach that depends on details of the font and text shaping
-- algorithms used by a particular release of LÖVE.
--
-- (This brittleness is one reason lines2 and its forks have no tests.)
--
-- To manage the brittleness, there'll be one version of this file for each
-- distinct LÖVE version that introduces font changes.
Version, Major_version = App.love_version()
if Major_version == 11 then
load_file_from_source_or_save_directory('text_tests_love11.lua')
elseif Major_version == 12 then
-- not released/stable yet
load_file_from_source_or_save_directory('text_tests_love12.lua')