XNDCSWPCWESQ3ZXCRWTLYEYOKFY5IVFYD24RJ2KEELVCWEVPVUQAC
FQESR6R62E7AOW5TCZNNAJNSHSB2VM7GFZ4YFM2M53FPA2SZKCFQC
S7CSVBHZ4AORIP4VIQETFAVXVMNKERVOI5DENNTU73ZO2L667N7QC
3OY667VAHI7ZNCTQH7NVPLJP42G3IBEZCU3CBYTQCOTJOZF5ZAZQC
LXTTOB33N2HCUZFIUDRQGGBVHK2HODRG4NBLH6RXRQZDCHF27BSAC
Q65423XUYWJ5ESAH2B63MJKJUXSTQDANPZRRMQNYTDF3YGT5HLRAC
2H76FV5SK3ZWFVQ6ISAOSUJQWW4SSNPRDEI75JIQAJRPIVWPOCDAC
O7YTBRQYVEM6LR22BJFPAGGF4CQP5R2C4B4KXVOTHUWA32TVZ6BQC
LLQC2M2IMEJBJQXZTKC3OAKG5WKHSERXKAKCYHQRUZZD6CVRIHAQC
WB66TPZSJ6SPPZKQVDON56URAO3XPKSY7CE5WKDSE4S36U2ZE6PAC
4FTOQOPZLSWHIQ45X6BTMJ5RXP6NIOOYYYRBCXVMDADSTSJD4P6AC
RAXUQQ6ZTTH4WCEDDLVXJSZ4E2W6NBEGTANCBDK57YO6ASCEI2CQC
JOPVPUSAMMU6RFVDQR4NJC4GNNUFB7GPKVH7OS5FKCYS5QZ53VLQC
3VHUIIATPOF7FXB7NTL5MESCV5BCQACII2D7QZ4UIUCBX3CWXMMAC
34BZ5ZKNAB4XQGXOPVBZHBDYD5D3X4V6T72XZSR5LJXF4UIVSWQAC
3PSFWAILGRA4OYXWS2DX7VF332AIBPYBXHEA4GIQY2XEJVD65UMAC
W55AT3VM73ME732N3C6NTDZ2KFA5SMYC752JHY7Z7PBGP4Z3X4RQC
I4S4EFYX6P4RSBCCRAYYXA5MD63T3YINN36LOQXXDKMN7MVS6WNAC
F52PSTYSIIUD7UPEVLEJPVSBQU7IKVHM4GN4YASNSKFP4MRE6S6AC
3XNFQDDNFGTN6ZFAB47AR6Q3663WLXDYC3K5TERYUWDM6U2FNOVAC
2L5MEZV344TOZLVY3432RHJFIRVXFD6O3GWLL5O4CV66BGAFTURQC
RMEYIV6ZJG53HQGSIZVE4WIAR7ZHB6F2V2OYYBR6752LIL7S2PRQC
7Y7C7N6OVIL4YT33DYCBAPTH7COHOAWQGHGPUNBNLYQ2HW65PWFAC
ICUW7F3XQLURK4LSNPH5E3NDEFSRHKATEUHH2UPFJTMHYR3ZJF3QC
R2ASHK5CEE3PTRLS37GP4PXJ7HIGJ6UD72KKBI57UDJI7VRROQGQC
RXMHAZ6VL4EJMYEZM32G4W54EPF3BPWPIGASJ7ND45FJ5F6TJCZAC
FBBHEUQNDHGZQES6GC3VW55DXILRP7PYYXNLQF3HGHKYLUGWAUBQC
QZUFJMD5OTAZGSUO4HSJ2NNNRJR7NOKIVOIIGHWM6IF4PFNMQZHAC
CQVWNL4MQYXJVKECYVKSUMRXYMQVEMYUBHHDTDMEU5GOZ67M7QOAC
QMRQL2FOIIXU3PCSH7UQIDRLKJN2DQHB5IUL4TLBVANNHWWETL6AC
MBAJPTDJ4KHWACEHWYGCFMHPQYM6FQKCSIIDKWCE765UI3VTDMIAC
CRBLAWBOTECOU5MFURWCRBR43OO7NWIHP5LC35IMGTQKN73AUS6AC
end
function test_search_downwards_from_end_of_line()
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- search for empty string
edit.run_after_keychord(Editor_state, 'C-f', 'f')
edit.run_after_keychord(Editor_state, 'down', 'down')
-- no crash
end
function test_search_downwards_from_final_pos_of_line()
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=3}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- search for empty string
edit.run_after_keychord(Editor_state, 'C-f', 'f')
edit.run_after_keychord(Editor_state, 'down', 'down')
-- no crash
end
end
function test_search_downwards_from_end_of_line()
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- search for empty string
edit.run_after_keychord(Editor_state, 'C-f', 'f')
edit.run_after_keychord(Editor_state, 'down', 'down')
-- no crash
end
function test_search_downwards_from_final_pos_of_line()
App.screen.init{width=120, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def', 'ghi'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=3}
Editor_state.screen_top1 = {line=1, pos=1}
edit.draw(Editor_state)
-- search for empty string
edit.run_after_keychord(Editor_state, 'C-f', 'f')
edit.run_after_keychord(Editor_state, 'down', 'down')
-- no crash
end
State.cursor = deepcopy(State.search_backup.cursor)
State.screen_top = deepcopy(State.search_backup.screen_top)
elseif chord == 'down' then
if #State.search_term > 0 then
edit.put_cursor_on_next_text_loc_wrapping_around_if_necessary(State)
Text.search_next(State)
end
function edit.put_cursor_on_next_text_line_wrapping_around_if_necessary(State)
local line = State.cursor1.line
local max = #State.lines
for _ = 1, max-1 do
line = (line+1) % max
if State.lines[line].mode == 'text' then
State.cursor1.line = line
State.cursor1.pos = 1
break
end
end
end
function edit.put_cursor_on_next_loc_wrapping_around_if_necessary(State)
local cursor_line = State.lines[State.cursor1.line].data
if State.cursor1.pos <= utf8.len(cursor_line) then
State.cursor1.pos = State.cursor1.pos + 1
else
edit.put_cursor_on_next_text_line_wrapping_around_if_necessary(State)
end
elseif chord == 'up' then
Text.search_previous(State)
end
return
elseif chord == 'C-f' then
State.search_term = ''
State.search_backup = {
cursor={line=State.cursor1.line, pos=State.cursor1.pos},
screen_top={line=State.screen_top1.line, pos=State.screen_top1.pos},
}
-- zoom
elseif chord == 'C-=' then
edit.update_font_settings(State, State.font_height+2)
Text.redraw_all(State)
elseif chord == 'C--' then
if State.font_height > 2 then
edit.update_font_settings(State, State.font_height-2)
Text.redraw_all(State)
end
elseif chord == 'C-0' then
edit.update_font_settings(State, 20)
Text.redraw_all(State)
-- undo
elseif chord == 'C-z' then
local event = undo_event(State)
if event then
local src = event.before
State.screen_top1 = deepcopy(src.screen_top)
State.cursor1 = deepcopy(src.cursor)
State.selection1 = deepcopy(src.selection)
patch(State.lines, event.after, event.before)
schedule_save(State)
end
elseif chord == 'C-y' then
local event = redo_event(State)
if event then
local src = event.after
State.screen_top1 = deepcopy(src.screen_top)
State.cursor1 = deepcopy(src.cursor)
State.selection1 = deepcopy(src.selection)
patch(State.lines, event.before, event.after)
schedule_save(State)
end
-- clipboard
elseif chord == 'C-a' then
State.selection1 = {line=1, pos=1}
State.cursor1 = {line=#State.lines, pos=utf8.len(State.lines[#State.lines].data)+1}
elseif chord == 'C-c' then
local s = Text.selection(State)
if s then
App.set_clipboard(s)
end
elseif chord == 'C-x' then
if s then
App.set_clipboard(s)
end
schedule_save(State)
elseif chord == 'C-v' then
-- We don't have a good sense of when to scroll, so we'll be conservative
-- and sometimes scroll when we didn't quite need to.
local before_line = State.cursor1.line
local before = snapshot(State, before_line)
local clipboard_data = App.get_clipboard()
for _,code in utf8.codes(clipboard_data) do
local c = utf8.char(code)
if c == '\n' then
Text.insert_return(State)
else
Text.insert_at_cursor(State, c)
end
end
if Text.cursor_out_of_screen(State) then
Text.snap_cursor_to_bottom_of_screen(State, State.left, State.right)
end
record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)})
State.line_height = math.floor(font_height*1.3)
end
--== some methods for tests
-- Insulate tests from some key globals so I don't have to change the vast
-- majority of tests when they're modified for the real app.
Test_margin_left = 25
Test_margin_right = 0
function edit.initialize_test_state()
-- if you change these values, tests will start failing
return edit.initialize_state(
15, -- top margin
Test_margin_left,
App.screen.width - Test_margin_right,
15) -- line height
end
-- all text_input events are also keypresses
-- TODO: handle chords of multiple keys
function edit.run_after_text_input(State, t)
edit.keychord_press(State, t)
edit.text_input(State, t)
edit.key_release(State, t)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end
-- not all keys are text_input
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end
function edit.run_after_mouse_click(State, x,y, mouse_button)
App.fake_mouse_press(x,y, mouse_button)
edit.mouse_press(State, x,y, mouse_button)
App.fake_mouse_release(x,y, mouse_button)
edit.mouse_release(State, x,y, mouse_button)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end
function edit.run_after_mouse_press(State, x,y, mouse_button)
App.fake_mouse_press(x,y, mouse_button)
edit.mouse_press(State, x,y, mouse_button)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end
function edit.run_after_mouse_release(State, x,y, mouse_button)
App.fake_mouse_release(x,y, mouse_button)
edit.mouse_release(State, x,y, mouse_button)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end