resolve conflicts

akkartik
Jun 11, 2024, 7:42 PM
4Y2QDDAZT2X7SEWYNFEMTYJC6O3UBK4ID3AIOAZZNE2S2H57UQSAC

Dependencies

  • [2] PTBOQDJ7 resolve stale conflicts
  • [3] IYXT7GFC resolve conflicts
  • [4] CRBLAWBO resolve conflicts
  • [5] RSZD5A7G forgot to add json.lua
  • [6] LWPFEZBI Merge lines.love
  • [7] FS2ITYYH record a known issue
  • [8] KYNGDE2C consistent names in a few more places
  • [9] 2CK5QI7W make love event names consistent
  • [10] ED4Z6ORC cleaner API for file-system access
  • [11] Q6RXCILQ Merge text.love
  • [12] ZLJYLPOT Merge lines.love
  • [13] Z5HLXU4P add state arg to a few functions
  • [14] NYQ7HD4D move
  • [15] RXNR3U5E Merge text.love
  • [16] QCPXQ2E3 add state arg to a few functions
  • [17] LYN3L74W correct commit f3abc2cbf2
  • [18] JCXL74WV bring back everything from commit a68647ae22
  • [19] MBAJPTDJ resolve conflicts
  • [20] TLOAPLBJ add a license
  • [21] LUNH47XX make text and drawings the same width
  • [22] DRFE3B3Z mouse buttons are integers, not strings
  • [23] P5QNVXSN drop final mention of state global beyond main.lua
  • [24] LXFHXL2N Merge text.love
  • [25] CE4LZV4T drop last couple of manual tests
  • [26] ZPUQSPQP extract a few methods
  • [27] OI4FPFIN support drawings in the source editor
  • [28] VBU5YHLR Merge lines.love
  • [29] PX7DDEMO autosave slightly less aggressively
  • [30] 3OKKTUT4 up and down arrow now moving by screen line where possible
  • [31] 3G723RV5 Merge text.love
  • [32] LXTTOB33 extract a couple of files
  • [33] TVCPXAAU rename
  • [34] MXA3RZYK deduce left/right from state where possible
  • [35] C3GUE45I Merge text.love
  • [36] OGUV4HSA remove some memory leaks from rendered fragments
  • [37] 5UKUADTW distinguish consistently between mouse buttons and other buttons
  • [38] X43ZIKR3 Merge text.love
  • [39] C45WCXJ2 keep drawings within the line width slider as well
  • [40] 5DOTWNVM right margin
  • [41] 2L5MEZV3 experiment: new edit namespace
  • [42] KMSL74GA support selections in the source editor
  • [43] 73OCE2MC after much struggle, a brute-force undo
  • [44] 5Y24ZDZI bugfix
  • [45] BJ5X5O4A let's prevent the text cursor from ever getting on a drawing
  • [46] VP5KC4XZ Merge lines.love
  • [47] SGMA5JLE save the list of tests in repo
  • [48] ORRSP7FV deduce test names on failures
  • [49] R3KXFRZN get rid of to_text
  • [50] KWIVKQQ7 Merge lines.love
  • [51] YXQOITYS Merge lines.love
  • [52] 32V6ZHQB Merge lines.love
  • [53] FNJF2FMQ bugfix: online help
  • [54] 2TQUKHBC Merge lines.love
  • [55] ECBDENZ4 Merge text.love
  • [56] A4BSGS2C Merge lines.love
  • [57] 3QNOKBFM beginnings of a test harness
  • [58] VXRYVZ74 Merge text.love
  • [59] 6LJZN727 handle chords
  • [60] 42LVB4DE test: naming a point
  • [61] VOU73AK6 Merge lines.love
  • [62] 34BZ5ZKN Merge lines.love
  • [63] KKQKPGCI resolve conflicts
  • [64] MD3W5IRA new fork: rip out drawing support
  • [65] D2GCFTTT clean up repl functionality
  • [66] DLQAEAC7 add state arg to Drawing.mouse_pressed
  • [67] PJ5PQAQE record support for multiple versions
  • [68] MU2HIRR6 Merge lines.love
  • [69] TOXPJJYY resolve conflicts
  • [70] T4FRZSYL delete an ancient, unused file
  • [71] M5JXTW56 Merge text.love
  • [72] UHB4GARJ left/right margin -> left/right coordinates
  • [73] 4YDBYBA4 clean up memory leak experiments
  • [74] VXORMHME delete experimental REPL
  • [75] TO6Y2G3U more decoupling editor tests from App
  • [76] JIK7ZRYI bugfix: imprecision in drawing
  • [77] QMRQL2FO resolve conflicts
  • [78] VLTU33KW resolve conflicts
  • [79] KJLZCK2R resolve conflicts
  • [80] KKMFQDR4 editing source code from within the app
  • [81] QD4LOFQR Merge text.love
  • [82] VTCPDL3A resolve conflicts
  • [83] ZS5IYZH5 stop caching screen_bottom1
  • [84] QXVD2RIF add state arg to Drawing.mouse_released
  • [85] CVSRHMJ2 experiment: slightly adaptive scrolling
  • [86] DLQMM265 scroll past first page
  • [87] 2CTN2IEF Merge lines.love
  • [88] AT3LVCMP Merge text.love
  • [89] BULPIBEG beginnings of a module for the text editor
  • [90] TBTRYEBP Merge lines.love
  • [91] REAIVN7W Merge lines.love
  • [92] OTIBCAUJ love2d scaffold
  • [93] CAG7PP5Y Merge text.love
  • [94] R6MNUXDJ pijul bug
  • [95] YY472LBU resolve conflicts
  • [96] KTZQ57HV replace globals with args in a few functions
  • [97] XX7G2FFJ intermingle freehand line drawings with text
  • [98] D4B52CQ2 Merge lines.love
  • [99] HYEAFRZ2 split mouse_pressed events between Text and Drawing
  • [100] 3PSFWAIL Merge lines.love
  • [101] VHQCNMAR several more modules
  • [102] BLWAYPKV extract a module
  • [103] 3OTESDW6 move drawing.starty into line cache
  • [104] 2TCIWW6Z stop caching starty
  • [105] VSJS6O4C Merge text.love
  • [106] M6TH7VSZ rip out notion of Line_width
  • [107] 6VJTQKW7 start supporting LÖVE v12
  • [108] LNUHQOGH start passing in Editor_state explicitly
  • [109] P4376EXK add state arg to few functions
  • [110] JOPVPUSA editing source code from within the app
  • [111] KZ5GAYRP this fixes the immediate regression
  • [112] VHUNJHXB Merge lines.love
  • [113] 36Z442IV back to commit 8123959e52f without code editing
  • [114] 2CFLXLIE Merge text.love
  • [115] UTDSCN3G Merge lines.love
  • [116] G3DLS5OU audit all asserts
  • [117] 66X36NZN a little more prose describing manual_tests
  • [118] 2JBAEQHU Merge lines.love
  • [119] AVTNUQYR basic test-enabled framework
  • [120] 4SR3Z4Y3 document the version of LÖVE I've been using
  • [121] N2NUGNN4 include a brief reference enabling many useful apps
  • [122] ONHKBLLC Merge lines.love
  • [123] K2X6G75Z start writing some tests for drawings
  • [124] NSM73TX3 crap, fix some final changes in the source editor
  • [125] O3WZWLYC Merge text.love
  • [126] LF7BWEG4 group all editor globals
  • [127] ORKN6EOB Merge lines.love
  • [128] VSGPLJFA resolve conflicts
  • [129] SCOXD4EO Merge lines.love
  • [130] 5ML5VGOC Merge text.love
  • [131] 5SM6DRHK port inscript's bugfix to source editor
  • [132] R5QXEHUI somebody stop me

