Merge text.love

[?]
Nov 4, 2022, 4:46 AM
RXNR3U5EC64CNAS7WXGTJBUCOMFCYS5TMBM4BG7D7XOPVAJPDY3AC

Dependencies

  • [2] 36Z442IV back to commit 8123959e52f without code editing
  • [3] VP5KC4XZ Merge lines.love
  • [4] JCSLDGAH beginnings of support for multiple shapes
  • [5] KKMFQDR4 editing source code from within the app
  • [6] OTIBCAUJ love2d scaffold
  • [7] GUOQRUL7 Merge lines.love
  • [8] LXTTOB33 extract a couple of files
  • [9] EMHRPJ3R no, that's not right
  • [10] XX7G2FFJ intermingle freehand line drawings with text
  • [11] KMSL74GA support selections in the source editor
  • [12] BULPIBEG beginnings of a module for the text editor
  • [13] 4AXV2HG4 all pending manual tests done!
  • [14] 7CLGG7J2 test: autosave after any shape
  • [15] YT5P6TO6 bugfix: save previous file when dropping a new one on
  • [16] RSZD5A7G forgot to add json.lua
  • [17] 32V6ZHQB Merge lines.love
  • [18] VXORMHME delete experimental REPL
  • [19] D4B52CQ2 Merge lines.love
  • [20] NEXUNNCF extract a function
  • [21] 2CTN2IEF Merge lines.love
  • [22] APX2PY6G stop tracking wallclock time
  • [23] FS2ITYYH record a known issue
  • [24] R5QXEHUI somebody stop me
  • [25] MD3W5IRA new fork: rip out drawing support
  • [26] IFGAJAF7 add a level of indirection to vertices of shapes
  • [27] BJ5X5O4A let's prevent the text cursor from ever getting on a drawing
  • [28] CIQN2MDE bugfix: typing a capital letter deletes selection
  • [29] LNUHQOGH start passing in Editor_state explicitly
  • [30] 7DYUAOI6 test: undo moving point
  • [31] 6LJZN727 handle chords
  • [32] 3QNOKBFM beginnings of a test harness
  • [33] Y4VYNEGF test: autosave after name/move/delete of point
  • [34] D2GCFTTT clean up repl functionality
  • [35] ETXNVRPT Merge lines.love
  • [36] VBU5YHLR Merge lines.love
  • [37] VHUNJHXB Merge lines.love
  • [38] BLWAYPKV extract a module
  • [39] FYS7TCDW bugfix
  • [40] Z4KNS42N to open a file without a terminal, drag it on!
  • [41] EF6MFB46 assume we always have a filename
  • [42] AJB4LFRB try to maintain a reasonable line width
  • [43] K2X6G75Z start writing some tests for drawings
  • [44] TXDMRA5J bugfix: alt-tab shouldn't emit keypress events
  • [45] VHQCNMAR several more modules
  • [46] 42LVB4DE test: naming a point
  • [47] AVTNUQYR basic test-enabled framework
  • [48] TLOAPLBJ add a license
  • [49] JOPVPUSA editing source code from within the app
  • [50] ATQO62TF Merge lines.love
  • [51] 2L5MEZV3 experiment: new edit namespace
  • [52] AYX33NBC Merge lines.love
  • [53] T7SJSJIH test: undo naming a point
  • [54] T4FRZSYL delete an ancient, unused file
  • [55] KAUD3YIK tests: deleting points/shapes
  • [56] OGUV4HSA remove some memory leaks from rendered fragments
  • [57] PX7DDEMO autosave slightly less aggressively
  • [58] 6DE7RBZ6 move mouse_released events to Drawing
  • [59] 66X36NZN a little more prose describing manual_tests
  • [60] TVCPXAAU rename
  • [61] 3PSFWAIL Merge lines.love
  • [62] 73OCE2MC after much struggle, a brute-force undo
  • [63] BYG5CEMV support for naming points
  • [64] L6XA5EY2 test: moving a point
  • [65] 4YDBYBA4 clean up memory leak experiments
  • [66] CE4LZV4T drop last couple of manual tests
  • [67] TO6Y2G3U more decoupling editor tests from App

