about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Manual_tests.md2
-rw-r--r--README.md100
-rw-r--r--edit.lua253
-rw-r--r--file.lua134
-rw-r--r--main.lua12
-rw-r--r--run.lua4
-rw-r--r--select.lua10
-rw-r--r--source.lua4
-rw-r--r--text.lua152
-rw-r--r--text_tests.lua129
-rw-r--r--undo.lua11
11 files changed, 144 insertions, 667 deletions
diff --git a/Manual_tests.md b/Manual_tests.md
index 8219c14..5e702fa 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 13b6e76..5b3cfc6 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 a directory
+By default, it reads/writes the file `lines.txt` in a directory
 specific 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
 
@@ -36,12 +37,7 @@ While editing text:
 * mouse drag or `shift` + movement to select text, `ctrl+a` to select all
 * `ctrl+e` to modify the sources
 
-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
 
@@ -52,71 +48,47 @@ 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.
-
 * Long wrapping lines can't yet distinguish between the cursor at end of one
   screen line and start of the next, so clicking the mouse to position the
   cursor can very occasionally do the wrong thing.
 
-* 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 13f2d6a..4e8425e 100644
--- a/main.lua
+++ b/main.lua
@@ -24,13 +24,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')
@@ -65,6 +58,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/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 81eb544..594ff4a 100644
--- a/source.lua
+++ b/source.lua
@@ -73,7 +73,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')
 
 
 
@@ -224,7 +224,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 1f247e1..9abc6fe 100644
--- a/text.lua
+++ b/text.lua
@@ -75,7 +75,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
@@ -125,7 +124,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)
@@ -178,16 +176,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
@@ -229,10 +222,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
@@ -326,7 +317,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)
@@ -342,11 +333,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)
@@ -380,29 +367,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
@@ -423,22 +405,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)
@@ -540,21 +515,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 = {
@@ -573,18 +538,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
 
@@ -608,23 +566,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
-  -- 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
@@ -644,24 +586,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)
@@ -821,9 +750,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)
@@ -886,8 +812,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 cfdffdd..516e377 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'}
+  Editor_state.lines = load_array{'abc', 'def', 'ghi', 'deg'}
   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