Change contents

  • file deletion: drawing_tests.lua (----------)drawing_tests.lua (----------)
    [5.2][5.1534:1575](),[5.2][5.1534:1575](),[5.1575][5.15:15]()
    App.screen.init{width=800, height=600}
    check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
    check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
    check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
    check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
    check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
    check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
    check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
    check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
    check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
    check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
    check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
    check_eq(#drawing.shapes, 1, 'baseline/#shapes')
    check_eq(#drawing.points, 2, 'baseline/#points')
    check_eq(drawing.shapes[1].mode, 'line', 'baseline/shape:1')
    check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
    check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
    check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
    check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
    check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
    check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
    check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
    check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
    check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
    check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
    check_nil(App.filesystem['foo'], 'early')
    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)
  • file deletion: drawing.lua (----------)drawing.lua (----------)
    [5.2][5.1542:1577](),[5.2][5.1542:1577](),[5.1577][5.98:98]()
    local starty = Text.starty(State, line_index)
    if pmx < State.right and pmy > starty and pmy < starty+Drawing.pixels(line.h, State.width) then
    love.graphics.rectangle('line', State.left,starty, State.width,Drawing.pixels(line.h, State.width))
    icon[State.current_drawing_mode](State.right-22, starty+4)
    icon[State.previous_drawing_mode](State.right-22, starty+4)
    local my = Drawing.coord(pmy-starty, State.width)
    Drawing.draw_shape(line, shape, starty, State.left,State.right)
    local function py(y) return Drawing.pixels(y, State.width)+starty end
    Drawing.draw_pending_shape(line, starty, State.left,State.right)
    function Drawing.in_current_drawing(State, x,y, left,right)
    return Drawing.in_drawing(State, State.lines.current_drawing_index, x,y, left,right)
    end
    function Drawing.in_drawing(State, line_index, x,y, left,right)
    assert(State.lines[line_index].mode == 'drawing')
    local starty = Text.starty(State, line_index)
    if starty == nil then return false end -- outside current page
    local drawing = State.lines[line_index]
    local width = right-left
    return y >= starty and y < starty + Drawing.pixels(drawing.h, width) and x >= left and x < right
    end
    local starty = Text.starty(State, drawing_index)
    local cy = Drawing.coord(y-starty, State.width)
    local starty = Text.starty(State, State.lines.current_drawing_index)
    if starty == nil then
    local my = Drawing.coord(pmy-starty, State.width)
    if Drawing.in_current_drawing(State, pmx,pmy, State.left,State.right) then
    if Drawing.in_current_drawing(State, pmx, pmy, State.left,State.right) then
    local starty = Text.starty(State, State.lines.current_drawing_index)
    local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
    local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
    App.mouse_move(State.left+Drawing.pixels(p2.x, State.width), starty+Drawing.pixels(p2.y, State.width))
    local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
    local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
    local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
    local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
    local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
    local drawing_index,drawing = Drawing.current_drawing(State)
    local starty = Text.starty(State, drawing_index)
    local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-starty, State.width)
    local drawing_index,drawing = Drawing.current_drawing(State)
    local starty = Text.starty(State, drawing_index)
    local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-starty, State.width)
    local drawing_index,drawing = Drawing.current_drawing(State)
    local starty = Text.starty(State, drawing_index)
    local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-starty, State.width)
    local drawing_index,drawing,_,i,p = Drawing.select_point_at_mouse(State)
    local drawing_index,drawing,_,point_index,p = Drawing.select_point_at_mouse(State)
    if Drawing.in_drawing(State, drawing_index, x,y, State.left,State.right) then
    return drawing_index,drawing
    local starty = Text.starty(State, drawing_index)
    if Drawing.in_drawing(State, drawing_index, x,y, State.left,State.right) then
    local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
    return drawing,starty,i,shape
    local starty = Text.starty(State, drawing_index)
    if Drawing.in_drawing(State, drawing_index, x,y, State.left,State.right) then
    local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
    return drawing_index,drawing,starty,i,point
    if Drawing.in_drawing(State, drawing_index, x,y, State.left,State.right) then
    return drawing
    end
    end
    end
    end
    function Drawing.contains_point(shape, p)
    if shape.mode == 'freehand' then
    -- not supported
    elseif shape.mode == 'line' or shape.mode == 'manhattan' then
    return shape.p1 == p or shape.p2 == p
    elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
    return table.find(shape.vertices, p)
    elseif shape.mode == 'circle' then
    return shape.center == p
    elseif shape.mode == 'arc' then
    return shape.center == p
    -- ugh, how to support angles
    elseif shape.mode == 'deleted' then
    -- already done
    else
    end
    end
    end
    end
    end
    end
    function Drawing.select_drawing_at_mouse(State)
    for drawing_index,drawing in ipairs(State.lines) do
    if drawing.mode == 'drawing' then
    local x, y = App.mouse_x(), App.mouse_y()
    for i,point in ipairs(drawing.points) do
    end
    end
    end
    end
    end
    end
    function Drawing.select_point_at_mouse(State)
    for drawing_index,drawing in ipairs(State.lines) do
    if drawing.mode == 'drawing' then
    local x, y = App.mouse_x(), App.mouse_y()
    for i,shape in ipairs(drawing.shapes) do
    end
    end
    end
    return nil
    end
    function Drawing.select_shape_at_mouse(State)
    for drawing_index,drawing in ipairs(State.lines) do
    if drawing.mode == 'drawing' then
    local x, y = App.mouse_x(), App.mouse_y()
    if drawing then
    if State.previous_drawing_mode == nil then
    -- don't clobber
    State.previous_drawing_mode = State.current_drawing_mode
    end
    State.current_drawing_mode = 'name'
    p.name = ''
    drawing.pending = {mode=State.current_drawing_mode, target_point=point_index}
    State.lines.current_drawing_index = drawing_index
    State.lines.current_drawing = drawing
    end
    elseif chord == 'C-d' and not App.mouse_down(1) then
    local _,drawing,_,i,p = Drawing.select_point_at_mouse(State)
    if drawing then
    for _,shape in ipairs(drawing.shapes) do
    if Drawing.contains_point(shape, i) then
    if shape.mode == 'polygon' then
    local idx = table.find(shape.vertices, i)
    if drawing then
    if State.previous_drawing_mode == nil then
    State.previous_drawing_mode = State.current_drawing_mode
    end
    State.current_drawing_mode = 'move'
    drawing.pending = {mode=State.current_drawing_mode, target_point=p, target_point_index=i}
    State.lines.current_drawing_index = drawing_index
    State.lines.current_drawing = drawing
    end
    elseif chord == 'C-n' and not App.mouse_down(1) then
    local center = drawing.points[drawing.pending.center]
    drawing.pending.radius = round(geom.dist(center.x,center.y, mx,my))
    drawing.pending.start_angle = geom.angle(center.x,center.y, mx,my)
    elseif App.mouse_down(1) and chord == 'o' then
    State.current_drawing_mode = 'circle'
    local _,drawing = Drawing.current_drawing(State)
    if drawing.pending.mode == 'freehand' then
    drawing.pending.center = Drawing.find_or_insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y, State.width)
    elseif drawing.pending.mode == 'line' or drawing.pending.mode == 'manhattan' then
    drawing.pending.center = drawing.pending.p1
    elseif drawing.pending.mode == 'polygon' or drawing.pending.mode == 'rectangle' or drawing.pending.mode == 'square' then
    drawing.pending.center = drawing.pending.vertices[1]
    end
    drawing.pending.mode = 'circle'
    elseif chord == 'C-u' and not App.mouse_down(1) then
    drawing.pending.mode = 'arc'
    local j = Drawing.find_or_insert_point(drawing.points, mx,my, State.width)
    while #drawing.pending.vertices >= 2 do
    table.remove(drawing.pending.vertices)
    end
    table.insert(drawing.pending.vertices, j)
    elseif chord == 'C-o' and not App.mouse_down(1) then
    State.current_drawing_mode = 'circle'
    elseif App.mouse_down(1) and chord == 'a' and State.current_drawing_mode == 'circle' then
    local j = Drawing.find_or_insert_point(drawing.points, mx,my, State.width)
    table.insert(drawing.pending.vertices, j)
    elseif App.mouse_down(1) and chord == 'p' and (State.current_drawing_mode == 'rectangle' or State.current_drawing_mode == 'square') then
    function Drawing.keychord_press(State, chord)
    if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
    local center = drawing.points[drawing.pending.center]
    drawing.pending.end_angle = geom.angle_with_hint(center.x,center.y, mx,my, drawing.pending.end_angle)
    table.insert(drawing.shapes, drawing.pending)
    end
    elseif drawing.pending.mode == 'name' then
    -- drop it
    else
    if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
    local center = drawing.points[drawing.pending.center]
    drawing.pending.radius = round(geom.dist(center.x,center.y, mx,my))
    table.insert(drawing.shapes, drawing.pending)
    end
    elseif drawing.pending.mode == 'arc' then
    if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
    local first = drawing.points[drawing.pending.vertices[1]]
    local second = drawing.points[drawing.pending.vertices[2]]
    local thirdx,thirdy, fourthx,fourthy = Drawing.complete_square(first.x,first.y, second.x,second.y, mx,my)
    table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, thirdx,thirdy, State.width))
    table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, fourthx,fourthy, State.width))
    table.insert(drawing.shapes, drawing.pending)
    end
    end
    elseif drawing.pending.mode == 'circle' then
    if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
    local first = drawing.points[drawing.pending.vertices[1]]
    local second = drawing.points[drawing.pending.vertices[2]]
    local thirdx,thirdy, fourthx,fourthy = Drawing.complete_rectangle(first.x,first.y, second.x,second.y, mx,my)
    table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, thirdx,thirdy, State.width))
    table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, fourthx,fourthy, State.width))
    table.insert(drawing.shapes, drawing.pending)
    end
    else
    -- too few points; draw nothing
    end
    elseif drawing.pending.mode == 'square' then
    if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
    table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, mx,my, State.width))
    table.insert(drawing.shapes, drawing.pending)
    end
    elseif drawing.pending.mode == 'rectangle' then
    table.insert(drawing.shapes, drawing.pending)
    end
    elseif drawing.pending.mode == 'polygon' then
    if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
    if math.abs(mx-p1.x) > math.abs(my-p1.y) then
    drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, mx, p1.y, State.width)
    else
    drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, p1.x, my, State.width)
    end
    local p2 = drawing.points[drawing.pending.p2]
    if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
    drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, mx,my, State.width)
    table.insert(drawing.shapes, drawing.pending)
    end
    elseif drawing.pending.mode == 'manhattan' then
    local p1 = drawing.points[drawing.pending.p1]
    if drawing.pending then
    if drawing.pending.mode == nil then
    -- nothing pending
    elseif drawing.pending.mode == 'freehand' then
    -- the last point added during update is good enough
    Drawing.smoothen(drawing.pending)
    table.insert(drawing.shapes, drawing.pending)
    elseif drawing.pending.mode == 'line' then
    function Drawing.mouse_release(State, x,y, mouse_button)
    drawing.pending.target_point.x = mx
    drawing.pending.target_point.y = my
    Drawing.relax_constraints(drawing, drawing.pending.target_point_index)
    end
    else
    -- do nothing
    end
    end
    function Drawing.relax_constraints(drawing, p)
    for _,shape in ipairs(drawing.shapes) do
    if shape.mode == 'manhattan' then
    if shape.p1 == p then
    shape.mode = 'line'
    elseif shape.p2 == p then
    shape.mode = 'line'
    end
    elseif shape.mode == 'rectangle' or shape.mode == 'square' then
    for _,v in ipairs(shape.vertices) do
    if v == p then
    shape.mode = 'polygon'
    end
    end
    end
    end
    end
    if drawing.pending.mode == 'freehand' then
    table.insert(drawing.pending.points, {x=mx, y=my})
    elseif drawing.pending.mode == 'move' then
    drawing.pending.target_point.x = mx
    drawing.pending.target_point.y = my
    Drawing.relax_constraints(drawing, drawing.pending.target_point_index)
    end
    end
    elseif State.current_drawing_mode == 'move' then
    if App.mouse_down(1) then
    -- some event cleared starty just this frame
    -- draw in this frame will soon set starty
    -- just skip this frame
    return
    end
    -- all the action is in mouse_release
    if State.current_drawing_mode == 'freehand' then
    drawing.pending = {mode=State.current_drawing_mode, points={{x=cx, y=cy}}}
    elseif State.current_drawing_mode == 'line' or State.current_drawing_mode == 'manhattan' then
    local j = Drawing.find_or_insert_point(drawing.points, cx, cy, State.width)
    drawing.pending = {mode=State.current_drawing_mode, p1=j}
    elseif State.current_drawing_mode == 'polygon' or State.current_drawing_mode == 'rectangle' or State.current_drawing_mode == 'square' then
    local j = Drawing.find_or_insert_point(drawing.points, cx, cy, State.width)
    drawing.pending = {mode=State.current_drawing_mode, vertices={j}}
    elseif State.current_drawing_mode == 'circle' then
    local j = Drawing.find_or_insert_point(drawing.points, cx, cy, State.width)
    drawing.pending = {mode=State.current_drawing_mode, center=j}
    elseif State.current_drawing_mode == 'move' then
    local cx = Drawing.coord(x-State.left, State.width)
    function Drawing.mouse_press(State, drawing_index, x,y, mouse_button)
    end
    local width = right-left
    -- after mouse_release
    end
    function Drawing.draw_shape(drawing, shape, top, left,right)
    local width = right-left
    local function px(x) return Drawing.pixels(x, width)+left end
    local function py(y) return Drawing.pixels(y, width)+top end
    if shape.mode == 'freehand' then
    local prev = nil
    for _,point in ipairs(shape.points) do
    if prev then
    love.graphics.line(px(prev.x),py(prev.y), px(point.x),py(point.y))
    end
    prev = point
    end
    elseif shape.mode == 'line' or shape.mode == 'manhattan' then
    local p1 = drawing.points[shape.p1]
    local p2 = drawing.points[shape.p2]
    love.graphics.line(px(p1.x),py(p1.y), px(p2.x),py(p2.y))
    elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
    local prev = nil
    for _,point in ipairs(shape.vertices) do
    local curr = drawing.points[point]
    if prev then
    love.graphics.line(px(prev.x),py(prev.y), px(curr.x),py(curr.y))
    end
    prev = curr
    end
    -- close the loop
    local curr = drawing.points[shape.vertices[1]]
    love.graphics.line(px(prev.x),py(prev.y), px(curr.x),py(curr.y))
    elseif shape.mode == 'circle' then
    -- TODO: clip
    local center = drawing.points[shape.center]
    love.graphics.circle('line', px(center.x),py(center.y), Drawing.pixels(shape.radius, width))
    elseif shape.mode == 'arc' then
    local center = drawing.points[shape.center]
    love.graphics.arc('line', 'open', px(center.x),py(center.y), Drawing.pixels(shape.radius, width), shape.start_angle, shape.end_angle, 360)
    elseif shape.mode == 'deleted' then
    -- ignore
    else
    for i,p in ipairs(line.points) do
    if p.deleted == nil then
    if Drawing.near(p, mx,my, State.width) then
    App.color(Focus_stroke_color)
    love.graphics.circle('line', px(p.x),py(p.y), Same_point_distance)
    else
    App.color(Stroke_color)
    love.graphics.circle('fill', px(p.x),py(p.y), 2)
    end
    if p.name then
    -- TODO: clip
    local x,y = px(p.x)+5, py(p.y)+5
    love.graphics.print(p.name, x,y)
    if State.current_drawing_mode == 'name' and i == line.pending.target_point then
    -- create a faint red box for the name
    App.color(Current_name_background_color)
    end
    local function px(x) return Drawing.pixels(x, State.width)+State.left end
    for _,shape in ipairs(line.shapes) do
    end
    if App.mouse_down(1) and love.keyboard.isDown('h') then
    draw_help_with_mouse_pressed(State, line_index)
    return
    end
    end
    if line.show_help then
    draw_help_without_mouse_pressed(State, line_index)
    return
    end
    local mx = Drawing.coord(pmx-State.left, State.width)
    else
    if icon[State.current_drawing_mode] then
    App.color(Icon_color)
  • file deletion: help.lua (----------)help.lua (----------)
    [5.2][5.11545:11577](),[5.2][5.11545:11577](),[5.11577][5.6622:6622]()
    local starty = Text.starty(State, drawing_index)
    local y = starty+10
    love.graphics.rectangle('fill', State.left,starty, State.width, math.max(Drawing.pixels(drawing.h, State.width),y-starty))
    local starty = Text.starty(State, drawing_index)
    local y = starty+10
    love.graphics.rectangle('fill', State.left,starty, State.width, math.max(Drawing.pixels(drawing.h, State.width),y-starty))
    end
    end
    function current_shape(State, shape)
    if State.current_drawing_mode == 'freehand' then
    return 'freehand stroke'
    elseif State.current_drawing_mode == 'line' then
    return 'straight line'
    elseif State.current_drawing_mode == 'manhattan' then
    return 'horizontal/vertical line'
    elseif State.current_drawing_mode == 'circle' and shape and shape.start_angle then
    return 'arc'
    else
    return State.current_drawing_mode
    end
    love.graphics.print("You're currently drawing a "..current_shape(State, drawing.pending), State.left+30,y)
    y = y + State.line_height
    love.graphics.print('Things you can do now:', State.left+30,y)
    y = y + State.line_height
    if State.current_drawing_mode == 'freehand' then
    love.graphics.print('* Release the mouse button to finish drawing the stroke', State.left+30,y)
    y = y + State.line_height
    elseif State.current_drawing_mode == 'line' or State.current_drawing_mode == 'manhattan' then
    love.graphics.print('* Release the mouse button to finish drawing the line', State.left+30,y)
    y = y + State.line_height
    elseif State.current_drawing_mode == 'circle' then
    if drawing.pending.mode == 'circle' then
    love.graphics.print('* Release the mouse button to finish drawing the circle', State.left+30,y)
    y = y + State.line_height
    love.graphics.print("* Press 'a' to draw just an arc of a circle", State.left+30,y)
    else
    love.graphics.print('* Release the mouse button to finish drawing the arc', State.left+30,y)
    end
    y = y + State.line_height
    elseif State.current_drawing_mode == 'polygon' then
    love.graphics.print('* Release the mouse button to finish drawing the polygon', State.left+30,y)
    y = y + State.line_height
    love.graphics.print("* Press 'p' to add a vertex to the polygon", State.left+30,y)
    y = y + State.line_height
    elseif State.current_drawing_mode == 'rectangle' then
    if #drawing.pending.vertices < 2 then
    love.graphics.print("* Press 'p' to add a vertex to the rectangle", State.left+30,y)
    y = y + State.line_height
    else
    love.graphics.print('* Release the mouse button to finish drawing the rectangle', State.left+30,y)
    y = y + State.line_height
    love.graphics.print("* Press 'p' to replace the second vertex of the rectangle", State.left+30,y)
    y = y + State.line_height
    end
    elseif State.current_drawing_mode == 'square' then
    if #drawing.pending.vertices < 2 then
    love.graphics.print("* Press 'p' to add a vertex to the square", State.left+30,y)
    y = y + State.line_height
    else
    love.graphics.print('* Release the mouse button to finish drawing the square', State.left+30,y)
    y = y + State.line_height
    love.graphics.print("* Press 'p' to replace the second vertex of the square", State.left+30,y)
    y = y + State.line_height
    end
    end
    love.graphics.print("* Press 'esc' then release the mouse button to cancel the current shape", State.left+30,y)
    y = y + State.line_height
    y = y + State.line_height
    if State.current_drawing_mode ~= 'line' then
    love.graphics.print("* Press 'l' to switch to drawing lines", State.left+30,y)
    y = y + State.line_height
    end
    if State.current_drawing_mode ~= 'manhattan' then
    love.graphics.print("* Press 'm' to switch to drawing horizontal/vertical lines", State.left+30,y)
    y = y + State.line_height
    end
    if State.current_drawing_mode ~= 'circle' then
    love.graphics.print("* Press 'o' to switch to drawing circles/arcs", State.left+30,y)
    y = y + State.line_height
    end
    if State.current_drawing_mode ~= 'polygon' then
    love.graphics.print("* Press 'g' to switch to drawing polygons", State.left+30,y)
    y = y + State.line_height
    end
    if State.current_drawing_mode ~= 'rectangle' then
    love.graphics.print("* Press 'r' to switch to drawing rectangles", State.left+30,y)
    y = y + State.line_height
    end
    if State.current_drawing_mode ~= 'square' then
    love.graphics.print("* Press 's' to switch to drawing squares", State.left+30,y)
    y = y + State.line_height
    end
    App.color(Help_background_color)
    App.color(Help_color)
    end
    function draw_help_with_mouse_pressed(State, drawing_index)
    local drawing = State.lines[drawing_index]
    love.graphics.print("Things you can do:", State.left+30,y)
    y = y + State.line_height
    love.graphics.print("* Press the mouse button to start drawing a "..current_shape(State), State.left+30,y)
    y = y + State.line_height
    love.graphics.print("* Hover on a point and press 'ctrl+u' to pick it up and start moving it,", State.left+30,y)
    y = y + State.line_height
    App.color(Help_color)
  • file deletion: source_select.lua (----------)source_select.lua (----------)
    [5.2][5.30050:30091](),[5.2][5.30050:30091](),[5.30091][5.23288:23288]()
    if y < State.top then
    local screen_bottom1 = Text.screen_bottom1(State)
    return screen_bottom1.line, Text.pos_at_end_of_screen_line(State, screen_bottom1)
    end
    function Text.cut_selection(State)
    if State.selection1.line == nil then return end
    local result = Text.selection(State)
    Text.delete_selection(State)
    return result
    end
    function Text.delete_selection(State)
    if State.selection1.line == nil then return end
    local minl,maxl = minmax(State.selection1.line, State.cursor1.line)
    local before = snapshot(State, minl, maxl)
    Text.delete_selection_without_undo(State)
    record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})
    end
    function Text.delete_selection_without_undo(State)
    if State.selection1.line == nil then return end
    -- min,max = sorted(State.selection1,State.cursor1)
    local minl,minp = State.selection1.line,State.selection1.pos
    local maxl,maxp = State.cursor1.line,State.cursor1.pos
    if minl > maxl then
    minl,maxl = maxl,minl
    minp,maxp = maxp,minp
    elseif minl == maxl then
    if minp > maxp then
    minp,maxp = maxp,minp
    end
    end
    -- update State.cursor1 and State.selection1
    State.cursor1.line = minl
    State.cursor1.pos = minp
    if Text.lt1(State.cursor1, State.screen_top1) then
    State.screen_top1.line = State.cursor1.line
    State.screen_top1.pos = Text.pos_at_start_of_screen_line(State, State.cursor1)
    end
    State.selection1 = {}
    -- delete everything between min (inclusive) and max (exclusive)
    Text.clear_screen_line_cache(State, minl)
    local min_offset = Text.offset(State.lines[minl].data, minp)
    local max_offset = Text.offset(State.lines[maxl].data, maxp)
    if minl == maxl then
    --? print('minl == maxl')
    State.lines[minl].data = State.lines[minl].data:sub(1, min_offset-1)..State.lines[minl].data:sub(max_offset)
    return
    end
    return State.screen_top1.line, State.screen_top1.pos
  • file deletion: source_text_tests.lua (----------)source_text_tests.lua (----------)
    [5.2][5.83739:83784](),[5.2][5.83739:83784](),[5.83784][5.3561:3561]()
    App.screen.init{width=800, height=600}
    edit.run_after_keychord(Editor_state, 'M-right', 'right')
    edit.run_after_keychord(Editor_state, 'M-right', 'right')
    edit.draw(Editor_state) -- populate line_cache.startpos for each line
    edit.run_after_keychord(Editor_state, 'S-right', 'right')
    edit.run_after_keychord(Editor_state, 'right', 'right')
    edit.draw(Editor_state) -- populate line_cache.startpos for each line
    edit.draw(Editor_state) -- populate line_cache.startpos for each line
    edit.draw(Editor_state) -- populate line_cache.startpos for each line
    edit.draw(Editor_state) -- populate line_cache.startpos for each line
    edit.run_after_keychord(Editor_state, 'right', 'right')
    check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
    check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
    check_eq(Editor_state.cursor1.pos, 6, 'cursor:pos')
    -- click on first location
    edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
    -- hold down shift and click on a second location
    App.fake_key_press('lshift')
    edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
    -- hold down shift and click at a third location
    App.fake_key_press('lshift')
    edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+Editor_state.line_height+5, 1)
    App.fake_key_release('lshift')
    -- selection is between first and third location. forget the second location, not the first.
    -- click on first location
    edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
    -- hold down shift and click somewhere else
    App.fake_key_press('lshift')
    edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
    edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
    App.fake_key_release('lshift')
    -- press mouse above first line of text
    edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)
    check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')
    check_eq(Editor_state.selection1.line, 1, 'selection:line')
    check_eq(Editor_state.selection1.pos, 1, 'selection:pos')
    -- press and hold on first location
    edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
    -- drag and release somewhere else
    edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
    -- no change to data, selection is reset
    App.fake_key_release('lshift')
    edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
    check_eq(Editor_state.cursor1.line, 2, 'line')
    check_eq(Editor_state.cursor1.pos, 4, 'pos')
    check_eq(Editor_state.cursor1.pos, 9, 'check')
    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
  • file deletion: source_text.lua (----------)source_text.lua (----------)
    [5.2][5.147125:147164](),[5.2][5.147125:147164](),[5.147164][5.83786:83786]()
    -- return y for the next line
    return y
    --? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
    --? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
    --? print('=>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
    State.screen_top1 = Text.previous_screen_top1(State)
    State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
    Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State)
    Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
    end
    -- return the top y coordinate of a given line_index,
    -- or nil if no part of it is on screen
    function Text.starty(State, line_index)
    -- does not modify State (except to populate line_cache)
    if line_index < State.screen_top1.line then return end
    local loc2 = Text.to2(State, State.screen_top1)
    local y = State.top
    while true do
    if State.lines[loc2.line].mode == 'drawing' then
    y = y + Drawing_padding_top
    end
    if loc2.line == line_index then return y end
    if State.lines[loc2.line].mode == 'text' then
    y = y + State.line_height
    elseif State.lines[loc2.line].mode == 'drawing' then
    y = y + Drawing.pixels(State.lines[loc2.line].h, State.width) + Drawing_padding_bottom
    end
    if y + State.line_height > App.screen.height then break end
    local next_loc2 = Text.next_screen_line(State, loc2)
    if Text.eq2(next_loc2, loc2) then break end -- end of file
    loc2 = next_loc2
    end
    end
    function Text.previous_screen_top1(State)
    -- duplicate some logic from love.draw
    -- does not modify State (except to populate line_cache)
    local loc2 = Text.to2(State, State.screen_top1)
    if loc2.line == 1 and loc2.screen_line == 1 and loc2.screen_pos == 1 then break end
    if State.lines[loc2.line].mode == 'text' then
    elseif State.lines[loc2.line].mode == 'drawing' then
    y = y - Drawing_padding_height - Drawing.pixels(State.lines[loc2.line].h, State.width)
    loc2 = Text.previous_screen_line(State, loc2)
    return Text.to1(State, loc2)
    State.screen_top1 = Text.screen_bottom1(State)
    end
    -- return the location of the start of the bottom-most line on screen
    function Text.screen_bottom1(State)
    -- duplicate some logic from love.draw
    -- does not modify State (except to populate line_cache)
    local loc2 = Text.to2(State, State.screen_top1)
    local y = State.top
    while true do
    if State.lines[loc2.line].mode == 'text' then
    y = y + State.line_height
    elseif State.lines[loc2.line].mode == 'drawing' then
    y = y + Drawing_padding_height + Drawing.pixels(State.lines[loc2.line].h, State.width)
    end
    if y + State.line_height > App.screen.height then break end
    local next_loc2 = Text.next_screen_line(State, loc2)
    if Text.eq2(next_loc2, loc2) then break end
    loc2 = next_loc2
    end
    return Text.to1(State, loc2)
    --? print('down', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)
    local screen_bottom1 = Text.screen_bottom1(State)
    --? print('down 2', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, screen_bottom1.line, screen_bottom1.pos)
    if State.cursor1.line > screen_bottom1.line then
    local screen_bottom1 = Text.screen_bottom1(State)
    local scroll_down = Text.le1(screen_bottom1, State.cursor1)
    --? print('=>', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)
    assert(State.lines[loc1.line].mode == 'text')
    end
    function Text.final_text_loc_on_screen(State)
    local screen_bottom1 = Text.screen_bottom1(State)
    if State.lines[screen_bottom1.line].mode == 'text' then
    return {
    line=screen_bottom1.line,
    pos=Text.pos_at_end_of_screen_line(State, screen_bottom1),
    }
    end
    local loc2 = Text.to2(State, screen_bottom1)
    while true do
    if State.lines[loc2.line].mode == 'text' then break end
    assert(loc2.line > 1 or loc2.screen_line > 1 and loc2.screen_pos > 1) -- elsewhere we're making sure there's always at least one text line on screen
    loc2 = Text.previous_screen_line(State, loc2)
    end
    local result = Text.to1(State, loc2)
    result.pos = Text.pos_at_end_of_screen_line(State, result)
    return result
    --? print('snap', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
    --? print('snap =>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
    local starty = Text.starty(State, line_index)
    if starty == nil then return false end -- outside current page
    if y < starty then return false end
    return y < starty + State.line_height*(#line_cache.screen_line_starting_pos - Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) + 1)
    local starty = Text.starty(State, line_index)
    assert(my >= starty, 'failed to map y pixel to line')
    local y = starty
    end
    function Text.eq2(a, b)
    return a.line == b.line and a.screen_line == b.screen_line and a.screen_pos == b.screen_pos
    function Text.next_screen_line(State, loc2)
    if State.lines[loc2.line].mode == 'drawing' then
    return {line=loc2.line+1, screen_line=1, screen_pos=1}
    end
    Text.populate_screen_line_starting_pos(State, loc2.line)
    if loc2.screen_line >= #State.line_cache[loc2.line].screen_line_starting_pos then
    if loc2.line < #State.lines then
    return {line=loc2.line+1, screen_line=1, screen_pos=1}
    else
    return loc2
    end
    else
    return {line=loc2.line, screen_line=loc2.screen_line+1, screen_pos=1}
    end
    end
    local screen_bottom1 = Text.screen_bottom1(State)
    elseif State.cursor1.line >= screen_bottom1.line then
    State.cursor1 = Text.final_text_loc_on_screen(State)
    end
    end
    end
    -- slightly expensive since it redraws the screen
    function Text.cursor_out_of_screen(State)
    if Text.cursor_out_of_screen(State) then
    if Text.lt1(State.cursor1, State.screen_top1) then
    State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
    -- resize helper
    function Text.tweak_screen_top_and_cursor(State)
    if State.screen_top1.pos == 1 then return end
    Text.populate_screen_line_starting_pos(State, State.screen_top1.line)
    local line = State.lines[State.screen_top1.line]
    local line_cache = State.line_cache[State.screen_top1.line]
    for i=2,#line_cache.screen_line_starting_pos do
    local pos = line_cache.screen_line_starting_pos[i]
    if pos == State.screen_top1.pos then
    break
    end
    if pos > State.screen_top1.pos then
    -- make sure screen top is at start of a screen line
    local prev = line_cache.screen_line_starting_pos[i-1]
    if State.screen_top1.pos - prev < pos - State.screen_top1.pos then
    State.screen_top1.pos = prev
    else
    State.screen_top1.pos = pos
    end
    break
    end
    end
    -- make sure cursor is on screen
    end
    local start_screen_line_index = Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos)
    for screen_line_index = start_screen_line_index,#line_cache.screen_line_starting_pos do
    local screen_line_starting_pos = line_cache.screen_line_starting_pos[screen_line_index]
    local screen_line_starting_byte_offset = Text.offset(line.data, screen_line_starting_pos)
    --? print('iter', y, screen_line_index, screen_line_starting_pos, string.sub(line.data, screen_line_starting_byte_offset))
    -- duplicate some logic from Text.draw
    end
    -- convert mx,my in pixels to schema-1 coordinates
    Text.populate_screen_line_starting_pos(State, line_index)
    Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
    --? print('cursor pos '..tostring(State.cursor1.pos)..' is on the #'..tostring(top2.screen_line)..' screen line down')
    local y = App.screen.height - State.line_height
    -- duplicate some logic from love.draw
    while true do
    end
    Text.populate_screen_line_starting_pos(State, loc1.line)
    local line_cache = State.line_cache[loc1.line]
    local most_recent_final_pos = utf8.len(State.lines[loc1.line].data)+1
    for i=#line_cache.screen_line_starting_pos,1,-1 do
    local spos = line_cache.screen_line_starting_pos[i]
    if spos <= loc1.pos then
    return most_recent_final_pos
    end
    most_recent_final_pos = spos-1
    end
    end
    function Text.start_of_line(State)
    --? print('cursor is NOT at final screen line of its line')
    local screen_line_starting_pos, screen_line_index = Text.pos_at_start_of_screen_line(State, State.cursor1)
    Text.populate_screen_line_starting_pos(State, State.cursor1.line)
    local new_screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos[screen_line_index+1]
    --? print('switching pos of screen line at cursor from '..tostring(screen_line_starting_pos)..' to '..tostring(new_screen_line_starting_pos))
    local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos)
    local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset)
    --? print('screen top before:', State.screen_top1.line, State.screen_top1.pos)
    --? print('scroll up preserving cursor')
    Text.snap_cursor_to_bottom_of_screen(State)
    --? print('screen top after:', State.screen_top1.line, State.screen_top1.pos)
    end
    assert(State.cursor1.pos, 'cursor has no pos')
    end
    function Text.up(State)
    State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
    end
    function Text.pagedown(State)
    end
    end
    y = y - State.line_height
    local y = App.screen.height - State.line_height
    while y >= State.top do
    -- duplicate some logic from love.draw
    end
    schedule_save(State)
    record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})
    elseif chord == 'backspace' then
    Text.insert_at_cursor(State, '\t')
    if State.cursor_y > App.screen.height - State.line_height then
    Text.populate_screen_line_starting_pos(State, State.cursor1.line)
    Text.snap_cursor_to_bottom_of_screen(State, State.left, State.right)
    Text.insert_at_cursor(State, t)
    if State.cursor_y > App.screen.height - State.line_height then
    Text.populate_screen_line_starting_pos(State, State.cursor1.line)
    Text.snap_cursor_to_bottom_of_screen(State, State.left, State.right)
    end
    record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})
    end
    function Text.insert_at_cursor(State, t)
    end
    function Text.screen_line(line, line_cache, i)
    local pos = line_cache.screen_line_starting_pos[i]
    local offset = Text.offset(line.data, pos)
    if i >= #line_cache.screen_line_starting_pos then
    return line.data:sub(offset)
    function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_numbers)
  • file deletion: source_edit.lua (----------)source_edit.lua (----------)
    [5.2][5.165788:165827](),[5.2][5.165788:165827](),[5.165827][5.152503:152503]()
    --
    -- On lines that are drawings, pos will be nil.
    cursor1 = {line=1, pos=1}, -- position of cursor; must be on a text line
    y = Text.draw(State, line_index, y, startpos, hide_cursor, show_line_numbers)
    if Drawing.in_drawing(State, line_index, x, y, State.left,State.right) then
    State.selection1 = Text.final_text_loc_on_screen(State)
    State.cursor1 = Text.final_text_loc_on_screen(State)
    State.cursor1 = Text.screen_bottom1(State)
    chord ~= 'C-a' and chord ~= 'C-c' and chord ~= 'C-x' and chord ~= 'backspace' and chord ~= 'delete' and chord ~= 'C-z' and chord ~= 'C-y' and not App.is_cursor_movement(key) then
    edit.draw(State)
    App.fake_mouse_release(x,y, mouse_button)
    Text.delete_selection(State, State.left, State.right)
    end
    edit.put_cursor_on_next_text_line(State)
    edit.clean_up_mouse_press(State)
    end
    State.lines.current_drawing_index = line_index
    State.lines.current_drawing = line
    Drawing.before = snapshot(State, line_index)
    --? 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
    selection1 = {},
    -- some extra state to compute selection between mouse press and release
    old_cursor1 = nil,
    old_selection1 = nil,
    mousepress_shift = nil,
    screen_top1 = {line=1, pos=1}, -- position of start of screen line at top of screen
  • edit in text.lua at line 383
    [5.858][5.175:250](),[5.858][5.175:250]()
    State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
  • edit in text.lua at line 387
    [5.3234][5.251:338](),[5.3234][5.251:338](),[5.3692][5.339:414](),[5.3692][5.339:414]()
    State.screen_top1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos}
    State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
  • resurrect zombie in text.lua at line 387
    [5.652][5.1190:1239](),[5.652][5.1190:1239]()
    State.screen_top1 = Text.screen_bottom1(State)
  • edit in text.lua at line 388
    [5.1239]
    [5.707]
    State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
  • edit in text.lua at line 665
    [5.3781][5.835:997]()
    --? print('snap =>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)