Change contents

  • file deletion: drawing_tests.lua (----------)drawing_tests.lua (----------)
    [4.2][4.1534:1575](),[4.1575][4.15:15](),[4.2][4.1534:1575]()
    Current_time = Current_time + 3.1
    edit.update(Editor_state, 0)
    -- undo is saved
    load_from_disk(Editor_state)
    Text.redraw_all(Editor_state)
    check_eq(#Editor_state.lines[1].shapes, 2, 'F - test_undo_delete_point/save')
    end
    edit.update(Editor_state, 0)
    Current_time = Current_time + 3.1
    edit.update(Editor_state, 0)
    -- undo is saved
    load_from_disk(Editor_state)
    Text.redraw_all(Editor_state)
    local p2 = Editor_state.lines[1].points[drawing.shapes[1].p2]
    check_eq(p2.x, 35, 'F - test_undo_move_point/save/x')
    check_eq(p2.y, 36, 'F - test_undo_move_point/save/y')
    end
    function test_undo_delete_point()
    io.write('\ntest_undo_delete_point')
    -- create a drawing with two lines connected at a point
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.filename = 'foo'
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+55, Editor_state.top+Drawing_padding_top+26, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 2, 'F - test_undo_delete_point/baseline/#shapes')
    check_eq(drawing.shapes[1].mode, 'line', 'F - test_undo_delete_point/baseline/shape:1')
    check_eq(drawing.shapes[2].mode, 'line', 'F - test_undo_delete_point/baseline/shape:2')
    -- hover on the common point and delete
    App.mouse_move(Editor_state.left+35, Editor_state.top+Drawing_padding_top+36)
    edit.run_after_keychord(Editor_state, 'C-d')
    check_eq(drawing.shapes[1].mode, 'deleted', 'F - test_undo_delete_point/shape:1')
    check_eq(drawing.shapes[2].mode, 'deleted', 'F - test_undo_delete_point/shape:2')
    -- undo
    edit.run_after_keychord(Editor_state, 'C-z')
    local drawing = Editor_state.lines[1]
    local p2 = drawing.points[drawing.shapes[1].p2]
    check_eq(Editor_state.next_history, 3, 'F - test_undo_move_point/next_history')
    check_eq(drawing.shapes[1].mode, 'line', 'F - test_undo_delete_point/shape:1')
    check_eq(drawing.shapes[2].mode, 'line', 'F - test_undo_delete_point/shape:2')
    -- wait until save
    edit.update(Editor_state, 0)
    Current_time = Current_time + 3.1
    edit.update(Editor_state, 0)
    -- undo is saved
    load_from_disk(Editor_state)
    Text.redraw_all(Editor_state)
    local p2 = Editor_state.lines[1].points[drawing.shapes[1].p2]
    check_eq(p2.name, '', 'F - test_undo_name_point/save')
    end
    function test_undo_move_point()
    io.write('\ntest_undo_move_point')
    -- create a drawing with a line
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.filename = 'foo'
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_undo_move_point/baseline/#shapes')
    check_eq(#drawing.points, 2, 'F - test_undo_move_point/baseline/#points')
    check_eq(drawing.shapes[1].mode, 'line', 'F - test_undo_move_point/baseline/shape:1')
    local p1 = drawing.points[drawing.shapes[1].p1]
    local p2 = drawing.points[drawing.shapes[1].p2]
    check_eq(p1.x, 5, 'F - test_undo_move_point/baseline/p1:x')
    check_eq(p1.y, 6, 'F - test_undo_move_point/baseline/p1:y')
    check_eq(p2.x, 35, 'F - test_undo_move_point/baseline/p2:x')
    check_eq(p2.y, 36, 'F - test_undo_move_point/baseline/p2:y')
    check_nil(p2.name, 'F - test_undo_move_point/baseline/p2:name')
    -- move p2
    edit.run_after_keychord(Editor_state, 'C-u')
    App.mouse_move(Editor_state.left+26, Editor_state.top+Drawing_padding_top+44)
    edit.update(Editor_state, 0.05)
    local p2 = drawing.points[drawing.shapes[1].p2]
    check_eq(p2.x, 26, 'F - test_undo_move_point/x')
    check_eq(p2.y, 44, 'F - test_undo_move_point/y')
    -- exit 'move' mode
    edit.run_after_mouse_click(Editor_state, Editor_state.left+26, Editor_state.top+Drawing_padding_top+44, 1)
    check_eq(Editor_state.next_history, 4, 'F - test_undo_move_point/next_history')
    -- undo
    edit.run_after_keychord(Editor_state, 'C-z')
    edit.run_after_keychord(Editor_state, 'C-z') -- bug: need to undo twice
    local drawing = Editor_state.lines[1]
    local p2 = drawing.points[drawing.shapes[1].p2]
    check_eq(Editor_state.next_history, 2, 'F - test_undo_move_point/next_history')
    check_eq(p2.x, 35, 'F - test_undo_move_point/x')
    check_eq(p2.y, 36, 'F - test_undo_move_point/y')
    -- wait until save
    edit.update(Editor_state, 0)
    Current_time = Current_time + 3.1
    edit.update(Editor_state, 0)
    -- deleted points disappear after file is reloaded
    load_from_disk(Editor_state)
    Text.redraw_all(Editor_state)
    check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_delete_lines_at_point/save')
    end
    function test_delete_line_under_mouse_pointer()
    io.write('\ntest_delete_line_under_mouse_pointer')
    -- create a drawing with two lines connected at a point
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+55, Editor_state.top+Drawing_padding_top+26, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 2, 'F - test_delete_line_under_mouse_pointer/baseline/#shapes')
    check_eq(drawing.shapes[1].mode, 'line', 'F - test_delete_line_under_mouse_pointer/baseline/shape:1')
    check_eq(drawing.shapes[2].mode, 'line', 'F - test_delete_line_under_mouse_pointer/baseline/shape:2')
    -- hover on one of the lines and delete
    App.mouse_move(Editor_state.left+25, Editor_state.top+Drawing_padding_top+26)
    edit.run_after_keychord(Editor_state, 'C-d')
    -- only that line is deleted
    check_eq(drawing.shapes[1].mode, 'deleted', 'F - test_delete_line_under_mouse_pointer/shape:1')
    check_eq(drawing.shapes[2].mode, 'line', 'F - test_delete_line_under_mouse_pointer/shape:2')
    end
    function test_delete_point_from_polygon()
    io.write('\ntest_delete_point_from_polygon')
    -- create a drawing with two lines connected at a point
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    -- first point
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    edit.run_after_keychord(Editor_state, 'g') -- polygon mode
    -- second point
    App.mouse_move(Editor_state.left+65, Editor_state.top+Drawing_padding_top+36)
    edit.run_after_keychord(Editor_state, 'p') -- add point
    -- third point
    App.mouse_move(Editor_state.left+35, Editor_state.top+Drawing_padding_top+26)
    edit.run_after_keychord(Editor_state, 'p') -- add point
    -- fourth point
    edit.run_after_mouse_release(Editor_state, Editor_state.left+14, Editor_state.top+Drawing_padding_top+16, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_delete_point_from_polygon/baseline/#shapes')
    check_eq(drawing.shapes[1].mode, 'polygon', 'F - test_delete_point_from_polygon/baseline/mode')
    check_eq(#drawing.shapes[1].vertices, 4, 'F - test_delete_point_from_polygon/baseline/vertices')
    -- hover on a point and delete
    App.mouse_move(Editor_state.left+35, Editor_state.top+Drawing_padding_top+26)
    edit.run_after_keychord(Editor_state, 'C-d')
    -- just the one point is deleted
    check_eq(drawing.shapes[1].mode, 'polygon', 'F - test_delete_point_from_polygon/shape')
    check_eq(#drawing.shapes[1].vertices, 3, 'F - test_delete_point_from_polygon/vertices')
    end
    function test_delete_point_from_polygon()
    io.write('\ntest_delete_point_from_polygon')
    -- create a drawing with two lines connected at a point
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    -- first point
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    edit.run_after_keychord(Editor_state, 'g') -- polygon mode
    -- second point
    App.mouse_move(Editor_state.left+65, Editor_state.top+Drawing_padding_top+36)
    edit.run_after_keychord(Editor_state, 'p') -- add point
    -- third point
    edit.run_after_mouse_release(Editor_state, Editor_state.left+14, Editor_state.top+Drawing_padding_top+16, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_delete_point_from_polygon/baseline/#shapes')
    check_eq(drawing.shapes[1].mode, 'polygon', 'F - test_delete_point_from_polygon/baseline/mode')
    check_eq(#drawing.shapes[1].vertices, 3, 'F - test_delete_point_from_polygon/baseline/vertices')
    -- hover on a point and delete
    App.mouse_move(Editor_state.left+65, Editor_state.top+Drawing_padding_top+36)
    edit.run_after_keychord(Editor_state, 'C-d')
    -- there's < 3 points left, so the whole polygon is deleted
    check_eq(drawing.shapes[1].mode, 'deleted', 'F - test_delete_point_from_polygon')
    end
    function test_undo_name_point()
    io.write('\ntest_undo_name_point')
    -- create a drawing with a line
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.filename = 'foo'
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    -- draw a line
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_undo_name_point/baseline/#shapes')
    check_eq(#drawing.points, 2, 'F - test_undo_name_point/baseline/#points')
    check_eq(drawing.shapes[1].mode, 'line', 'F - test_undo_name_point/baseline/shape:1')
    local p1 = drawing.points[drawing.shapes[1].p1]
    local p2 = drawing.points[drawing.shapes[1].p2]
    check_eq(p1.x, 5, 'F - test_undo_name_point/baseline/p1:x')
    check_eq(p1.y, 6, 'F - test_undo_name_point/baseline/p1:y')
    check_eq(p2.x, 35, 'F - test_undo_name_point/baseline/p2:x')
    check_eq(p2.y, 36, 'F - test_undo_name_point/baseline/p2:y')
    check_nil(p2.name, 'F - test_undo_name_point/baseline/p2:name')
    check_eq(#Editor_state.history, 1, 'F - test_undo_name_point/baseline/history:1')
    --? print('a', Editor_state.lines.current_drawing)
    -- enter 'name' mode without moving the mouse
    edit.run_after_keychord(Editor_state, 'C-n')
    edit.run_after_textinput(Editor_state, 'A')
    edit.run_after_keychord(Editor_state, 'return')
    check_eq(p2.name, 'A', 'F - test_undo_name_point/baseline')
    check_eq(#Editor_state.history, 3, 'F - test_undo_name_point/baseline/history:2')
    check_eq(Editor_state.next_history, 4, 'F - test_undo_name_point/baseline/next_history')
    --? print('b', Editor_state.lines.current_drawing)
    -- undo
    edit.run_after_keychord(Editor_state, 'C-z')
    local drawing = Editor_state.lines[1]
    local p2 = drawing.points[drawing.shapes[1].p2]
    check_eq(Editor_state.next_history, 3, 'F - test_undo_name_point/next_history')
    check_eq(p2.name, '', 'F - test_undo_name_point') -- not quite what it was before, but close enough
    -- wait until save
    edit.update(Editor_state, 0)
    Current_time = Current_time + 3.1
    edit.update(Editor_state, 0)
    -- change is saved
    load_from_disk(Editor_state)
    Text.redraw_all(Editor_state)
    local p2 = Editor_state.lines[1].points[drawing.shapes[1].p2]
    check_eq(p2.x, 26, 'F - test_move_point/save/x')
    check_eq(p2.y, 44, 'F - test_move_point/save/y')
    end
    function test_move_point_on_manhattan_line()
    io.write('\ntest_move_point_on_manhattan_line')
    -- create a drawing with a manhattan line
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.filename = 'foo'
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'manhattan'
    edit.draw(Editor_state)
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+46, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_move_point_on_manhattan_line/baseline/#shapes')
    check_eq(#drawing.points, 2, 'F - test_move_point_on_manhattan_line/baseline/#points')
    check_eq(drawing.shapes[1].mode, 'manhattan', 'F - test_move_point_on_manhattan_line/baseline/shape:1')
    edit.draw(Editor_state)
    -- enter 'move' mode
    edit.run_after_keychord(Editor_state, 'C-u')
    check_eq(Editor_state.current_drawing_mode, 'move', 'F - test_move_point_on_manhattan_line/mode:1')
    -- move point
    App.mouse_move(Editor_state.left+26, Editor_state.top+Drawing_padding_top+44)
    edit.update(Editor_state, 0.05)
    -- line is no longer manhattan
    check_eq(drawing.shapes[1].mode, 'line', 'F - test_move_point_on_manhattan_line/baseline/shape:1')
    end
    function test_delete_lines_at_point()
    io.write('\ntest_delete_lines_at_point')
    -- create a drawing with two lines connected at a point
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.filename = 'foo'
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+55, Editor_state.top+Drawing_padding_top+26, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 2, 'F - test_delete_lines_at_point/baseline/#shapes')
    check_eq(drawing.shapes[1].mode, 'line', 'F - test_delete_lines_at_point/baseline/shape:1')
    check_eq(drawing.shapes[2].mode, 'line', 'F - test_delete_lines_at_point/baseline/shape:2')
    -- hover on the common point and delete
    App.mouse_move(Editor_state.left+35, Editor_state.top+Drawing_padding_top+36)
    edit.run_after_keychord(Editor_state, 'C-d')
    check_eq(drawing.shapes[1].mode, 'deleted', 'F - test_delete_lines_at_point/shape:1')
    check_eq(drawing.shapes[2].mode, 'deleted', 'F - test_delete_lines_at_point/shape:2')
    -- wait for some time
    edit.update(Editor_state, 0)
    Current_time = Current_time + 3.1
    edit.update(Editor_state, 0)
    -- line is saved to disk
    load_from_disk(Editor_state)
    Text.redraw_all(Editor_state)
    local drawing = Editor_state.lines[1]
    local p2 = Editor_state.lines[1].points[drawing.shapes[1].p2]
    check_eq(p2.x, 35, 'F - test_move_point/save/x')
    check_eq(p2.y, 36, 'F - test_move_point/save/y')
    edit.draw(Editor_state)
    -- enter 'move' mode without moving the mouse
    edit.run_after_keychord(Editor_state, 'C-u')
    check_eq(Editor_state.current_drawing_mode, 'move', 'F - test_move_point/mode:1')
    -- point is lifted
    check_eq(drawing.pending.mode, 'move', 'F - test_move_point/mode:2')
    check_eq(drawing.pending.target_point, p2, 'F - test_move_point/target')
    -- move point
    App.mouse_move(Editor_state.left+26, Editor_state.top+Drawing_padding_top+44)
    edit.update(Editor_state, 0.05)
    local p2 = drawing.points[drawing.shapes[1].p2]
    check_eq(p2.x, 26, 'F - test_move_point/x')
    check_eq(p2.y, 44, 'F - test_move_point/y')
    -- exit 'move' mode
    edit.run_after_mouse_click(Editor_state, Editor_state.left+26, Editor_state.top+Drawing_padding_top+44, 1)
    check_eq(Editor_state.current_drawing_mode, 'line', 'F - test_move_point/mode:3')
    check_eq(drawing.pending, {}, 'F - test_move_point/pending')
    -- wait until save
    edit.update(Editor_state, 0)
    Current_time = Current_time + 3.1
    edit.update(Editor_state, 0)
    -- change is saved
    load_from_disk(Editor_state)
    Text.redraw_all(Editor_state)
    local p2 = Editor_state.lines[1].points[drawing.shapes[1].p2]
    check_eq(p2.name, 'A', 'F - test_name_point/save')
    end
    function test_move_point()
    io.write('\ntest_move_point')
    -- create a drawing with a line
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.filename = 'foo'
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_move_point/baseline/#shapes')
    check_eq(#drawing.points, 2, 'F - test_move_point/baseline/#points')
    check_eq(drawing.shapes[1].mode, 'line', 'F - test_move_point/baseline/shape:1')
    local p1 = drawing.points[drawing.shapes[1].p1]
    local p2 = drawing.points[drawing.shapes[1].p2]
    check_eq(p1.x, 5, 'F - test_move_point/baseline/p1:x')
    check_eq(p1.y, 6, 'F - test_move_point/baseline/p1:y')
    check_eq(p2.x, 35, 'F - test_move_point/baseline/p2:x')
    check_eq(p2.y, 36, 'F - test_move_point/baseline/p2:y')
    -- wait until save
    edit.update(Editor_state, 0)
    Current_time = Current_time + 3.1
    edit.update(Editor_state, 0)
    -- The format on disk isn't perfectly stable. Table fields can be reordered.
    -- So just reload from disk to verify.
    load_from_disk(Editor_state)
    Text.redraw_all(Editor_state)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_draw_line/save/#shapes')
    check_eq(#drawing.points, 2, 'F - test_draw_line/save/#points')
    check_eq(drawing.shapes[1].mode, 'line', 'F - test_draw_line/save/shape:1')
    local p1 = drawing.points[drawing.shapes[1].p1]
    local p2 = drawing.points[drawing.shapes[1].p2]
    check_eq(p1.x, 5, 'F - test_draw_line/save/p1:x')
    check_eq(p1.y, 6, 'F - test_draw_line/save/p1:y')
    check_eq(p2.x, 35, 'F - test_draw_line/save/p2:x')
    check_eq(p2.y, 36, 'F - test_draw_line/save/p2:y')
    end
    function test_draw_horizontal_line()
    io.write('\ntest_draw_horizontal_line')
    -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'manhattan'
    edit.draw(Editor_state)
    check_eq(#Editor_state.lines, 2, 'F - test_draw_horizontal_line/baseline/#lines')
    check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_horizontal_line/baseline/mode')
    check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_horizontal_line/baseline/y')
    check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_horizontal_line/baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_horizontal_line/baseline/#shapes')
    -- draw a line that is more horizontal than vertical
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+26, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_draw_horizontal_line/#shapes')
    check_eq(#drawing.points, 2, 'F - test_draw_horizontal_line/#points')
    check_eq(drawing.shapes[1].mode, 'manhattan', 'F - test_draw_horizontal_line/shape_mode')
    local p1 = drawing.points[drawing.shapes[1].p1]
    local p2 = drawing.points[drawing.shapes[1].p2]
    check_eq(p1.x, 5, 'F - test_draw_horizontal_line/p1:x')
    check_eq(p1.y, 6, 'F - test_draw_horizontal_line/p1:y')
    check_eq(p2.x, 35, 'F - test_draw_horizontal_line/p2:x')
    check_eq(p2.y, p1.y, 'F - test_draw_horizontal_line/p2:y')
    end
    function test_draw_circle()
    io.write('\ntest_draw_circle')
    -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    check_eq(#Editor_state.lines, 2, 'F - test_draw_circle/baseline/#lines')
    check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_circle/baseline/mode')
    check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_circle/baseline/y')
    check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_circle/baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_circle/baseline/#shapes')
    -- draw a circle
    App.mouse_move(Editor_state.left+4, Editor_state.top+Drawing_padding_top+4) -- hover on drawing
    edit.run_after_keychord(Editor_state, 'C-o')
    edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35+30, Editor_state.top+Drawing_padding_top+36, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_draw_circle/#shapes')
    check_eq(#drawing.points, 1, 'F - test_draw_circle/#points')
    check_eq(drawing.shapes[1].mode, 'circle', 'F - test_draw_horizontal_line/shape_mode')
    check_eq(drawing.shapes[1].radius, 30, 'F - test_draw_circle/radius')
    local center = drawing.points[drawing.shapes[1].center]
    check_eq(center.x, 35, 'F - test_draw_circle/center:x')
    check_eq(center.y, 36, 'F - test_draw_circle/center:y')
    end
    function test_cancel_stroke()
    io.write('\ntest_cancel_stroke')
    -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.filename = 'foo'
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    check_eq(#Editor_state.lines, 2, 'F - test_cancel_stroke/baseline/#lines')
    check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_cancel_stroke/baseline/mode')
    check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_cancel_stroke/baseline/y')
    check_eq(Editor_state.lines[1].h, 128, 'F - test_cancel_stroke/baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_cancel_stroke/baseline/#shapes')
    -- start drawing a line
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    -- cancel
    edit.run_after_keychord(Editor_state, 'escape')
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 0, 'F - test_cancel_stroke/#shapes')
    end
    function test_keys_do_not_affect_shape_when_mouse_up()
    io.write('\ntest_keys_do_not_affect_shape_when_mouse_up')
    -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    -- hover over drawing and press 'o' without holding mouse
    App.mouse_move(Editor_state.left+4, Editor_state.top+Drawing_padding_top+4) -- hover on drawing
    edit.run_after_keychord(Editor_state, 'o')
    -- no change to drawing mode
    check_eq(Editor_state.current_drawing_mode, 'line', 'F - test_keys_do_not_affect_shape_when_mouse_up/drawing_mode')
    -- no change to text either because we didn't run the textinput event
    end
    function test_draw_circle_mid_stroke()
    io.write('\ntest_draw_circle_mid_stroke')
    -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    check_eq(#Editor_state.lines, 2, 'F - test_draw_circle_mid_stroke/baseline/#lines')
    check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_circle_mid_stroke/baseline/mode')
    check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_circle_mid_stroke/baseline/y')
    check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_circle_mid_stroke/baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_circle_mid_stroke/baseline/#shapes')
    -- draw a circle
    App.mouse_move(Editor_state.left+4, Editor_state.top+Drawing_padding_top+4) -- hover on drawing
    edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    edit.run_after_keychord(Editor_state, 'o')
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35+30, Editor_state.top+Drawing_padding_top+36, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_draw_circle_mid_stroke/#shapes')
    check_eq(#drawing.points, 1, 'F - test_draw_circle_mid_stroke/#points')
    check_eq(drawing.shapes[1].mode, 'circle', 'F - test_draw_horizontal_line/shape_mode')
    check_eq(drawing.shapes[1].radius, 30, 'F - test_draw_circle_mid_stroke/radius')
    local center = drawing.points[drawing.shapes[1].center]
    check_eq(center.x, 35, 'F - test_draw_circle_mid_stroke/center:x')
    check_eq(center.y, 36, 'F - test_draw_circle_mid_stroke/center:y')
    end
    function test_draw_arc()
    io.write('\ntest_draw_arc')
    -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'circle'
    edit.draw(Editor_state)
    check_eq(#Editor_state.lines, 2, 'F - test_draw_arc/baseline/#lines')
    check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_arc/baseline/mode')
    check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_arc/baseline/y')
    check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_arc/baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_arc/baseline/#shapes')
    -- draw an arc
    edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    App.mouse_move(Editor_state.left+35+30, Editor_state.top+Drawing_padding_top+36)
    edit.run_after_keychord(Editor_state, 'a') -- arc mode
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35+50, Editor_state.top+Drawing_padding_top+36+50, 1) -- 45°
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_draw_arc/#shapes')
    check_eq(#drawing.points, 1, 'F - test_draw_arc/#points')
    check_eq(drawing.shapes[1].mode, 'arc', 'F - test_draw_horizontal_line/shape_mode')
    local arc = drawing.shapes[1]
    check_eq(arc.radius, 30, 'F - test_draw_arc/radius')
    local center = drawing.points[arc.center]
    check_eq(center.x, 35, 'F - test_draw_arc/center:x')
    check_eq(center.y, 36, 'F - test_draw_arc/center:y')
    check_eq(arc.start_angle, 0, 'F - test_draw_arc/start:angle')
    check_eq(arc.end_angle, math.pi/4, 'F - test_draw_arc/end:angle')
    end
    function test_draw_polygon()
    io.write('\ntest_draw_polygon')
    -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    edit.draw(Editor_state)
    check_eq(Editor_state.current_drawing_mode, 'line', 'F - test_draw_polygon/baseline/drawing_mode')
    check_eq(#Editor_state.lines, 2, 'F - test_draw_polygon/baseline/#lines')
    check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_polygon/baseline/mode')
    check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_polygon/baseline/y')
    check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_polygon/baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_polygon/baseline/#shapes')
    -- first point
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    edit.run_after_keychord(Editor_state, 'g') -- polygon mode
    -- second point
    App.mouse_move(Editor_state.left+65, Editor_state.top+Drawing_padding_top+36)
    edit.run_after_keychord(Editor_state, 'p') -- add point
    -- final point
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+26, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_draw_polygon/#shapes')
    check_eq(#drawing.points, 3, 'F - test_draw_polygon/vertices')
    local shape = drawing.shapes[1]
    check_eq(shape.mode, 'polygon', 'F - test_draw_polygon/shape_mode')
    check_eq(#shape.vertices, 3, 'F - test_draw_polygon/vertices')
    local p = drawing.points[shape.vertices[1]]
    check_eq(p.x, 5, 'F - test_draw_polygon/p1:x')
    check_eq(p.y, 6, 'F - test_draw_polygon/p1:y')
    local p = drawing.points[shape.vertices[2]]
    check_eq(p.x, 65, 'F - test_draw_polygon/p2:x')
    check_eq(p.y, 36, 'F - test_draw_polygon/p2:y')
    local p = drawing.points[shape.vertices[3]]
    check_eq(p.x, 35, 'F - test_draw_polygon/p3:x')
    check_eq(p.y, 26, 'F - test_draw_polygon/p3:y')
    end
    function test_draw_rectangle()
    io.write('\ntest_draw_rectangle')
    -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    edit.draw(Editor_state)
    check_eq(Editor_state.current_drawing_mode, 'line', 'F - test_draw_rectangle/baseline/drawing_mode')
    check_eq(#Editor_state.lines, 2, 'F - test_draw_rectangle/baseline/#lines')
    check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_rectangle/baseline/mode')
    check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_rectangle/baseline/y')
    check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_rectangle/baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_rectangle/baseline/#shapes')
    -- first point
    edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    edit.run_after_keychord(Editor_state, 'r') -- rectangle mode
    -- second point/first edge
    App.mouse_move(Editor_state.left+42, Editor_state.top+Drawing_padding_top+45)
    edit.run_after_keychord(Editor_state, 'p')
    -- override second point/first edge
    App.mouse_move(Editor_state.left+75, Editor_state.top+Drawing_padding_top+76)
    edit.run_after_keychord(Editor_state, 'p')
    -- release (decides 'thickness' of rectangle perpendicular to first edge)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+15, Editor_state.top+Drawing_padding_top+26, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_draw_rectangle/#shapes')
    check_eq(#drawing.points, 5, 'F - test_draw_rectangle/#points') -- currently includes every point added
    local shape = drawing.shapes[1]
    check_eq(shape.mode, 'rectangle', 'F - test_draw_rectangle/shape_mode')
    check_eq(#shape.vertices, 4, 'F - test_draw_rectangle/vertices')
    local p = drawing.points[shape.vertices[1]]
    check_eq(p.x, 35, 'F - test_draw_rectangle/p1:x')
    check_eq(p.y, 36, 'F - test_draw_rectangle/p1:y')
    local p = drawing.points[shape.vertices[2]]
    check_eq(p.x, 75, 'F - test_draw_rectangle/p2:x')
    check_eq(p.y, 76, 'F - test_draw_rectangle/p2:y')
    local p = drawing.points[shape.vertices[3]]
    check_eq(p.x, 70, 'F - test_draw_rectangle/p3:x')
    check_eq(p.y, 81, 'F - test_draw_rectangle/p3:y')
    local p = drawing.points[shape.vertices[4]]
    check_eq(p.x, 30, 'F - test_draw_rectangle/p4:x')
    check_eq(p.y, 41, 'F - test_draw_rectangle/p4:y')
    end
    function test_draw_rectangle_intermediate()
    io.write('\ntest_draw_rectangle_intermediate')
    -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    edit.draw(Editor_state)
    check_eq(Editor_state.current_drawing_mode, 'line', 'F - test_draw_rectangle_intermediate/baseline/drawing_mode')
    check_eq(#Editor_state.lines, 2, 'F - test_draw_rectangle_intermediate/baseline/#lines')
    check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_rectangle_intermediate/baseline/mode')
    check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_rectangle_intermediate/baseline/y')
    check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_rectangle_intermediate/baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_rectangle_intermediate/baseline/#shapes')
    -- first point
    edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    edit.run_after_keychord(Editor_state, 'r') -- rectangle mode
    -- second point/first edge
    App.mouse_move(Editor_state.left+42, Editor_state.top+Drawing_padding_top+45)
    edit.run_after_keychord(Editor_state, 'p')
    -- override second point/first edge
    App.mouse_move(Editor_state.left+75, Editor_state.top+Drawing_padding_top+76)
    edit.run_after_keychord(Editor_state, 'p')
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.points, 3, 'F - test_draw_rectangle_intermediate/#points') -- currently includes every point added
    local pending = drawing.pending
    check_eq(pending.mode, 'rectangle', 'F - test_draw_rectangle_intermediate/shape_mode')
    check_eq(#pending.vertices, 2, 'F - test_draw_rectangle_intermediate/vertices')
    local p = drawing.points[pending.vertices[1]]
    check_eq(p.x, 35, 'F - test_draw_rectangle_intermediate/p1:x')
    check_eq(p.y, 36, 'F - test_draw_rectangle_intermediate/p1:y')
    local p = drawing.points[pending.vertices[2]]
    check_eq(p.x, 75, 'F - test_draw_rectangle_intermediate/p2:x')
    check_eq(p.y, 76, 'F - test_draw_rectangle_intermediate/p2:y')
    -- outline of rectangle is drawn based on where the mouse is, but we can't check that so far
    end
    function test_draw_square()
    io.write('\ntest_draw_square')
    -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    edit.draw(Editor_state)
    check_eq(Editor_state.current_drawing_mode, 'line', 'F - test_draw_square/baseline/drawing_mode')
    check_eq(#Editor_state.lines, 2, 'F - test_draw_square/baseline/#lines')
    check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_square/baseline/mode')
    check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_square/baseline/y')
    check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_square/baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_square/baseline/#shapes')
    -- first point
    edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    edit.run_after_keychord(Editor_state, 's') -- square mode
    -- second point/first edge
    App.mouse_move(Editor_state.left+42, Editor_state.top+Drawing_padding_top+45)
    edit.run_after_keychord(Editor_state, 'p')
    -- override second point/first edge
    App.mouse_move(Editor_state.left+65, Editor_state.top+Drawing_padding_top+66)
    edit.run_after_keychord(Editor_state, 'p')
    -- release (decides which side of first edge to draw square on)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+15, Editor_state.top+Drawing_padding_top+26, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_draw_square/#shapes')
    check_eq(#drawing.points, 5, 'F - test_draw_square/#points') -- currently includes every point added
    check_eq(drawing.shapes[1].mode, 'square', 'F - test_draw_square/shape_mode')
    check_eq(#drawing.shapes[1].vertices, 4, 'F - test_draw_square/vertices')
    local p = drawing.points[drawing.shapes[1].vertices[1]]
    check_eq(p.x, 35, 'F - test_draw_square/p1:x')
    check_eq(p.y, 36, 'F - test_draw_square/p1:y')
    local p = drawing.points[drawing.shapes[1].vertices[2]]
    check_eq(p.x, 65, 'F - test_draw_square/p2:x')
    check_eq(p.y, 66, 'F - test_draw_square/p2:y')
    local p = drawing.points[drawing.shapes[1].vertices[3]]
    check_eq(p.x, 35, 'F - test_draw_square/p3:x')
    check_eq(p.y, 96, 'F - test_draw_square/p3:y')
    local p = drawing.points[drawing.shapes[1].vertices[4]]
    check_eq(p.x, 5, 'F - test_draw_square/p4:x')
    check_eq(p.y, 66, 'F - test_draw_square/p4:y')
    end
    function test_name_point()
    io.write('\ntest_name_point')
    -- create a drawing with a line
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.filename = 'foo'
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    -- draw a line
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_name_point/baseline/#shapes')
    check_eq(#drawing.points, 2, 'F - test_name_point/baseline/#points')
    check_eq(drawing.shapes[1].mode, 'line', 'F - test_name_point/baseline/shape:1')
    local p1 = drawing.points[drawing.shapes[1].p1]
    local p2 = drawing.points[drawing.shapes[1].p2]
    check_eq(p1.x, 5, 'F - test_name_point/baseline/p1:x')
    check_eq(p1.y, 6, 'F - test_name_point/baseline/p1:y')
    check_eq(p2.x, 35, 'F - test_name_point/baseline/p2:x')
    check_eq(p2.y, 36, 'F - test_name_point/baseline/p2:y')
    check_nil(p2.name, 'F - test_name_point/baseline/p2:name')
    -- enter 'name' mode without moving the mouse
    edit.run_after_keychord(Editor_state, 'C-n')
    check_eq(Editor_state.current_drawing_mode, 'name', 'F - test_name_point/mode:1')
    edit.run_after_textinput(Editor_state, 'A')
    check_eq(p2.name, 'A', 'F - test_name_point')
    -- still in 'name' mode
    check_eq(Editor_state.current_drawing_mode, 'name', 'F - test_name_point/mode:2')
    -- exit 'name' mode
    edit.run_after_keychord(Editor_state, 'return')
    check_eq(Editor_state.current_drawing_mode, 'line', 'F - test_name_point/mode:3')
    check_eq(p2.name, 'A', 'F - test_name_point')
    -- wait until save
    edit.update(Editor_state, 0)
    Current_time = Current_time + 3.1
    edit.update(Editor_state, 0)
    -- filesystem contains drawing and an empty line of text
    check_eq(App.filesystem['foo'], '```lines\n```\n\n', 'F - test_creating_drawing_saves')
    end
    function test_draw_line()
    io.write('\ntest_draw_line')
    -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)
    App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixels
    Editor_state = edit.initialize_test_state()
    Editor_state.filename = 'foo'
    Editor_state.lines = load_array{'```lines', '```', ''}
    Text.redraw_all(Editor_state)
    Editor_state.current_drawing_mode = 'line'
    edit.draw(Editor_state)
    check_eq(#Editor_state.lines, 2, 'F - test_draw_line/baseline/#lines')
    check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_line/baseline/mode')
    check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_line/baseline/y')
    check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_line/baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_line/baseline/#shapes')
    -- draw a line
    edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)
    local drawing = Editor_state.lines[1]
    check_eq(#drawing.shapes, 1, 'F - test_draw_line/#shapes')
    check_eq(#drawing.points, 2, 'F - test_draw_line/#points')
    check_eq(drawing.shapes[1].mode, 'line', 'F - test_draw_line/shape:1')
    local p1 = drawing.points[drawing.shapes[1].p1]
    local p2 = drawing.points[drawing.shapes[1].p2]
    check_eq(p1.x, 5, 'F - test_draw_line/p1:x')
    check_eq(p1.y, 6, 'F - test_draw_line/p1:y')
    check_eq(p2.x, 35, 'F - test_draw_line/p2:x')
    check_eq(p2.y, 36, 'F - test_draw_line/p2:y')
    -- wait until save
    edit.update(Editor_state, 0)
    -- major tests for drawings
    -- We minimize assumptions about specific pixels, and try to test at the level
    -- of specific shapes. In particular, no tests of freehand drawings.
    function test_creating_drawing_saves()
    io.write('\ntest_creating_drawing_saves')
    App.screen.init{width=120, height=60}
    Editor_state = edit.initialize_test_state()
    Editor_state.filename = 'foo'
    Editor_state.lines = load_array{}
    Text.redraw_all(Editor_state)
    edit.draw(Editor_state)
    -- click on button to create drawing
    edit.run_after_mouse_click(Editor_state, 8,Editor_state.top+8, 1)
    -- file not immediately saved
    edit.update(Editor_state, 0.01)
    check_nil(App.filesystem['foo'], 'F - test_creating_drawing_saves/early')
    -- wait until save
  • file deletion: source_tests.lua (----------)source_tests.lua (----------)
    [4.2][4.150262:150302](),[4.2][4.150262:150302](),[4.150302][4.147166:147166]()
    Current_time = Current_time + 0.1
    App.run_after_keychord('C-l')
    -- margins are now adjusted
    check_eq(Editor_state.left, Margin_left, 'F - test_show_log_browser_side_resizes_both_sides_if_cannot_double_window_width/edit:left')
    check_eq(Editor_state.right, App.screen.width/2 - Margin_right, 'F - test_show_log_browser_side_resizes_both_sides_if_cannot_double_window_width/edit:right')
    check_eq(Log_browser_state.left, App.screen.width/2 + Margin_left, 'F - test_show_log_browser_side_resizes_both_sides_if_cannot_double_window_width/log:left')
    check_eq(Log_browser_state.right, App.screen.width - Margin_right, 'F - test_show_log_browser_side_resizes_both_sides_if_cannot_double_window_width/log:right')
    Current_time = Current_time + 0.1
    App.run_after_keychord('C-l')
    -- window width is doubled
    check_eq(App.screen.width, 600, 'F - test_show_log_browser_side_doubles_window_width_if_possible/display:width')
    -- left side margins are unchanged
    check_eq(Editor_state.left, Margin_left, 'F - test_show_log_browser_side_doubles_window_width_if_possible/edit:left')
    check_eq(Editor_state.right, old_editor_right, 'F - test_show_log_browser_side_doubles_window_width_if_possible/edit:right')
    -- log browser margins are adjusted
    check_eq(Log_browser_state.left, App.screen.width/2 + Margin_left, 'F - test_show_log_browser_side_doubles_window_width_if_possible/log:left')
    check_eq(Log_browser_state.right, App.screen.width - Margin_right, 'F - test_show_log_browser_side_doubles_window_width_if_possible/log:right')
    end
    function test_show_log_browser_side_resizes_both_sides_if_cannot_double_window_width()
    io.write('\ntest_show_log_browser_side_resizes_both_sides_if_cannot_double_window_width')
    -- initialize screen dimensions and indicate that it is maximized
    App.screen.init{width=300, height=300}
    Display_width = 300
    -- initialize source app with left side occupying more than half the display
    Current_app = 'source'
    Editor_state = edit.initialize_test_state()
    Editor_state.filename = 'foo'
    Editor_state.left = Margin_left
    Editor_state.right = 200
    Text.redraw_all(Editor_state)
    Log_browser_state = edit.initialize_test_state()
    -- log browser has some arbitrary margins
    Log_browser_state.left = 200 + Margin_left
    Log_browser_state.right = 400
    Text.redraw_all(Log_browser_state)
    log_browser.parse(Log_browser_state)
    -- display log browser
    Current_time = Current_time + 0.1
    App.run_after_keychord('C-l')
    check(Show_log_browser_side, 'F - test_show_log_browser_side')
    end
    function test_show_log_browser_side_doubles_window_width_if_possible()
    io.write('\ntest_show_log_browser_side_doubles_window_width_if_possible')
    -- initialize screen dimensions to half width
    App.screen.init{width=300, height=300}
    Display_width = App.screen.width*2
    -- initialize source app with left side occupying entire window (half the display)
    Current_app = 'source'
    Editor_state = edit.initialize_test_state()
    Editor_state.filename = 'foo'
    Editor_state.left = Margin_left
    Editor_state.right = App.screen.width - Margin_right
    local old_editor_right = Editor_state.right
    Text.redraw_all(Editor_state)
    Log_browser_state = edit.initialize_test_state()
    -- log browser has some arbitrary margins
    Log_browser_state.left = 200 + Margin_left
    Log_browser_state.right = 400
    Text.redraw_all(Log_browser_state)
    log_browser.parse(Log_browser_state)
    -- display log browser
  • file deletion: source_edit.lua (----------)source_edit.lua (----------)
    [4.2][4.165788:165827](),[4.2][4.165788:165827](),[4.165827][4.152503:152503]()
    State.next_save = Current_time + 3 -- short enough that you're likely to still remember what you did
    end
    end
    function edit.quit(State)
    -- make sure to save before quitting
    if State.next_save then
    save_to_disk(State)
    end
    end
    function edit.mouse_pressed(State, x,y, mouse_button)
    if State.search_term then return end
    if State.next_save and State.next_save < Current_time then
    save_to_disk(State)
    State.next_save = nil
    end
    end
    function schedule_save(State)
    if State.next_save == nil then
  • edit in main.lua at line 56
    [4.187634][4.415:449](),[4.187634][4.415:449]()
    Last_resize_time = Current_time
  • resolve order conflict in main.lua at line 56
    [2.2945]
    [4.40]
  • resurrect zombie in main.lua at line 61
    [4.187464][2.3020:3078](),[4.187464][2.3020:3078]()
    if rawget(_G, 'jit') then
    jit.off()
    jit.flush()
  • edit in main.lua at line 64
    [2.3078][4.44:44](),[4.187548][4.44:44](),[4.187512][3.416:416](),[4.187512][3.416:416](),[3.416][4.450:485](),[4.188051][4.450:485]()
    Last_focus_time = Current_time
  • resolve order conflict in main.lua at line 64
    [2.3078]
    [4.1113]
  • replacement in main.lua at line 117
    [2.5572][2.5572:5607]()
    Last_resize_time = App.getTime()
    [2.5572]
    [2.5607]
    Last_resize_time = Current_time
  • replacement in main.lua at line 178
    [2.7195][2.7195:7231]()
    Last_focus_time = App.getTime()
    [2.7195]
    [4.188666]
    Last_focus_time = Current_time
  • edit in main.lua at line 200
    [4.3121][4.189648:189685](),[4.3121][4.189648:189685]()
    function App.keyreleased(chord, key)