diff options
-rw-r--r-- | Manual_tests.md | 2 | ||||
-rw-r--r-- | README.md | 100 | ||||
-rw-r--r-- | edit.lua | 253 | ||||
-rw-r--r-- | file.lua | 134 | ||||
-rw-r--r-- | main.lua | 12 | ||||
-rw-r--r-- | reference.md | 6 | ||||
-rw-r--r-- | run.lua | 4 | ||||
-rw-r--r-- | select.lua | 10 | ||||
-rw-r--r-- | source.lua | 4 | ||||
-rw-r--r-- | text.lua | 155 | ||||
-rw-r--r-- | text_tests.lua | 129 | ||||
-rw-r--r-- | undo.lua | 11 |
12 files changed, 147 insertions, 673 deletions
diff --git a/Manual_tests.md b/Manual_tests.md index 4318496..236cd27 100644 --- a/Manual_tests.md +++ b/Manual_tests.md @@ -6,7 +6,7 @@ record those here. Initializing settings: - delete app settings, start; window opens running the text editor - quit while running the text editor, restart; window opens running the text editor in same position+dimensions - - quit while editing source (color; no drawings; no selection), restart; window opens editing source in same position+dimensions + - quit while editing source (color; no selection), restart; window opens editing source in same position+dimensions - start out running the text editor, move window, press ctrl+e twice; window is running text editor in same position+dimensions - start out editing source, move window, press ctrl+e twice; window is editing source in same position+dimensions - no log file; switching to source works diff --git a/README.md b/README.md index 50a4dd0..f94a998 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ -# Plain text with lines +# An editor for plain text. + +Not very useful by itself, but it's a fork of [lines.love](http://akkartik.name/lines.html) +that you can take in other directions besides line drawings, while easily +sharing patches between forks. -An editor for plain text where you can also seamlessly insert line drawings. Designed above all to be easy to modify and give you early warning if your modifications break something. -http://akkartik.name/lines.html - ## Getting started Install [LÖVE](https://love2d.org). It's just a 5MB download, open-source and @@ -17,13 +18,13 @@ optionally with a file path to edit. Alternatively, turn it into a .love file you can double-click on: ``` -$ zip -r /tmp/lines.love *.lua +$ zip -r /tmp/text.love *.lua ``` -By default, lines.love reads/writes the file `lines.txt` in +By default, it reads/writes the file `lines.txt` in [a directory relative to this app](https://love2d.org/wiki/love.filesystem.getSourceBaseDirectory). -To open a different file, drop it on the lines.love window. +To open a different file, drop it on the app window. ## Keyboard shortcuts @@ -35,12 +36,7 @@ While editing text: * `alt+right`/`alt+left` to jump to the next/previous word, respectively * mouse drag or `shift` + movement to select text, `ctrl+a` to select all -For shortcuts while editing drawings, consult the online help. Either: -* hover on a drawing and hit `ctrl+h`, or -* click on a drawing to start a stroke and then press and hold `h` to see your - options at any point during a stroke. - -lines.love has been exclusively tested so far with a US keyboard layout. If +Exclusively tested so far with a US keyboard layout. If you use a different layout, please let me know if things worked, or if you found anything amiss: http://akkartik.name/contact @@ -51,67 +47,43 @@ found anything amiss: http://akkartik.name/contact * No support yet for right-to-left languages. * Undo/redo may be sluggish in large files. Large files may grow sluggish in - other ways. lines.love works well in all circumstances with files under - 50KB. + other ways. Works well in all circumstances with files under 50KB. * If you kill the process, say by force-quitting because things things get sluggish, you can lose data. -* The text cursor will always stay on the screen. This can have some strange - implications: - - * A long series of drawings will get silently skipped when you hit - page-down, until a line of text can be showed on screen. - * If there's no line of text at the top of the file, you may not be able - to scroll back up to the top with page-up. - - So far this app isn't really designed for drawing-heavy files. For now I'm - targeting mostly-text files with a few drawings mixed in. - -* No clipping yet for drawings. In particular, circles/squares/rectangles and - point labels can overflow a drawing. - -* Touchpads can drag the mouse pointer using a light touch or a heavy click. - On Linux, drags using the light touch get interrupted when a key is pressed. - You'll have to press down to drag. - * Can't scroll while selecting text with mouse. * No scrollbars yet. That stuff is hard. ## Mirrors and Forks -Updates to lines.love can be downloaded from the following mirrors in addition -to the website above: -* https://github.com/akkartik/lines.love -* https://repo.or.cz/lines.love.git -* https://tildegit.org/akkartik/lines.love -* https://git.tilde.institute/akkartik/lines.love -* https://git.sr.ht/~akkartik/lines.love -* https://notabug.org/akkartik/lines.love -* https://codeberg.org/akkartik/lines.love -* https://pagure.io/lines.love - -Forks of lines.love are encouraged. If you show me your fork, I'll link to it -here. - -* https://github.com/akkartik/lines-polygon-experiment -- an experiment that - uses separate shortcuts for regular polygons. `ctrl+3` for triangles, - `ctrl+4` for squares, etc. -* https://git.sr.ht/~akkartik/text.love -- a stripped down version without - drawings; useful starting point for some forks -* https://git.sr.ht/~akkartik/pensieve.love -- a note-taking app on an - infinite 2D surface. Still in development. -* https://git.sr.ht/~akkartik/capture.love -- a blank-slate mode for the - note-taking app, so all the stuff pensieve.love puts on screen doesn't cause - you to forget what you came to write down. - -## Associated tools - -* https://codeberg.org/akkartik/lines2md exports lines.love files to Markdown - and (non-editable) SVG. -* https://git.sr.ht/~akkartik/lines2html.love exports lines.love files to html - and inline SVG. +This repo is a fork of [lines.love](http://akkartik.name/lines.html), an +editor for plain text where you can also seamlessly insert line drawings. +Updates to it can be downloaded from the following mirrors: + +* https://repo.or.cz/text.love.git +* https://tildegit.org/akkartik/text.love +* https://git.tilde.institute/akkartik/text.love +* https://git.sr.ht/~akkartik/text.love +* https://github.com/akkartik/text.love +* https://codeberg.org/akkartik/text.love +* https://notabug.org/akkartik/text.love +* https://pagure.io/text.love + +Further forks are encouraged. If you show me your fork, I'll link to it here. + +* https://git.sr.ht/~akkartik/view.love -- a stripped down version without + support for modifying files; useful starting point for some forks. +* https://git.sr.ht/~akkartik/pong.love -- a fairly minimal example app that + can edit and debug its own source code. +* https://git.sr.ht/~akkartik/template-live-editor -- a template for + building "free-wheeling" live programs (easy to fork, can be modified as + they run), with a text editor primitive. +* https://git.sr.ht/~akkartik/luaML.love -- a free-wheeling 'browser' for a + Lua-based markup language built as a live program. +* https://git.sr.ht/~akkartik/driver.love -- a programming environment for + modifying free-wheeling programs while they run. ## Feedback diff --git a/edit.lua b/edit.lua index 0f5bef5..45aa879 100644 --- a/edit.lua +++ b/edit.lua @@ -1,51 +1,19 @@ -- some constants people might like to tweak Text_color = {r=0, g=0, b=0} Cursor_color = {r=1, g=0, b=0} -Stroke_color = {r=0, g=0, b=0} -Current_stroke_color = {r=0.7, g=0.7, b=0.7} -- in process of being drawn -Current_name_background_color = {r=1, g=0, b=0, a=0.1} -- name currently being edited Focus_stroke_color = {r=1, g=0, b=0} -- what mouse is hovering over Highlight_color = {r=0.7, g=0.7, b=0.9} -- selected text -Icon_color = {r=0.7, g=0.7, b=0.7} -- color of current mode icon in drawings -Help_color = {r=0, g=0.5, b=0} -Help_background_color = {r=0, g=0.5, b=0, a=0.1} Margin_top = 15 Margin_left = 25 Margin_right = 25 -Drawing_padding_top = 10 -Drawing_padding_bottom = 10 -Drawing_padding_height = Drawing_padding_top + Drawing_padding_bottom - -Same_point_distance = 4 -- pixel distance at which two points are considered the same - edit = {} -- run in both tests and a real run function edit.initialize_state(top, left, right, font_height, line_height) -- currently always draws to bottom of screen local result = { - -- a line is either text or a drawing - -- a text is a table with: - -- mode = 'text', - -- string data, - -- a drawing is a table with: - -- mode = 'drawing' - -- a (y) coord in pixels (updated while painting screen), - -- a (h)eight, - -- an array of points, and - -- an array of shapes - -- a shape is a table containing: - -- a mode - -- an array points for mode 'freehand' (raw x,y coords; freehand drawings don't pollute the points array of a drawing) - -- an array vertices for mode 'polygon', 'rectangle', 'square' - -- p1, p2 for mode 'line' - -- center, radius for mode 'circle' - -- center, radius, start_angle, end_angle for mode 'arc' - -- Unless otherwise specified, coord fields are normalized; a drawing is always 256 units wide - -- The field names are carefully chosen so that switching modes in midstream - -- remembers previously entered points where that makes sense. - lines = {{mode='text', data=''}}, -- array of lines + lines = {{data=''}}, -- array of strings -- Lines can be too long to fit on screen, in which case they _wrap_ into -- multiple _screen lines_. @@ -82,9 +50,6 @@ function edit.initialize_state(top, left, right, font_height, line_height) -- c cursor_x = 0, cursor_y = 0, - current_drawing_mode = 'line', - previous_drawing_mode = nil, -- extra state for some ephemeral modes like moving/deleting/naming points - font_height = font_height, line_height = line_height, @@ -112,10 +77,9 @@ function edit.check_locs(State) -- throw away all cursor state entirely if edit.invalid1(State, State.screen_top1) or edit.invalid1(State, State.cursor1) - or not edit.cursor_on_text(State) or not Text.le1(State.screen_top1, State.cursor1) then State.screen_top1 = {line=1, pos=1} - edit.put_cursor_on_first_text_line(State) + State.cursor1 = {line=1, pos=1} end end @@ -126,22 +90,7 @@ function edit.invalid1(State, loc1) return loc1.pos > #State.lines[loc1.line].data end -function edit.cursor_on_text(State) - return State.cursor1.line <= #State.lines - and State.lines[State.cursor1.line].mode == 'text' -end - -function edit.put_cursor_on_first_text_line(State) - for i,line in ipairs(State.lines) do - if line.mode == 'text' then - State.cursor1 = {line=i, pos=1} - break - end - end -end - function edit.draw(State) - State.button_handlers = {} App.color(Text_color) if #State.lines ~= #State.line_cache then print(('line_cache is out of date; %d when it should be %d'):format(#State.line_cache, #State.lines)) @@ -160,38 +109,13 @@ function edit.draw(State) --? print('draw:', y, line_index, line) if y + State.line_height > App.screen.height then break end State.screen_bottom1 = {line=line_index, pos=nil} - if line.mode == 'text' then ---? print('text.draw', y, line_index) - local startpos = 1 - if line_index == State.screen_top1.line then - startpos = State.screen_top1.pos - end - if line.data == '' then - -- button to insert new drawing - button(State, 'draw', {x=State.left-Margin_left+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 then - State.cursor1.line = State.cursor1.line+1 - end - schedule_save(State) - record_undo_event(State, {before=Drawing.before, after=snapshot(State, line_index-1, line_index+1)}) - end, - }) - end - y, State.screen_bottom1.pos = Text.draw(State, line_index, y, startpos) ---? print('=> y', y) - elseif line.mode == 'drawing' then - y = y+Drawing_padding_top - Drawing.draw(State, line_index, y) - y = y + Drawing.pixels(line.h, State.width) + Drawing_padding_bottom - else - print(line.mode) - assert(false) +--? print('text.draw', y, line_index) + local startpos = 1 + if line_index == State.screen_top1.line then + startpos = State.screen_top1.pos end + y, State.screen_bottom1.pos = Text.draw(State, line_index, y, startpos) +--? print('=> y', y) end if State.search_term then Text.draw_search_bar(State) @@ -199,7 +123,6 @@ function edit.draw(State) end function edit.update(State, dt) - Drawing.update(State, dt) if State.next_save and State.next_save < Current_time then save_to_disk(State) State.next_save = nil @@ -224,42 +147,26 @@ end function edit.mouse_press(State, x,y, mouse_button) if State.search_term then return end --? print('press', State.cursor1.line) - if mouse_press_consumed_by_any_button_handler(State, x,y, mouse_button) then - -- press on a button and it returned 'true' to short-circuit - return - end - for line_index,line in ipairs(State.lines) do - if line.mode == 'text' then - if Text.in_line(State, line_index, x,y) then - -- delicate dance between cursor, selection and old cursor/selection - -- scenarios: - -- regular press+release: sets cursor, clears selection - -- shift press+release: - -- sets selection to old cursor if not set otherwise leaves it untouched - -- sets cursor - -- press and hold to start a selection: sets selection on press, cursor on release - -- press and hold, then press shift: ignore shift - -- i.e. mouse_release should never look at shift state - State.old_cursor1 = State.cursor1 - State.old_selection1 = State.selection1 - State.mousepress_shift = App.shift_down() - State.selection1 = { - line=line_index, - pos=Text.to_pos_on_line(State, line_index, x, y), - } ---? print('selection', State.selection1.line, State.selection1.pos) - break - end - elseif line.mode == 'drawing' then - local line_cache = State.line_cache[line_index] - if Drawing.in_drawing(line, line_cache, x, y, State.left,State.right) then - State.lines.current_drawing_index = line_index - State.lines.current_drawing = line - Drawing.before = snapshot(State, line_index) - Drawing.mouse_press(State, line_index, x,y, mouse_button) - break - end + if Text.in_line(State, line_index, x,y) then + -- delicate dance between cursor, selection and old cursor/selection + -- scenarios: + -- regular press+release: sets cursor, clears selection + -- shift press+release: + -- sets selection to old cursor if not set otherwise leaves it untouched + -- sets cursor + -- press and hold to start a selection: sets selection on press, cursor on release + -- press and hold, then press shift: ignore shift + -- i.e. mouse_released should never look at shift state + State.old_cursor1 = State.cursor1 + State.old_selection1 = State.selection1 + State.mousepress_shift = App.shift_down() + State.selection1 = { + line=line_index, + pos=Text.to_pos_on_line(State, line_index, x, y), + } +--? print('selection', State.selection1.line, State.selection1.pos) + break end end end @@ -267,40 +174,29 @@ end function edit.mouse_release(State, x,y, mouse_button) if State.search_term then return end --? print('release', State.cursor1.line) - if State.lines.current_drawing then - Drawing.mouse_release(State, x,y, mouse_button) - schedule_save(State) - if Drawing.before then - record_undo_event(State, {before=Drawing.before, after=snapshot(State, State.lines.current_drawing_index)}) - Drawing.before = nil - end - else - for line_index,line in ipairs(State.lines) do - if line.mode == 'text' then - if Text.in_line(State, line_index, x,y) then ---? print('reset selection') - 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 then - if State.old_selection1.line == nil then - State.selection1 = State.old_cursor1 - else - State.selection1 = State.old_selection1 - end - end - State.old_cursor1, State.old_selection1, State.mousepress_shift = nil - if eq(State.cursor1, State.selection1) then - State.selection1 = {} - end - break + for line_index,line in ipairs(State.lines) do + if Text.in_line(State, line_index, x,y) then +--? print('reset selection') + 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 then + if State.old_selection1.line == nil then + State.selection1 = State.old_cursor1 + else + State.selection1 = State.old_selection1 end end + State.old_cursor1, State.old_selection1, State.mousepress_shift = nil + if eq(State.cursor1, State.selection1) then + State.selection1 = {} + end + break end ---? print('selection:', State.selection1.line, State.selection1.pos) end +--? print('selection:', State.selection1.line, State.selection1.pos) end function edit.mouse_wheel_move(State, dx,dy) @@ -322,25 +218,15 @@ function edit.text_input(State, t) if State.search_term then State.search_term = State.search_term..t Text.search_next(State) - elseif State.lines.current_drawing and State.current_drawing_mode == 'name' then - local before = snapshot(State, State.lines.current_drawing_index) - local drawing = State.lines.current_drawing - local p = drawing.points[drawing.pending.target_point] - p.name = p.name..t - record_undo_event(State, {before=before, after=snapshot(State, State.lines.current_drawing_index)}) else - local drawing_index, drawing = Drawing.current_drawing(State) - if drawing_index == nil then - for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll - Text.text_input(State, t) - end + for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll + Text.text_input(State, t) end schedule_save(State) end function edit.keychord_press(State, chord, key) if State.selection1.line and - not State.lines.current_drawing 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 @@ -398,8 +284,6 @@ function edit.keychord_press(State, chord, key) State.selection1 = deepcopy(src.selection) patch(State.lines, event.after, event.before) patch_placeholders(State.line_cache, event.after, event.before) - -- invalidate various cached bits of lines - State.lines.current_drawing = nil -- if we're scrolling, reclaim all fragments to avoid memory leaks Text.redraw_all(State) schedule_save(State) @@ -413,8 +297,6 @@ function edit.keychord_press(State, chord, key) State.cursor1 = deepcopy(src.cursor) State.selection1 = deepcopy(src.selection) patch(State.lines, event.before, event.after) - -- invalidate various cached bits of lines - State.lines.current_drawing = nil -- if we're scrolling, reclaim all fragments to avoid memory leaks Text.redraw_all(State) schedule_save(State) @@ -455,44 +337,7 @@ function edit.keychord_press(State, chord, key) end schedule_save(State) record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)}) - -- dispatch to drawing or text - elseif App.mouse_down(1) or chord:sub(1,2) == 'C-' then - -- DON'T reset line_cache.starty here - local drawing_index, drawing = Drawing.current_drawing(State) - if drawing_index then - local before = snapshot(State, drawing_index) - Drawing.keychord_press(State, chord) - record_undo_event(State, {before=before, after=snapshot(State, drawing_index)}) - schedule_save(State) - end - elseif chord == 'escape' and not App.mouse_down(1) then - for _,line in ipairs(State.lines) do - if line.mode == 'drawing' then - line.show_help = false - end - end - elseif State.lines.current_drawing and State.current_drawing_mode == 'name' then - if chord == 'return' then - State.current_drawing_mode = State.previous_drawing_mode - State.previous_drawing_mode = nil - else - local before = snapshot(State, State.lines.current_drawing_index) - local drawing = State.lines.current_drawing - local p = drawing.points[drawing.pending.target_point] - if chord == 'escape' then - p.name = nil - record_undo_event(State, {before=before, after=snapshot(State, State.lines.current_drawing_index)}) - elseif chord == 'backspace' then - local len = utf8.len(p.name) - if len > 0 then - local byte_offset = Text.offset(p.name, len-1) - if len == 1 then byte_offset = 0 end - p.name = string.sub(p.name, 1, byte_offset) - record_undo_event(State, {before=before, after=snapshot(State, State.lines.current_drawing_index)}) - end - end - end - schedule_save(State) + -- dispatch to text else for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll Text.keychord_press(State, chord) diff --git a/file.lua b/file.lua index 2140bb1..d7fcc4e 100644 --- a/file.lua +++ b/file.lua @@ -22,15 +22,11 @@ function load_from_file(infile) while true do local line = infile_next_line() if line == nil then break end - if line == '```lines' then -- inflexible with whitespace since these files are always autogenerated - table.insert(result, load_drawing(infile_next_line)) - else - table.insert(result, {mode='text', data=line}) - end + table.insert(result, {data=line}) end end if #result == 0 then - table.insert(result, {mode='text', data=''}) + table.insert(result, {data=''}) end return result end @@ -41,82 +37,12 @@ function save_to_disk(State) error('failed to write to "'..State.filename..'"') end for _,line in ipairs(State.lines) do - if line.mode == 'drawing' then - store_drawing(outfile, line) - else - outfile:write(line.data) - outfile:write('\n') - end + outfile:write(line.data) + outfile:write('\n') end outfile:close() end -function load_drawing(infile_next_line) - local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}} - while true do - local line = infile_next_line() - assert(line) - if line == '```' then break end - local shape = json.decode(line) - if shape.mode == 'freehand' then - -- no changes needed - elseif shape.mode == 'line' or shape.mode == 'manhattan' then - local name = shape.p1.name - shape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600) - drawing.points[shape.p1].name = name - name = shape.p2.name - shape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600) - drawing.points[shape.p2].name = name - elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then - for i,p in ipairs(shape.vertices) do - local name = p.name - shape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600) - drawing.points[shape.vertices[i]].name = name - end - elseif shape.mode == 'circle' or shape.mode == 'arc' then - local name = shape.center.name - shape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600) - drawing.points[shape.center].name = name - elseif shape.mode == 'deleted' then - -- ignore - else - print(shape.mode) - assert(false) - end - table.insert(drawing.shapes, shape) - end - return drawing -end - -function store_drawing(outfile, drawing) - outfile:write('```lines\n') - for _,shape in ipairs(drawing.shapes) do - if shape.mode == 'freehand' then - outfile:write(json.encode(shape), '\n') - elseif shape.mode == 'line' or shape.mode == 'manhattan' then - local line = json.encode({mode=shape.mode, p1=drawing.points[shape.p1], p2=drawing.points[shape.p2]}) - outfile:write(line, '\n') - elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then - local obj = {mode=shape.mode, vertices={}} - for _,p in ipairs(shape.vertices) do - table.insert(obj.vertices, drawing.points[p]) - end - local line = json.encode(obj) - outfile:write(line, '\n') - elseif shape.mode == 'circle' then - outfile:write(json.encode({mode=shape.mode, center=drawing.points[shape.center], radius=shape.radius}), '\n') - elseif shape.mode == 'arc' then - outfile:write(json.encode({mode=shape.mode, center=drawing.points[shape.center], radius=shape.radius, start_angle=shape.start_angle, end_angle=shape.end_angle}), '\n') - elseif shape.mode == 'deleted' then - -- ignore - else - print(shape.mode) - assert(false) - end - end - outfile:write('```\n') -end - -- for tests function load_array(a) local result = {} @@ -125,62 +51,14 @@ function load_array(a) while true do i,line = next_line(a, i) if i == nil then break end ---? print(line) - if line == '```lines' then -- inflexible with whitespace since these files are always autogenerated ---? print('inserting drawing') - i, drawing = load_drawing_from_array(next_line, a, i) ---? print('i now', i) - table.insert(result, drawing) - else ---? print('inserting text') - table.insert(result, {mode='text', data=line}) - end + table.insert(result, {data=line}) end if #result == 0 then - table.insert(result, {mode='text', data=''}) + table.insert(result, {data=''}) end return result end -function load_drawing_from_array(iter, a, i) - local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}} - local line - while true do - i, line = iter(a, i) - assert(i) ---? print(i) - if line == '```' then break end - local shape = json.decode(line) - if shape.mode == 'freehand' then - -- no changes needed - elseif shape.mode == 'line' or shape.mode == 'manhattan' then - local name = shape.p1.name - shape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600) - drawing.points[shape.p1].name = name - name = shape.p2.name - shape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600) - drawing.points[shape.p2].name = name - elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then - for i,p in ipairs(shape.vertices) do - local name = p.name - shape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600) - drawing.points[shape.vertices[i]].name = name - end - elseif shape.mode == 'circle' or shape.mode == 'arc' then - local name = shape.center.name - shape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600) - drawing.points[shape.center].name = name - elseif shape.mode == 'deleted' then - -- ignore - else - print(shape.mode) - assert(false) - end - table.insert(drawing.shapes, shape) - end - return i, drawing -end - function is_absolute_path(path) local os_path_separator = package.config:sub(1,1) if os_path_separator == '/' then diff --git a/main.lua b/main.lua index a59c483..0297c26 100644 --- a/main.lua +++ b/main.lua @@ -32,13 +32,6 @@ load_file_from_source_or_save_directory('button.lua') -- both sides require (different parts of) the logging framework load_file_from_source_or_save_directory('log.lua') --- both sides use drawings -load_file_from_source_or_save_directory('icons.lua') -load_file_from_source_or_save_directory('drawing.lua') - load_file_from_source_or_save_directory('geom.lua') - load_file_from_source_or_save_directory('help.lua') -load_file_from_source_or_save_directory('drawing_tests.lua') - -- but some files we want to only load sometimes function App.load() log_new('session') @@ -73,6 +66,11 @@ function App.load() load_file_from_source_or_save_directory('source_undo.lua') load_file_from_source_or_save_directory('colorize.lua') load_file_from_source_or_save_directory('source_text_tests.lua') + load_file_from_source_or_save_directory('icons.lua') + load_file_from_source_or_save_directory('drawing.lua') + load_file_from_source_or_save_directory('geom.lua') + load_file_from_source_or_save_directory('help.lua') + load_file_from_source_or_save_directory('drawing_tests.lua') load_file_from_source_or_save_directory('source_tests.lua') else assert(false, 'unknown app "'..Current_app..'"') diff --git a/reference.md b/reference.md index 3234cfa..5c3c29f 100644 --- a/reference.md +++ b/reference.md @@ -189,9 +189,9 @@ early warning if you break something. * `state = edit.initialize_state(top, left, right, font_height, line_height)` -- returns an object that can be used to render an interactive editor widget - for text and line drawings starting at `y=top` on the app window, between - `x=left` and `x=right`. Wraps long lines at word boundaries where possible, - or in the middle of words (no hyphenation yet) when it must. + for text starting at `y=top` on the app window, between `x=left` and + `x=right`. Wraps long lines at word boundaries where possible, or in the + middle of words (no hyphenation yet) when it must. * `edit.quit()` -- calling this ensures any final edits are flushed to disk before the app exits. diff --git a/run.lua b/run.lua index d029bed..2aba0e9 100644 --- a/run.lua +++ b/run.lua @@ -34,7 +34,7 @@ function run.initialize(arg) -- keep a few blank lines around: https://merveilles.town/@akkartik/110084833821965708 - love.window.setTitle('lines.love - '..Editor_state.filename) + love.window.setTitle('text.love - '..Editor_state.filename) @@ -123,7 +123,7 @@ function run.file_drop(file) -- keep a few blank lines around: https://merveilles.town/@akkartik/110084833821965708 - love.window.setTitle('lines.love - '..Editor_state.filename) + love.window.setTitle('text.love - '..Editor_state.filename) diff --git a/select.lua b/select.lua index efb6909..0050895 100644 --- a/select.lua +++ b/select.lua @@ -86,10 +86,8 @@ end function Text.to_pos(State, x,y) for line_index,line in ipairs(State.lines) do - if line.mode == 'text' then - if Text.in_line(State, line_index, x,y) then - return line_index, Text.to_pos_on_line(State, line_index, x,y) - end + if Text.in_line(State, line_index, x,y) then + return line_index, Text.to_pos_on_line(State, line_index, x,y) end end end @@ -169,9 +167,7 @@ function Text.selection(State) assert(minl < maxl) local result = {State.lines[minl].data:sub(min_offset)} for i=minl+1,maxl-1 do - if State.lines[i].mode == 'text' then - table.insert(result, State.lines[i].data) - end + table.insert(result, State.lines[i].data) end table.insert(result, State.lines[maxl].data:sub(1, max_offset-1)) return table.concat(result, '\n') diff --git a/source.lua b/source.lua index cbbd6cf..54d4d1d 100644 --- a/source.lua +++ b/source.lua @@ -80,7 +80,7 @@ function source.initialize() -- keep a few blank lines around: https://merveilles.town/@akkartik/110084833821965708 - love.window.setTitle('lines.love - source') + love.window.setTitle('text.love - source') @@ -231,7 +231,7 @@ function source.file_drop(file) -- keep a few blank lines around: https://merveilles.town/@akkartik/110084833821965708 - love.window.setTitle('lines.love - source') + love.window.setTitle('text.love - source') diff --git a/text.lua b/text.lua index eadb7c5..89a673a 100644 --- a/text.lua +++ b/text.lua @@ -77,7 +77,6 @@ end function Text.populate_screen_line_starting_pos(State, line_index) local line = State.lines[line_index] - if line.mode ~= 'text' then return end local line_cache = State.line_cache[line_index] if line_cache.screen_line_starting_pos then return @@ -127,7 +126,6 @@ function Text.text_input(State, t) end function Text.insert_at_cursor(State, t) - assert(State.lines[State.cursor1.line].mode == 'text') local 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) @@ -180,16 +178,11 @@ function Text.keychord_press(State, chord) end elseif State.cursor1.line > 1 then before = snapshot(State, State.cursor1.line-1, State.cursor1.line) - if State.lines[State.cursor1.line-1].mode == 'drawing' then - table.remove(State.lines, State.cursor1.line-1) - table.remove(State.line_cache, State.cursor1.line-1) - else - -- join lines - State.cursor1.pos = utf8.len(State.lines[State.cursor1.line-1].data)+1 - State.lines[State.cursor1.line-1].data = State.lines[State.cursor1.line-1].data..State.lines[State.cursor1.line].data - table.remove(State.lines, State.cursor1.line) - table.remove(State.line_cache, State.cursor1.line) - end + -- join lines + State.cursor1.pos = utf8.len(State.lines[State.cursor1.line-1].data)+1 + State.lines[State.cursor1.line-1].data = State.lines[State.cursor1.line-1].data..State.lines[State.cursor1.line].data + table.remove(State.lines, State.cursor1.line) + table.remove(State.line_cache, State.cursor1.line) State.cursor1.line = State.cursor1.line-1 end if State.screen_top1.line > #State.lines then @@ -231,10 +224,8 @@ function Text.keychord_press(State, chord) -- no change to State.cursor1.pos end elseif State.cursor1.line < #State.lines then - if State.lines[State.cursor1.line+1].mode == 'text' then - -- join lines - State.lines[State.cursor1.line].data = State.lines[State.cursor1.line].data..State.lines[State.cursor1.line+1].data - end + -- join lines + State.lines[State.cursor1.line].data = State.lines[State.cursor1.line].data..State.lines[State.cursor1.line+1].data table.remove(State.lines, State.cursor1.line+1) table.remove(State.line_cache, State.cursor1.line+1) end @@ -328,7 +319,7 @@ end function Text.insert_return(State) local 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.lines, State.cursor1.line+1, {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) @@ -344,11 +335,7 @@ function Text.pageup(State) while y >= State.top do --? print(y, top2.line, top2.screen_line, top2.screen_pos) if State.screen_top1.line == 1 and State.screen_top1.pos == 1 then break end - if State.lines[State.screen_top1.line].mode == 'text' then - y = y - State.line_height - elseif State.lines[State.screen_top1.line].mode == 'drawing' then - y = y - Drawing_padding_height - Drawing.pixels(State.lines[State.screen_top1.line].h, State.width) - end + y = y - State.line_height top2 = Text.previous_screen_line(State, top2) end State.screen_top1 = Text.to1(State, top2) @@ -382,29 +369,24 @@ function Text.pagedown(State) end function Text.up(State) - assert(State.lines[State.cursor1.line].mode == 'text') --? print('up', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos) local screen_line_starting_pos, screen_line_index = Text.pos_at_start_of_screen_line(State, State.cursor1) if screen_line_starting_pos == 1 then --? print('cursor is at first screen line of its line') -- line is done; skip to previous text line - local new_cursor_line = State.cursor1.line - while new_cursor_line > 1 do - new_cursor_line = new_cursor_line-1 - if State.lines[new_cursor_line].mode == 'text' then ---? print('found previous text line') - State.cursor1 = {line=new_cursor_line, pos=nil} - Text.populate_screen_line_starting_pos(State, State.cursor1.line) - -- previous text line found, pick its final screen line ---? print('has multiple screen lines') - local screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos ---? print(#screen_line_starting_pos) - screen_line_starting_pos = screen_line_starting_pos[#screen_line_starting_pos] - local screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, screen_line_starting_pos) - local s = string.sub(State.lines[State.cursor1.line].data, screen_line_starting_byte_offset) - State.cursor1.pos = screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1 - break - end + if State.cursor1.line > 1 then + local new_cursor_line = State.cursor1.line-1 +--? print('found previous text line') + State.cursor1 = {line=new_cursor_line, pos=nil} + Text.populate_screen_line_starting_pos(State, State.cursor1.line) + -- previous text line found, pick its final screen line +--? print('has multiple screen lines') + local screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos +--? print(#screen_line_starting_pos) + screen_line_starting_pos = screen_line_starting_pos[#screen_line_starting_pos] + local screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, screen_line_starting_pos) + local s = string.sub(State.lines[State.cursor1.line].data, screen_line_starting_byte_offset) + State.cursor1.pos = screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1 end else -- move up one screen line in current line @@ -425,22 +407,15 @@ function Text.up(State) end function Text.down(State) - assert(State.lines[State.cursor1.line].mode == 'text') --? print('down', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, State.screen_bottom1.line, State.screen_bottom1.pos) if Text.cursor_at_final_screen_line(State) then -- line is done, skip to next text line --? print('cursor at final screen line of its line') - local new_cursor_line = State.cursor1.line - while new_cursor_line < #State.lines do - new_cursor_line = new_cursor_line+1 - if State.lines[new_cursor_line].mode == 'text' then - State.cursor1 = { - line = new_cursor_line, - pos = Text.nearest_cursor_pos(State.lines[new_cursor_line].data, State.cursor_x, State.left), - } ---? print(State.cursor1.pos) - break - end + if State.cursor1.line < #State.lines then + local new_cursor_line = State.cursor1.line+1 + State.cursor1.line = new_cursor_line + State.cursor1.pos = Text.nearest_cursor_pos(State.lines[State.cursor1.line].data, State.cursor_x, State.left) +--? print(State.cursor1.pos) end if State.cursor1.line > State.screen_bottom1.line then --? print('screen top before:', State.screen_top1.line, State.screen_top1.pos) @@ -542,21 +517,11 @@ function Text.match(s, pos, pat) end function Text.left(State) - assert(State.lines[State.cursor1.line].mode == 'text') if State.cursor1.pos > 1 then State.cursor1.pos = State.cursor1.pos-1 - else - local new_cursor_line = State.cursor1.line - while new_cursor_line > 1 do - new_cursor_line = new_cursor_line-1 - if State.lines[new_cursor_line].mode == 'text' then - State.cursor1 = { - line = new_cursor_line, - pos = utf8.len(State.lines[new_cursor_line].data) + 1, - } - break - end - end + elseif State.cursor1.line > 1 then + State.cursor1.line = State.cursor1.line-1 + State.cursor1.pos = utf8.len(State.lines[State.cursor1.line].data) + 1 end if Text.lt1(State.cursor1, State.screen_top1) then State.screen_top1 = { @@ -575,18 +540,11 @@ function Text.right(State) end function Text.right_without_scroll(State) - assert(State.lines[State.cursor1.line].mode == 'text') if State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) then State.cursor1.pos = State.cursor1.pos+1 - else - local new_cursor_line = State.cursor1.line - while new_cursor_line <= #State.lines-1 do - new_cursor_line = new_cursor_line+1 - if State.lines[new_cursor_line].mode == 'text' then - State.cursor1 = {line=new_cursor_line, pos=1} - break - end - end + elseif State.cursor1.line <= #State.lines-1 then + State.cursor1.line = State.cursor1.line+1 + State.cursor1.pos = 1 end end @@ -610,26 +568,7 @@ function Text.cursor_at_final_screen_line(State) end function Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State) - local y = State.top - while State.cursor1.line <= #State.lines do - if State.lines[State.cursor1.line].mode == 'text' then - break - end ---? print('cursor skips', State.cursor1.line) - y = y + Drawing_padding_height + Drawing.pixels(State.lines[State.cursor1.line].h, State.width) - State.cursor1.line = State.cursor1.line + 1 - end - if State.cursor1.pos == nil then - State.cursor1.pos = 1 - end - -- hack: insert a text line at bottom of file if necessary - if State.cursor1.line > #State.lines then - assert(State.cursor1.line == #State.lines+1) - table.insert(State.lines, {mode='text', data=''}) - table.insert(State.line_cache, {}) - end ---? print(y, App.screen.height, App.screen.height-State.line_height) - if y > App.screen.height - State.line_height then + if State.top > App.screen.height - State.line_height then --? print('scroll up') Text.snap_cursor_to_bottom_of_screen(State) end @@ -649,24 +588,11 @@ function Text.snap_cursor_to_bottom_of_screen(State) while true do --? print(y, 'top2:', top2.line, top2.screen_line, top2.screen_pos) if top2.line == 1 and top2.screen_line == 1 then break end - if top2.screen_line > 1 or State.lines[top2.line-1].mode == 'text' then - local h = State.line_height - if y - h < State.top then - break - end - y = y - h - else - assert(top2.line > 1) - assert(State.lines[top2.line-1].mode == 'drawing') - -- We currently can't draw partial drawings, so either skip it entirely - -- or not at all. - local h = Drawing_padding_height + Drawing.pixels(State.lines[top2.line-1].h, State.width) - if y - h < State.top then - break - end ---? print('skipping drawing of height', h) - y = y - h + local h = State.line_height + if y - h < State.top then + break end + y = y - h top2 = Text.previous_screen_line(State, top2) end --? print('top2 finally:', top2.line, top2.screen_line, top2.screen_pos) @@ -826,9 +752,6 @@ function Text.x(s, pos) end function Text.to2(State, loc1) - if State.lines[loc1.line].mode == 'drawing' then - return {line=loc1.line, screen_line=1, screen_pos=1} - end local result = {line=loc1.line} local line_cache = State.line_cache[loc1.line] Text.populate_screen_line_starting_pos(State, loc1.line) @@ -891,8 +814,6 @@ function Text.previous_screen_line(State, loc2) return {line=loc2.line, screen_line=loc2.screen_line-1, screen_pos=1} elseif loc2.line == 1 then return loc2 - elseif State.lines[loc2.line-1].mode == 'drawing' then - return {line=loc2.line-1, screen_line=1, screen_pos=1} else local l = State.lines[loc2.line-1] Text.populate_screen_line_starting_pos(State, loc2.line-1) diff --git a/text_tests.lua b/text_tests.lua index 8b646a4..c5d689d 100644 --- a/text_tests.lua +++ b/text_tests.lua @@ -13,32 +13,6 @@ function test_initial_state() check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos') end -function test_click_to_create_drawing() - 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_mouse_click(Editor_state, 8,Editor_state.top+8, 1) - -- cursor skips drawing to always remain on text - check_eq(#Editor_state.lines, 2, '#lines') - check_eq(Editor_state.cursor1.line, 2, 'cursor') -end - -function test_backspace_to_delete_drawing() - -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end) - App.screen.init{width=120, height=60} - Editor_state = edit.initialize_test_state() - Editor_state.lines = load_array{'```lines', '```', ''} - Text.redraw_all(Editor_state) - -- cursor is on text as always (outside tests this will get initialized correctly) - Editor_state.cursor1.line = 2 - -- backspacing deletes the drawing - edit.run_after_keychord(Editor_state, 'backspace') - check_eq(#Editor_state.lines, 1, '#lines') - check_eq(Editor_state.cursor1.line, 1, 'cursor') -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} @@ -923,35 +897,6 @@ function test_pagedown() App.screen.check(y, 'ghi', 'screen:2') end -function test_pagedown_skips_drawings() - -- some lines of text with a drawing intermixed - local drawing_width = 50 - App.screen.init{width=Editor_state.left+drawing_width, height=80} - Editor_state = edit.initialize_test_state() - Editor_state.lines = load_array{'abc', -- height 15 - '```lines', '```', -- height 25 - 'def', -- height 15 - 'ghi'} -- height 15 - Text.redraw_all(Editor_state) - check_eq(Editor_state.lines[2].mode, 'drawing', 'baseline/lines') - Editor_state.cursor1 = {line=1, pos=1} - Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} - local drawing_height = Drawing_padding_height + drawing_width/2 -- default - -- initially the screen displays the first line and the drawing - -- 15px margin + 15px line1 + 10px margin + 25px drawing + 10px margin = 75px < screen height 80px - edit.draw(Editor_state) - local y = Editor_state.top - App.screen.check(y, 'abc', 'baseline/screen:1') - -- after pagedown the screen draws the drawing up top - -- 15px margin + 10px margin + 25px drawing + 10px margin + 15px line3 = 75px < screen height 80px - edit.run_after_keychord(Editor_state, 'pagedown') - check_eq(Editor_state.screen_top1.line, 2, 'screen_top') - check_eq(Editor_state.cursor1.line, 3, 'cursor') - y = Editor_state.top + drawing_height - App.screen.check(y, 'def', 'screen:1') -end - function test_pagedown_often_shows_start_of_wrapping_line() -- draw a few lines ending in part of a wrapping line App.screen.init{width=50, height=60} @@ -1055,31 +1000,6 @@ function test_down_arrow_moves_cursor() App.screen.check(y, 'ghi', 'screen:3') end -function test_down_arrow_skips_drawing() - -- some lines of text with a drawing intermixed - local drawing_width = 50 - App.screen.init{width=Editor_state.left+drawing_width, height=100} - Editor_state = edit.initialize_test_state() - Editor_state.lines = load_array{'abc', -- height 15 - '```lines', '```', -- height 25 - 'ghi'} - Text.redraw_all(Editor_state) - Editor_state.cursor1 = {line=1, pos=1} - Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} - edit.draw(Editor_state) - local y = Editor_state.top - App.screen.check(y, 'abc', 'baseline/screen:1') - y = y + Editor_state.line_height - local drawing_height = Drawing_padding_height + drawing_width/2 -- default - y = y + drawing_height - App.screen.check(y, 'ghi', 'baseline/screen:3') - check(Editor_state.cursor_x, 'baseline/cursor_x') - -- after hitting the down arrow the cursor moves down by 2 lines, skipping the drawing - edit.run_after_keychord(Editor_state, 'down') - check_eq(Editor_state.cursor1.line, 3, 'cursor') -end - function test_down_arrow_scrolls_down_by_one_line() -- display the first three lines with the cursor on the bottom line App.screen.init{width=120, height=60} @@ -1228,31 +1148,6 @@ function test_up_arrow_moves_cursor() App.screen.check(y, 'ghi', 'screen:3') end -function test_up_arrow_skips_drawing() - -- some lines of text with a drawing intermixed - local drawing_width = 50 - App.screen.init{width=Editor_state.left+drawing_width, height=100} - Editor_state = edit.initialize_test_state() - Editor_state.lines = load_array{'abc', -- height 15 - '```lines', '```', -- height 25 - 'ghi'} - Text.redraw_all(Editor_state) - Editor_state.cursor1 = {line=3, pos=1} - Editor_state.screen_top1 = {line=1, pos=1} - Editor_state.screen_bottom1 = {} - edit.draw(Editor_state) - local y = Editor_state.top - App.screen.check(y, 'abc', 'baseline/screen:1') - y = y + Editor_state.line_height - local drawing_height = Drawing_padding_height + drawing_width/2 -- default - y = y + drawing_height - App.screen.check(y, 'ghi', 'baseline/screen:3') - check(Editor_state.cursor_x, 'baseline/cursor_x') - -- after hitting the up arrow the cursor moves up by 2 lines, skipping the drawing - edit.run_after_keychord(Editor_state, 'up') - check_eq(Editor_state.cursor1.line, 1, 'cursor') -end - function test_up_arrow_scrolls_up_by_one_line() -- display the lines 2/3/4 with the cursor on line 2 App.screen.init{width=120, height=60} @@ -1281,28 +1176,6 @@ function test_up_arrow_scrolls_up_by_one_line() App.screen.check(y, 'ghi', 'screen:3') end -function test_up_arrow_scrolls_up_by_one_line_skipping_drawing() - -- display lines 3/4/5 with a drawing just off screen at line 2 - App.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.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 the up arrow the screen scrolls up to previous text line - edit.run_after_keychord(Editor_state, 'up') - check_eq(Editor_state.screen_top1.line, 1, 'screen_top') - check_eq(Editor_state.cursor1.line, 1, 'cursor') -end - function test_up_arrow_scrolls_up_by_one_screen_line() -- display lines starting from second screen line of a line App.screen.init{width=Editor_state.left+30, height=60} @@ -1985,7 +1858,7 @@ end function test_search() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() - Editor_state.lines = load_array{'```lines', '```', 'def', 'ghi', '’deg'} -- contains unicode quote in final line + Editor_state.lines = load_array{'abc', 'def', 'ghi', '’deg'} -- contains unicode quote in final line Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} diff --git a/undo.lua b/undo.lua index d3d5f0f..873feea 100644 --- a/undo.lua +++ b/undo.lua @@ -50,8 +50,6 @@ function snapshot(State, s,e) screen_top=deepcopy(State.screen_top1), selection=deepcopy(State.selection1), cursor=deepcopy(State.cursor1), - current_drawing_mode=Drawing_mode, - previous_drawing_mode=State.previous_drawing_mode, lines={}, start_line=s, end_line=e, @@ -60,14 +58,7 @@ function snapshot(State, s,e) -- deep copy lines without cached stuff like text fragments for i=s,e do local line = State.lines[i] - if line.mode == 'text' then - table.insert(event.lines, {mode='text', data=line.data}) -- I've forgotten: should we deepcopy(line.data)? - elseif line.mode == 'drawing' then - table.insert(event.lines, {mode='drawing', h=line.h, points=deepcopy(line.points), shapes=deepcopy(line.shapes), pending={}}) - else - print(line.mode) - assert(false) - end + table.insert(event.lines, {data=line.data}) -- I've forgotten: should we deepcopy(line.data)? end return event end |