YAU43TBB2N2EZE2REHBY3IZHC42N6PXBAVZHPVML23KCB2SIGG7QC CDU55PERQTKYQCGL6TOLI5K67TBRQDNAMJEYJVU4KZMFFD4JWEHQC BD2JB3BUQ6LWLSMSOTVSV5IZNITRIXBXFIBKWZRQNBJDPPH64XKAC FIZSQ25MSANXHHE6UN26WWSFCWMBQ5GSN4XYZP7VKB7TMW3WADGAC TYLURRX3NWR66WYQSCPNFKB2N42NYKCY2TSL2Z6ZK7L4KCKFZF2QC ICUW7F3XQLURK4LSNPH5E3NDEFSRHKATEUHH2UPFJTMHYR3ZJF3QC BULPIBEGL7TMK6CVIE7IS7WGAHGOSUJBGJSFQK542MOWGHP2ADQQC Q65423XUYWJ5ESAH2B63MJKJUXSTQDANPZRRMQNYTDF3YGT5HLRAC SPSW74Y5OJ54Y7VQ3SJFCJR5CYDKTR4A3TOEVZODDZLUSDDU2GZAC EB6LHFJRSQIQWC3M5CZWOLG7PX3HHO2ZQ2TGMBCOLUBDZSP7CJZAC 6PCE7VUWEE5UVSE3ZZSQAMGUJDDHOBNO2MVD6POUWFP7UY7WOZHAC ZS5IYZH5EXXPSVIFWS7XW5POEVRRCK6XV6PB36D3EJXJRT22LKOQC LLQC2M2IMEJBJQXZTKC3OAKG5WKHSERXKAKCYHQRUZZD6CVRIHAQC Z5HLXU4PJWWJJDBCK52NBD6PIRIA3TAN2BKZB5HBYFGIDBX4F5HAC QAMVLUK22RP5RBDTDV5XVPQCSJUWDWESV4TRCUTNUM46E26BH2AQC QCPXQ2E3USF3Z6R6WJ2JKHTRMPKA6QWXFKKRMLXA3MXABJEL543AC 2QLTVKM6MVC25L44BSHKPZAXOOLK2VAUCGCYRUDRXXNYNSWDIWQAC JOPVPUSAMMU6RFVDQR4NJC4GNNUFB7GPKVH7OS5FKCYS5QZ53VLQC KHOQZJWF3SNBCKLU5A7YDKPI6QHKZN7TERN2CRLB7GHG5GYBNNLAC D4B52CQ2QKG2HQKFUQOO5S2ME325DTW3PH2D7SBXCW4BPQFYG7CAC 5XMBCKJZ7YCUOOQWWZRNLXMXD3MUYC2VAF3VKA4BIEHQRPDQMUMAC CRBLAWBOTECOU5MFURWCRBR43OO7NWIHP5LC35IMGTQKN73AUS6AC 3VHUIIATPOF7FXB7NTL5MESCV5BCQACII2D7QZ4UIUCBX3CWXMMAC YXQOITYSGYQQBOMF7CJ33C2CTDAI5R2W2RCRSKJ6JTLV7MS4WVXAC 7TNVOEVBH43S2RZJSTV4MYWPT7JCWP4OX6UK3DKTQP2E3TSBASXAC VOU73AK6XOVIOCY6PHUXS5RQZ2TGFEF7RYNOKFE2XSHRCZBAJMYQC LNA7OTZTO3TWABP5VAPWAWFUYXGPHCFBSWL62ELQICRDUHF43SKQC VIGITGW5FAYYF4PDK6XAPI4S43X4FMAMBH77VLGMKLNUY7JJHR4QC LXTTOB33N2HCUZFIUDRQGGBVHK2HODRG4NBLH6RXRQZDCHF27BSAC MUJTM6REGQAK3LZTIFWGJRXE2UPCM4HSLXQYSF5ITLXLS6JCVPMQC R2ASHK5CEE3PTRLS37GP4PXJ7HIGJ6UD72KKBI57UDJI7VRROQGQC 2L5MEZV344TOZLVY3432RHJFIRVXFD6O3GWLL5O4CV66BGAFTURQC MBAJPTDJ4KHWACEHWYGCFMHPQYM6FQKCSIIDKWCE765UI3VTDMIAC QMRQL2FOIIXU3PCSH7UQIDRLKJN2DQHB5IUL4TLBVANNHWWETL6AC State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.cursor1 = deepcopy(State.screen_top1)State.cursor1 = deepcopy(State.screen_top1)State.screen_top1 = deepcopy(State.cursor1)
State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.selection1 = deepcopy(State.cursor1)State.cursor1 = deepcopy(State.screen_top1)State.cursor1 = deepcopy(State.screen_top1)State.cursor1 = deepcopy(State.screen_top1)State.screen_top1 = deepcopy(State.cursor1)
-- Return any intersection of the region from State.selection1 to State.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}.
-- Return any intersection of the region from State.selection1 to-- State.cursor1 (or current mouse, if mouse is pressed) with the region-- between {line=line_index, pos=apos} and {line=line_index, pos=bpos}.
for i=1,math.floor(-dy) doText.down(State)endendendfunction edit.text_input(State, t)--? print('text input', t)if State.search_term thenState.search_term = State.search_term..tText.search_next(State)elseText.text_input(State, t)endschedule_save(State)endfunction edit.keychord_press(State, chord, key)if State.selection1.line and-- printable character created using shift key => delete selection-- (we're not creating any ctrl-shift- or alt-shift- combinations using regular/printable keys)(not App.shift_down() or utf8.len(key) == 1) and
Text.delete_selection(State, State.left, State.right)endif State.search_term thenif chord == 'escape' thenState.search_term = nilState.cursor1 = State.search_backup.cursorState.screen_top1 = State.search_backup.screen_topState.search_backup = nilText.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leakselseif chord == 'return' thenState.search_term = nilState.search_backup = nilelseif chord == 'backspace' thenlocal len = utf8.len(State.search_term)local byte_offset = Text.offset(State.search_term, len)State.search_term = string.sub(State.search_term, 1, byte_offset-1)
elseif chord == 'down' thenState.cursor1.pos = State.cursor1.pos+1Text.search_next(State)elseif chord == 'up' thenText.search_previous(State)endreturnelseif chord == 'C-f' thenState.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},}-- zoomelseif chord == 'C-=' thenedit.update_font_settings(State, State.font_height+2)Text.redraw_all(State)elseif chord == 'C--' thenif State.font_height > 2 thenedit.update_font_settings(State, State.font_height-2)Text.redraw_all(State)endelseif chord == 'C-0' thenedit.update_font_settings(State, 20)Text.redraw_all(State)-- undoelseif chord == 'C-z' thenlocal event = undo_event(State)if event thenlocal src = event.beforeState.screen_top1 = deepcopy(src.screen_top)State.cursor1 = deepcopy(src.cursor)State.selection1 = deepcopy(src.selection)patch(State.lines, event.after, event.before)patch_placeholders(State.line_cache, event.after, event.before)-- if we're scrolling, reclaim all fragments to avoid memory leaksText.redraw_all(State)schedule_save(State)endelseif chord == 'C-y' thenlocal event = redo_event(State)if event thenlocal src = event.afterState.screen_top1 = deepcopy(src.screen_top)State.cursor1 = deepcopy(src.cursor)State.selection1 = deepcopy(src.selection)patch(State.lines, event.before, event.after)-- if we're scrolling, reclaim all fragments to avoid memory leaksText.redraw_all(State)schedule_save(State)end-- clipboardelseif chord == 'C-a' thenState.selection1 = {line=1, pos=1}State.cursor1 = {line=#State.lines, pos=utf8.len(State.lines[#State.lines].data)+1}elseif chord == 'C-c' thenlocal s = Text.selection(State)if s thenApp.set_clipboard(s)endelseif chord == 'C-x' thenlocal s = Text.cut_selection(State, State.left, State.right)if s thenApp.set_clipboard(s)endschedule_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.linelocal before = snapshot(State, before_line)local clipboard_data = App.get_clipboard()for _,code in utf8.codes(clipboard_data) dolocal c = utf8.char(code)if c == '\n' thenText.insert_return(State)elseText.insert_at_cursor(State, c)endendif Text.cursor_out_of_screen(State) thenText.snap_cursor_to_bottom_of_screen(State, State.left, State.right)endschedule_save(State)record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)})-- dispatch to textelseText.keychord_press(State, chord)endendfunction edit.key_release(State, key, scancode)endfunction edit.update_font_settings(State, font_height)State.font_height = font_height
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 = 25Test_margin_right = 0function edit.initialize_test_state()-- if you change these values, tests will start failingreturn edit.initialize_state(15, -- top marginTest_margin_left,App.screen.width - Test_margin_right,
15) -- line heightend-- all text_input events are also keypresses-- TODO: handle chords of multiple keysfunction 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)endfunction 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)endfunction 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)endfunction 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