Merge text.love
[?]
Mar 18, 2023, 5:46 AM
3G723RV5YPWQQQ52OMKDQAQ2OXV6NGLFXLPNB7YXH5WZIYC7PQXQCDependencies
- [2]
STYH5IVGMerge text.love - [3]
R56CKHYZMerge text.love - [4]
2TQUKHBCMerge lines.love - [5]
Q434NJPRMerge lines.love - [6]
BLWAYPKVextract a module - [7]
TVCPXAAUrename - [8]
LXTTOB33extract a couple of files - [9]
6LJZN727handle chords - [10]
ECBDENZ4Merge text.love - [11]
AT3LVCMPMerge text.love - [12]
KKMFQDR4editing source code from within the app - [13]
RSZD5A7Gforgot to add json.lua - [14]
25V2GA6Jtaking stock - [15]
AKZWDWIAMerge lines.love - [16]
RXNR3U5EMerge text.love - [17]
ORKN6EOBMerge lines.love - [18]
C3GUE45IMerge text.love - [19]
EX43CDDIMerge text.love - [20]
R5QXEHUIsomebody stop me - [21]
D4B52CQ2Merge lines.love - [22]
VP5KC4XZMerge lines.love - [23]
VYAFKS7RMerge lines.love - [24]
73OCE2MCafter much struggle, a brute-force undo - [25]
ATQO62TFMerge lines.love - [26]
3QNOKBFMbeginnings of a test harness - [27]
A4BSGS2CMerge lines.love - [28]
JOPVPUSAediting source code from within the app - [29]
3PSFWAILMerge lines.love - [30]
3TI67SEJmore bugfix - [31]
KOTI3MFGbugfix in previous commit - [32]
BJ5X5O4Alet's prevent the text cursor from ever getting on a drawing - [33]
TPGGOVD4Merge text.love - [34]
4SR3Z4Y3document the version of LÖVE I've been using - [35]
ZTZOO2OQMerge lines.love - [36]
2L5MEZV3experiment: new edit namespace - [37]
242L3OQXbugfix: ensure Cursor_line is always on a text line - [38]
UAYCSFSKMerge text.love - [39]
LF7BWEG4group all editor globals - [40]
K74U4BAUMerge lines.love - [41]
4YDBYBA4clean up memory leak experiments - [42]
TRNWIQN6more precise height calculation when scrolling up as much as possible while keeping cursor on screen - [43]
KMSL74GAsupport selections in the source editor - [44]
T4FRZSYLdelete an ancient, unused file - [45]
MXA3RZYKdeduce left/right from state where possible - [46]
2CTN2IEFMerge lines.love - [47]
3OKKTUT4up and down arrow now moving by screen line where possible - [48]
OGUV4HSAremove some memory leaks from rendered fragments - [49]
CE4LZV4Tdrop last couple of manual tests - [50]
OI4FPFINsupport drawings in the source editor - [51]
P3K7UH5CMerge lines.love - [52]
XS3PZI7GMerge lines.love - [53]
66X36NZNa little more prose describing manual_tests - [54]
GUOQRUL7Merge lines.love - [55]
WKKABOJ6one issue less - [56]
AYX33NBCMerge lines.love - [57]
VHQCNMARseveral more modules - [58]
VBU5YHLRMerge lines.love - [59]
OTIBCAUJlove2d scaffold - [60]
TLOAPLBJadd a license - [61]
XX7G2FFJintermingle freehand line drawings with text - [62]
VXORMHMEdelete experimental REPL - [63]
FS2ITYYHrecord a known issue - [64]
K2X6G75Zstart writing some tests for drawings - [65]
5OALPNN3add args to some functions - [66]
MD3W5IRAnew fork: rip out drawing support - [67]
JCXL74WVbring back everything from commit a68647ae22 - [68]
SCOXD4EOMerge lines.love - [69]
U3MJNFUYMerge lines.love - [70]
D4FEFHQCflesh out Readme - [71]
ETXNVRPTMerge lines.love - [72]
AVTNUQYRbasic test-enabled framework - [73]
BULPIBEGbeginnings of a module for the text editor - [74]
VHUNJHXBMerge lines.love - [75]
36Z442IVback to commit 8123959e52f without code editing - [76]
QCPXQ2E3add state arg to a few functions - [77]
D2GCFTTTclean up repl functionality
Change contents
- file deletion: source_text_tests.lua source_text_tests.lua
endfunction test_up_arrow_scrolls_up_by_one_line_skipping_drawing()-- display lines 3/4/5 with a drawing just off screen at line 2App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', '```lines', '```', 'def', 'ghi', 'jkl'}Text.redraw_all(Editor_state)Editor_state.cursor1 = {line=3, pos=1}Editor_state.screen_top1 = {line=3, pos=1}Editor_state.screen_bottom1 = {}edit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'def', 'baseline/screen:1')y = y + Editor_state.line_heightApp.screen.check(y, 'ghi', 'baseline/screen:2')y = y + Editor_state.line_heightApp.screen.check(y, 'jkl', 'baseline/screen:3')-- after hitting the up arrow the screen scrolls up to previous text lineedit.run_after_keychord(Editor_state, 'up')check_eq(Editor_state.screen_top1.line, 1, 'screen_top')check_eq(Editor_state.cursor1.line, 1, 'cursor')endfunction test_up_arrow_scrolls_up_by_one_screen_line() - file deletion: source_text.lua source_text.lua
function ends_with(s, suffix)if #s < #suffix thenreturn falseendfor i=0,#suffix-1 doif s:sub(#s-i,#s-i) ~= suffix:sub(#suffix-i,#suffix-i) thenreturn falseendendreturn trueendfunction starts_with(s, prefix)if #s < #prefix thenreturn falseendfor i=1,#prefix doif s:sub(i,i) ~= prefix:sub(i,i) thenreturn falseendendreturn trueendState.cursor1 = {line=State.screen_bottom1.line,pos=Text.to_pos_on_line(State, State.screen_bottom1.line, State.right-5, App.screen.height-5),}endendend-- slightly expensive since it redraws the screenfunction Text.cursor_out_of_screen(State)App.draw()return State.cursor_y == nil-- this approach is cheaper and almost works, except on the final screen-- where file ends above bottom of screen--? local botpos = Text.pos_at_start_of_screen_line(State, State.cursor1)--? local botline1 = {line=State.cursor1.line, pos=botpos}--? return Text.lt1(State.screen_bottom1, botline1)endreturn {line=loc2.line-1, screen_line=#State.line_cache[loc2.line-1].screen_line_starting_pos, screen_pos=1}endendlocal l = State.lines[loc2.line-1]Text.populate_screen_line_starting_pos(State, loc2.line-1)elseif State.lines[loc2.line-1].mode == 'drawing' thenreturn {line=loc2.line-1, screen_line=1, screen_pos=1}elsereturn a.pos <= b.posendif a.line > b.line thenreturn falseendreturn a.pos < b.posendfunction Text.le1(a, b)if a.line < b.line thenreturn trueendfunction Text.eq1(a, b)return a.line == b.line and a.pos == b.posendfunction Text.lt1(a, b)if a.line < b.line thenreturn trueendif a.line > b.line thenreturn falseendlocal s = string.sub(line.data, screen_line_starting_byte_offset)--? print('return', mx, Text.nearest_cursor_pos(s, mx, State.left), '=>', screen_line_starting_pos + Text.nearest_cursor_pos(s, mx, State.left) - 1)return screen_line_starting_pos + Text.nearest_cursor_pos(s, mx, State.left) - 1endy = nextyendassert(false)endfunction Text.screen_line_width(State, line_index, i)local line = State.lines[line_index]local line_cache = State.line_cache[line_index]local start_pos = line_cache.screen_line_starting_pos[i]local start_offset = Text.offset(line.data, start_pos)local screen_lineif i < #line_cache.screen_line_starting_pos thenlocal past_end_pos = line_cache.screen_line_starting_pos[i+1]local past_end_offset = Text.offset(line.data, past_end_pos)screen_line = string.sub(line.data, start_offset, past_end_offset-1)elsescreen_line = string.sub(line.data, start_pos)endlocal screen_line_text = App.newText(love.graphics.getFont(), screen_line)return App.width(screen_line_text)endreturn line_cache.screen_line_starting_pos[screen_line_index+1]-1endif screen_line_index < #line_cache.screen_line_starting_pos and mx > State.left + Text.screen_line_width(State, line_index, screen_line_index) then--? print('past end of non-final line; return')local start_screen_line_index = Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos)for screen_line_index = start_screen_line_index,#line_cache.screen_line_starting_pos dolocal screen_line_starting_pos = line_cache.screen_line_starting_pos[screen_line_index]local screen_line_starting_byte_offset = Text.offset(line.data, screen_line_starting_pos)--? print('iter', y, screen_line_index, screen_line_starting_pos, string.sub(line.data, screen_line_starting_byte_offset))local nexty = y + State.line_heightif my < nexty then-- On all wrapped screen lines but the final one, clicks past end of-- line position cursor on final character of screen line.-- (The final screen line positions past end of screen line as always.)Text.populate_screen_line_starting_pos(State, line_index)return y < line_cache.starty + State.line_height*(#line_cache.screen_line_starting_pos - Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) + 1)end-- convert mx,my in pixels to schema-1 coordinates--? print('snap =>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks--? print(y, 'top2:', top2.line, top2.screen_line, top2.screen_pos)if top2.line == 1 and top2.screen_line == 1 then break endtop2.screen_pos = 1 -- start of screen line--? print('snap', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)--? print('cursor pos '..tostring(State.cursor1.pos)..' is on the #'..tostring(top2.screen_line)..' screen line down')local y = App.screen.height - State.line_height-- duplicate some logic from love.drawwhile true do--? print('to2: =>', top2.line, top2.screen_line, top2.screen_pos)-- slide to start of screen line--? print('to2:', State.cursor1.line, State.cursor1.pos)local top2 = Text.to2(State, State.cursor1)return screen_lines[#screen_lines] <= State.cursor1.posendfunction Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State)State.screen_top1 = {line=State.cursor1.line,pos=Text.pos_at_start_of_screen_line(State, State.cursor1),}Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaksendendassert(State.lines[State.cursor1.line].mode == 'text')if State.cursor1.pos > 1 thenState.cursor1.pos = State.cursor1.pos-1if Text.cursor_out_of_screen(State) thenText.snap_cursor_to_bottom_of_screen(State)endendfunction Text.match(s, pos, pat)local start_offset = Text.offset(s, pos)assert(start_offset)local end_offset = Text.offset(s, pos+1)assert(end_offset > start_offset)local curr = s:sub(start_offset, end_offset-1)return curr:match(pat)endfunction Text.left(State)function Text.word_right(State)-- skip some whitespacewhile true doif State.cursor1.pos > utf8.len(State.lines[State.cursor1.line].data) thenbreakendif Text.match(State.lines[State.cursor1.line].data, State.cursor1.pos, '%S') thenbreakendText.right_without_scroll(State)end-- skip some non-whitespacewhile true do-- skip some whitespacewhile true doif State.cursor1.pos == 1 thenbreakendif Text.match(State.lines[State.cursor1.line].data, State.cursor1.pos-1, '%S') thenbreakendText.left(State)endState.cursor1.pos = utf8.len(State.lines[State.cursor1.line].data) + 1if Text.cursor_out_of_screen(State) thenText.snap_cursor_to_bottom_of_screen(State)endendfunction Text.word_left(State)State.screen_top1 = {line=State.cursor1.line, pos=State.cursor1.pos} -- copyendendfunction Text.end_of_line(State)State.cursor1.pos = 1if Text.lt1(State.cursor1, State.screen_top1) thenelse-- move down one screen line in current linelocal scroll_down = Text.le1(State.screen_bottom1, State.cursor1)--? print('cursor is NOT at final screen line of its line')local screen_line_starting_pos, screen_line_index = Text.pos_at_start_of_screen_line(State, State.cursor1)Text.populate_screen_line_starting_pos(State, State.cursor1.line)local new_screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos[screen_line_index+1]--? print('switching pos of screen line at cursor from '..tostring(screen_line_starting_pos)..' to '..tostring(new_screen_line_starting_pos))local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos)local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset)State.cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1--? print('cursor pos is now', State.cursor1.line, State.cursor1.pos)if scroll_down then--? print('scroll up preserving cursor')Text.snap_cursor_to_bottom_of_screen(State)--? print('screen top after:', State.screen_top1.line, State.screen_top1.pos)endState.screen_top1 = {line=State.cursor1.line,pos=Text.pos_at_start_of_screen_line(State, State.cursor1),}Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaksendendState.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State)--? print('top now', State.screen_top1.line)Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks--? print('pagedown end')endfunction Text.up(State)State.screen_top1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos}end--? print('setting top to', State.screen_top1.line, State.screen_top1.pos)if bot2.screen_line > 1 thenbot2.screen_line = math.max(bot2.screen_line-10, 1)endlocal new_top1 = Text.to1(State, bot2)if Text.lt1(State.screen_top1, new_top1) thenState.screen_top1 = new_top1else-- If a line/paragraph gets to a page boundary, I often want to scroll-- before I get to the bottom.-- However, only do this if it makes forward progress.local bot2 = Text.to2(State, State.screen_bottom1)State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State)--? print(State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)--? print('pageup end')endfunction Text.pagedown(State)--? print('pagedown')if State.screen_top1.line == 1 and State.screen_top1.pos == 1 then break endif State.lines[State.screen_top1.line].mode == 'text' theny = y - State.line_heightelseif State.lines[State.screen_top1.line].mode == 'drawing' theny = y - Drawing_padding_height - Drawing.pixels(State.lines[State.screen_top1.line].h, State.width)endlocal byte_offset = Text.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)table.insert(State.lines, State.cursor1.line+1, {mode='text', data=string.sub(State.lines[State.cursor1.line].data, byte_offset)})table.insert(State.line_cache, State.cursor1.line+1, {})State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)Text.clear_screen_line_cache(State, State.cursor1.line)State.cursor1 = {line=State.cursor1.line+1, pos=1}endfunction Text.pageup(State)--? print('pageup')-- duplicate some logic from love.drawlocal top2 = Text.to2(State, State.screen_top1)--? print(App.screen.height)local y = App.screen.height - State.line_heightwhile y >= State.top do--? print(y, top2.line, top2.screen_line, top2.screen_pos)State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}endState.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}endState.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}endState.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}endState.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}endState.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}endState.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}endState.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}endState.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}endState.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}endif State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) thenlocal byte_start = utf8.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)local byte_end = utf8.offset(State.lines[State.cursor1.line].data, State.cursor1.pos+1)if byte_start thenif byte_end thenState.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_start-1)..string.sub(State.lines[State.cursor1.line].data, byte_end)elseState.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_start-1)end-- no change to State.cursor1.posif State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) thenbefore = snapshot(State, State.cursor1.line)elsebefore = snapshot(State, State.cursor1.line, State.cursor1.line+1)endState.screen_top1 = {line=State.cursor1.line,pos=Text.pos_at_start_of_screen_line(State, State.cursor1),}Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaksendText.clear_screen_line_cache(State, State.cursor1.line)assert(Text.le1(State.screen_top1, State.cursor1))schedule_save(State)record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})elseif chord == 'delete' thenif State.cursor1.pos > 1 thenbefore = snapshot(State, State.cursor1.line)local byte_start = utf8.offset(State.lines[State.cursor1.line].data, State.cursor1.pos-1)local byte_end = utf8.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)if byte_start thenif byte_end thenState.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_start-1)..string.sub(State.lines[State.cursor1.line].data, byte_end)elseState.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_start-1)endState.cursor1.pos = State.cursor1.pos-1local byte_offset = Text.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)..t..string.sub(State.lines[State.cursor1.line].data, byte_offset)Text.clear_screen_line_cache(State, State.cursor1.line)State.cursor1.pos = State.cursor1.pos+1endreturn y, screen_line_starting_posendfunction Text.draw_cursor(State, x, y)-- blink every 0.5sif math.floor(Cursor_time*2)%2 == 0 thenApp.color(Cursor_color)love.graphics.rectangle('fill', x,y, 3,State.line_height)endState.cursor_x = xState.cursor_y = y+State.line_heightendfunction Text.populate_screen_line_starting_pos(State, line_index)local line = State.lines[line_index]if Focus == 'edit' and not hide_cursor and State.search_term == nil thenif line_index == State.cursor1.line and State.cursor1.pos == pos thenText.draw_cursor(State, x, y)endif line_index == State.cursor1.line thenif pos <= State.cursor1.pos and pos + frag_len > State.cursor1.pos thenif State.search_term thenif State.lines[State.cursor1.line].data:sub(State.cursor1.pos, State.cursor1.pos+utf8.len(State.search_term)-1) == State.search_term thenlocal lo_px = Text.draw_highlight(State, line, x,y, pos, State.cursor1.pos, State.cursor1.pos+utf8.len(State.search_term))App.color(Text_color)love.graphics.print(State.search_term, x+lo_px,y)endelseif Focus == 'edit' thenText.draw_cursor(State, x+Text.x(frag, State.cursor1.pos-pos+1), y)App.color(Text_color)endendendx = x + frag_widthendpos = pos + frag_lenendreturn y, screen_line_starting_posendscreen_line_starting_pos = posx = State.left-- wrap long lineslocal x = State.leftlocal pos = 1local screen_line_starting_pos = startposText.compute_fragments(State, line_index)local pos = 1initialize_color()for _, f in ipairs(line_cache.fragments) doApp.color(Text_color)local frag, frag_text = f.data, f.textselect_color(frag)local frag_len = utf8.len(frag)--? print('text.draw:', frag, 'at', line_index,pos, 'after', x,y)if pos < startpos then-- render nothing--? print('skipping', frag)else-- render fragmentlocal frag_width = App.width(frag_text)if x + frag_width > State.right thenassert(x > State.left) -- no overfull linesy = y + State.line_heightif y + State.line_height > App.screen.height then-- return the final y, and position of start of final screen line drawnfunction Text.draw(State, line_index, y, startpos, hide_cursor)local line = State.lines[line_index]local line_cache = State.line_cache[line_index]line_cache.starty = yline_cache.startpos = startpos - file deletion: source_edit.lua source_edit.lua
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.setClipboardText(s)endelseif chord == 'C-x' thenfor _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scrolllocal s = Text.cut_selection(State, State.left, State.right)if s thenApp.setClipboardText(s)endschedule_save(State)elseif chord == 'C-v' thenfor _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll-- 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.getClipboardText()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)})if 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' thenfor _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scrolllocal event = undo_event(State)if event thenlocal src = event.beforeState.screen_top1 = deepcopy(src.screen_top)State.cursor1 = deepcopy(src.cursor)cursor={line=State.cursor1.line, pos=State.cursor1.pos},screen_top={line=State.screen_top1.line, pos=State.screen_top1.pos},}assert(State.search_text == nil)State.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 = {State.cursor1 = {line=line_index,pos=Text.to_pos_on_line(State, line_index, x, y),}--? print('cursor', State.cursor1.line, State.cursor1.pos)if State.mousepress_shift thenif State.old_selection1.line == nil thenState.selection1 = State.old_cursor1elseState.selection1 = State.old_selection1endendState.old_cursor1, State.old_selection1, State.mousepress_shift = nilif eq(State.cursor1, State.selection1) thenState.selection1 = {}endbreakendendend--? print('selection:', State.selection1.line, State.selection1.pos)State.selection1 = {line=line_index,pos=Text.to_pos_on_line(State, line_index, x, y),}--? print('selection', State.selection1.line, State.selection1.pos)breakendelseif line.mode == 'drawing' thenlocal line_cache = State.line_cache[line_index]if Drawing.in_drawing(line, line_cache, x, y, State.left,State.right) thenState.lines.current_drawing_index = line_indexState.lines.current_drawing = lineDrawing.before = snapshot(State, line_index)--? print('press', State.selection1.line, State.selection1.pos)if mouse_press_consumed_by_any_button_handler(State, x,y, mouse_button) then-- press on a button and it returned 'true' to short-circuitreturnendfor line_index,line in ipairs(State.lines) do-- give some time for the OS to flush everything to disklove.timer.sleep(0.1)endendy, State.screen_bottom1.pos = Text.draw(State, line_index, y, startpos, hide_cursor)y = y + State.line_height--? print('=> y', y)elseif line.mode == 'drawing' theny = y+Drawing_padding_topDrawing.draw(State, line_index, y)y = y + Drawing.pixels(line.h, State.width) + Drawing_padding_bottomelseprint(line.mode)assert(false)startpos = State.screen_top1.posendif line.data == '' then-- button to insert new drawingbutton(State, 'draw', {x=4,y=y+4, w=12,h=12, color={1,1,0},icon = icon.insert_drawing,onpress1 = function()Drawing.before = snapshot(State, line_index-1, line_index)table.insert(State.lines, line_index, {mode='drawing', y=y, h=256/2, points={}, shapes={}, pending={}})table.insert(State.line_cache, line_index, {})if State.cursor1.line >= line_index thenState.cursor1.line = State.cursor1.line+1endschedule_save(State)record_undo_event(State, {before=Drawing.before, after=snapshot(State, line_index-1, line_index+1)})end,})--? print('text.draw', y, line_index)local startpos = 1if line_index == State.screen_top1.line thenState.screen_bottom1 = {line=line_index, pos=nil}if line.mode == 'text' then--? print('draw:', y, line_index, line)if y + State.line_height > App.screen.height then break endprint(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)assert(false)endState.cursor_x = nilState.cursor_y = nillocal y = State.top--? print('== draw')for line_index = State.screen_top1.line,#State.lines dolocal line = State.lines[line_index]left = math.floor(left),right = math.floor(right),width = right-left,screen_top1 = {line=1, pos=1}, -- position of start of screen line at top of screencursor1 = {line=1, pos=1}, -- position of cursorscreen_bottom1 = {line=1, pos=1}, -- position of start of screen line at bottom of screenselection1 = {},-- some extra state to compute selection between mouse press and releaseold_cursor1 = nil,old_selection1 = nil,mousepress_shift = nil,-- when selecting text, avoid recomputing some state on every single framerecent_mouse = {},lines = {{mode='text', data=''}}, -- array of lines-- Lines can be too long to fit on screen, in which case they _wrap_ into-- multiple _screen lines_.-- rendering wrapped text lines needs some additional short-lived data per line:-- startpos, the index of data the line starts rendering from, can only be >1 for topmost line on screen-- starty, the y coord in pixels the line starts rendering from-- fragments: snippets of rendered love.graphics.Text, guaranteed to not straddle screen lines-- screen_line_starting_pos: optional array of grapheme indices if it wraps over more than one screen lineline_cache = {},-- Given wrapping, any potential location for the text cursor can be described in two ways:-- * schema 1: As a combination of line index and position within a line (in utf8 codepoint units)-- * schema 2: As a combination of line index, screen line index within the line, and a position within the screen line.-- a line is either text or a drawing-- a text is a table with:-- mode = 'text',-- string data, - file deletion: source.lua source.lua
-- environment for a mutable file-- TODO: some initialization is also happening in load_settings/initialize_default_settings. Clean that up. - file deletion: log_browser.lua log_browser.lua
Editor_state.cursor1 = {line=line.line_number, pos=1}-- make sure it's visible-- TODO: handle extremely long linesEditor_state.screen_top1.line = math.max(0, Editor_state.cursor1.line-5)-- show cursorFocus = 'edit'Log_browser_state.cursor1 = {line=1, pos=1}endSection_stack = {}Section_border_color = {r=0.7, g=0.7, b=0.7}Cursor_line_background_color = {r=0.7, g=0.7, b=0, a=0.1}Section_border_padding_horizontal = 30 -- TODO: adjust this based on font height (because we draw text vertically along the bordersSection_border_padding_vertical = 15 -- TODO: adjust this based on font heightlog_browser = {}function log_browser.parse(State)for _,line in ipairs(State.lines) doif line.data ~= '' then - edit in text.lua at line 622
--? print('snap', State.screen_top1.line, State.screen_top1.pos, State.screen_top1.posB, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos) - resurrect zombie in README.md at line 62