get rid of all bifold text

[?]
Mar 18, 2023, 4:46 AM
G54H3YG2NEZPW2F6OYT5JPV7KSKVMNW5D3QT3FBCXTJHAQYTV5UAC

Dependencies

  • [2] XW7ANEJX switch shortcuts for bifold text
  • [3] B4JEWKWI hide editor cursor while in file navigator
  • [4] BH7BT36L ctrl+a: select entire buffer
  • [5] SWZAQHGR bugfix: up arrow when line above is a drawing
  • [6] QCPXQ2E3 add state arg to a few functions
  • [7] ODLKHO7B switch to line index in a function
  • [8] X3CQLBTR set window title within each app
  • [9] OI4FPFIN support drawings in the source editor
  • [10] 4VQGE7RA new test
  • [11] Z5HLXU4P add state arg to a few functions
  • [12] 3MAZEQK5 add state arg to Text.textinput
  • [13] AOZX2G5F source: no commandline args
  • [14] 2CK5QI7W make love event names consistent
  • [15] UN7GKYV5 support hyperlinks in the source editor
  • [16] IRCKL6VN extract scrolling logic out of insert_at_cursor
  • [17] KMSL74GA support selections in the source editor
  • [18] BYKXF3YY bugfix: draw menu after everything else
  • [19] YRJFJNUD bugfix
  • [20] JMUD7T3O get rid of ugly side-effects in tests
  • [21] KKMFQDR4 editing source code from within the app
  • [22] UHB4GARJ left/right margin -> left/right coordinates
  • [23] O4RRXNOK bugfix: disallow font size of 0
  • [*] BULPIBEG beginnings of a module for the text editor

Change contents

  • edit in text.lua at line 159
    [6.107][6.531:690](),[6.15258][6.531:690](),[6.531][6.531:690]()
    --? print('=>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
  • edit in source_text.lua at line 3
    [6.83813][6.83813:83875]()
    AB_padding = 20 -- space in pixels between A side and B side
  • replacement in source_text.lua at line 5
    [6.83964][6.83964:84036](),[6.84036][3.7:82]()
    -- return the final y, and pos,posB of start of final screen line drawn
    function Text.draw(State, line_index, y, startpos, startposB, hide_cursor)
    [6.83964]
    [6.84098]
    -- return the final y, and position of start of final screen line drawn
    function Text.draw(State, line_index, y, startpos, hide_cursor)
  • replacement in source_text.lua at line 11
    [6.84244][6.84244:84630](),[6.84630][3.83:139](),[3.139][6.84669:85853](),[6.84669][6.84669:85853](),[6.86007][6.86007:86501](),[6.86501][3.140:194](),[3.194][6.86538:87305](),[6.86538][6.86538:87305]()
    line_cache.startposB = startposB
    -- draw A side
    local overflows_screen, x, pos, screen_line_starting_pos
    if startpos then
    overflows_screen, x, y, pos, screen_line_starting_pos = Text.draw_wrapping_line(State, line_index, State.left, y, startpos)
    if overflows_screen then
    return y, screen_line_starting_pos
    end
    if Focus == 'edit' and State.cursor1.pos then
    if not hide_cursor and not State.search_term then
    if line_index == State.cursor1.line and State.cursor1.pos == pos then
    Text.draw_cursor(State, x, y)
    end
    end
    end
    else
    x = State.left
    end
    -- check for B side
    --? if line_index == 8 then print('checking for B side') end
    if line.dataB == nil then
    assert(y)
    assert(screen_line_starting_pos)
    --? if line_index == 8 then print('return 1') end
    return y, screen_line_starting_pos
    end
    if not State.expanded and not line.expanded then
    assert(y)
    assert(screen_line_starting_pos)
    --? if line_index == 8 then print('return 2') end
    button(State, 'expand', {x=x+AB_padding, y=y+2, w=App.width(State.em), h=State.line_height-4, color={1,1,1},
    icon = function(button_params)
    App.color(Fold_background_color)
    love.graphics.rectangle('fill', button_params.x, button_params.y, App.width(State.em), State.line_height-4, 2,2)
    end,
    onpress1 = function()
    line.expanded = true
    end,
    })
    return y, screen_line_starting_pos
    end
    -- draw B side
    --? if line_index == 8 then print('drawing B side') end
    App.color(Fold_color)
    if startposB then
    overflows_screen, x, y, pos, screen_line_starting_pos = Text.draw_wrapping_lineB(State, line_index, x,y, startposB)
    else
    overflows_screen, x, y, pos, screen_line_starting_pos = Text.draw_wrapping_lineB(State, line_index, x+AB_padding,y, 1)
    end
    if overflows_screen then
    return y, nil, screen_line_starting_pos
    end
    --? if line_index == 8 then print('a') end
    if Focus == 'edit' and State.cursor1.posB then
    --? if line_index == 8 then print('b') end
    if not hide_cursor and not State.search_term then
    --? if line_index == 8 then print('c', State.cursor1.line, State.cursor1.posB, line_index, pos) end
    if line_index == State.cursor1.line and State.cursor1.posB == pos then
    Text.draw_cursor(State, x, y)
    end
    end
    end
    return y, nil, screen_line_starting_pos
    end
    -- Given an array of fragments, draw the subset starting from pos to screen
    -- starting from (x,y).
    -- Return:
    -- - whether we got to bottom of screen before end of line
    -- - the final (x,y)
    -- - the final pos
    -- - starting pos of the final screen line drawn
    function Text.draw_wrapping_line(State, line_index, x,y, startpos)
    local line = State.lines[line_index]
    local line_cache = State.line_cache[line_index]
    --? print('== line', line_index, '^'..line.data..'$')
    [6.84244]
    [6.87305]
    -- wrap long lines
    local x = State.left
    local pos = 1
  • replacement in source_text.lua at line 34
    [6.88027][6.88027:88105]()
    return --[[screen filled]] true, x,y, pos, screen_line_starting_pos
    [6.88027]
    [6.88105]
    return y, screen_line_starting_pos
  • replacement in source_text.lua at line 59
    [6.88263][6.88263:88332]()
    if State.cursor1.pos and line_index == State.cursor1.line then
    [6.88263]
    [6.88332]
    if line_index == State.cursor1.line then
  • replacement in source_text.lua at line 77
    [6.89103][6.89103:90125](),[6.90125][6.20953:21138](),[6.21138][6.90125:91019](),[6.90125][6.90125:91019]()
    return false, x,y, pos, screen_line_starting_pos
    end
    function Text.draw_wrapping_lineB(State, line_index, x,y, startpos)
    local line = State.lines[line_index]
    local line_cache = State.line_cache[line_index]
    local screen_line_starting_pos = startpos
    Text.compute_fragmentsB(State, line_index, x)
    local pos = 1
    for _, f in ipairs(line_cache.fragmentsB) do
    local frag, frag_text = f.data, f.text
    local frag_len = utf8.len(frag)
    --? print('text.draw:', frag, 'at', line_index,pos, 'after', x,y)
    if pos < startpos then
    -- render nothing
    --? print('skipping', frag)
    else
    -- render fragment
    local frag_width = App.width(frag_text)
    if x + frag_width > State.right then
    assert(x > State.left) -- no overfull lines
    y = y + State.line_height
    if y + State.line_height > App.screen.height then
    return --[[screen filled]] true, x,y, pos, screen_line_starting_pos
    end
    screen_line_starting_pos = pos
    x = State.left
    end
    if State.selection1.line then
    local lo, hi = Text.clip_selection(State, line_index, pos, pos+frag_len)
    Text.draw_highlight(State, line, x,y, pos, lo,hi)
    end
    App.screen.draw(frag_text, x,y)
    -- render cursor if necessary
    if State.cursor1.posB and line_index == State.cursor1.line then
    if pos <= State.cursor1.posB and pos + frag_len > State.cursor1.posB then
    if State.search_term then
    if State.lines[State.cursor1.line].dataB:sub(State.cursor1.posB, State.cursor1.posB+utf8.len(State.search_term)-1) == State.search_term then
    local lo_px = Text.draw_highlight(State, line, x,y, pos, State.cursor1.posB, State.cursor1.posB+utf8.len(State.search_term))
    App.color(Fold_color)
    love.graphics.print(State.search_term, x+lo_px,y)
    end
    elseif Focus == 'edit' then
    Text.draw_cursor(State, x+Text.x(frag, State.cursor1.posB-pos+1), y)
    App.color(Fold_color)
    end
    end
    end
    x = x + frag_width
    [6.89103]
    [6.91019]
    if Focus == 'edit' and not hide_cursor and State.search_term == nil then
    if line_index == State.cursor1.line and State.cursor1.pos == pos then
    Text.draw_cursor(State, x, y)
  • edit in source_text.lua at line 81
    [6.91027][6.91027:91052]()
    pos = pos + frag_len
  • replacement in source_text.lua at line 82
    [6.91058][6.91058:91109]()
    return false, x,y, pos, screen_line_starting_pos
    [6.91058]
    [6.91109]
    return y, screen_line_starting_pos
  • edit in source_text.lua at line 167
    [6.94278][6.94278:97104]()
    function Text.populate_screen_line_starting_posB(State, line_index, x)
    local line = State.lines[line_index]
    local line_cache = State.line_cache[line_index]
    if line_cache.screen_line_starting_posB then
    return
    end
    -- duplicate some logic from Text.draw
    Text.compute_fragmentsB(State, line_index, x)
    line_cache.screen_line_starting_posB = {1}
    local pos = 1
    for _, f in ipairs(line_cache.fragmentsB) do
    local frag, frag_text = f.data, f.text
    -- render fragment
    local frag_width = App.width(frag_text)
    if x + frag_width > State.right then
    x = State.left
    table.insert(line_cache.screen_line_starting_posB, pos)
    end
    x = x + frag_width
    local frag_len = utf8.len(frag)
    pos = pos + frag_len
    end
    end
    function Text.compute_fragmentsB(State, line_index, x)
    --? print('compute_fragmentsB', line_index, 'between', x, State.right)
    local line = State.lines[line_index]
    local line_cache = State.line_cache[line_index]
    if line_cache.fragmentsB then
    return
    end
    line_cache.fragmentsB = {}
    -- try to wrap at word boundaries
    for frag in line.dataB:gmatch('%S*%s*') do
    local frag_text = App.newText(love.graphics.getFont(), frag)
    local frag_width = App.width(frag_text)
    --? print('x: '..tostring(x)..'; '..tostring(State.right-x)..'px to go')
    while x + frag_width > State.right do
    --? print(('checking whether to split fragment ^%s$ of width %d when rendering from %d'):format(frag, frag_width, x))
    if (x-State.left) < 0.8 * (State.right-State.left) then
    --? print('splitting')
    -- long word; chop it at some letter
    -- We're not going to reimplement TeX here.
    local bpos = Text.nearest_pos_less_than(frag, State.right - x)
    --? print('bpos', bpos)
    if bpos == 0 then break end -- avoid infinite loop when window is too narrow
    local boffset = Text.offset(frag, bpos+1) -- byte _after_ bpos
    --? print('space for '..tostring(bpos)..' graphemes, '..tostring(boffset-1)..' bytes')
    local frag1 = string.sub(frag, 1, boffset-1)
    local frag1_text = App.newText(love.graphics.getFont(), frag1)
    local frag1_width = App.width(frag1_text)
    --? print('extracting ^'..frag1..'$ of width '..tostring(frag1_width)..'px')
    assert(x + frag1_width <= State.right)
    table.insert(line_cache.fragmentsB, {data=frag1, text=frag1_text})
    frag = string.sub(frag, boffset)
    frag_text = App.newText(love.graphics.getFont(), frag)
    frag_width = App.width(frag_text)
    end
    x = State.left -- new line
    end
    if #frag > 0 then
    --? print('inserting ^'..frag..'$ of width '..tostring(frag_width)..'px')
    table.insert(line_cache.fragmentsB, {data=frag, text=frag_text})
    end
    x = x + frag_width
    end
    end
  • replacement in source_text.lua at line 181
    [6.97835][6.97835:98660]()
    if State.cursor1.pos then
    local byte_offset = Text.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)
    State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)..t..string.sub(State.lines[State.cursor1.line].data, byte_offset)
    Text.clear_screen_line_cache(State, State.cursor1.line)
    State.cursor1.pos = State.cursor1.pos+1
    else
    assert(State.cursor1.posB)
    local byte_offset = Text.offset(State.lines[State.cursor1.line].dataB, State.cursor1.posB)
    State.lines[State.cursor1.line].dataB = string.sub(State.lines[State.cursor1.line].dataB, 1, byte_offset-1)..t..string.sub(State.lines[State.cursor1.line].dataB, byte_offset)
    Text.clear_screen_line_cache(State, State.cursor1.line)
    State.cursor1.posB = State.cursor1.posB+1
    end
    [6.97835]
    [6.98660]
    local byte_offset = Text.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)
    State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)..t..string.sub(State.lines[State.cursor1.line].data, byte_offset)
    Text.clear_screen_line_cache(State, State.cursor1.line)
    State.cursor1.pos = State.cursor1.pos+1
  • replacement in source_text.lua at line 219
    [6.100094][6.100094:100150]()
    if State.cursor1.pos and State.cursor1.pos > 1 then
    [6.100094]
    [6.100150]
    if State.cursor1.pos > 1 then
  • edit in source_text.lua at line 230
    [6.100802][6.100802:101643]()
    end
    elseif State.cursor1.posB then
    if State.cursor1.posB > 1 then
    before = snapshot(State, State.cursor1.line)
    local byte_start = utf8.offset(State.lines[State.cursor1.line].dataB, State.cursor1.posB-1)
    local byte_end = utf8.offset(State.lines[State.cursor1.line].dataB, State.cursor1.posB)
    if byte_start then
    if byte_end then
    State.lines[State.cursor1.line].dataB = string.sub(State.lines[State.cursor1.line].dataB, 1, byte_start-1)..string.sub(State.lines[State.cursor1.line].dataB, byte_end)
    else
    State.lines[State.cursor1.line].dataB = string.sub(State.lines[State.cursor1.line].dataB, 1, byte_start-1)
    end
    State.cursor1.posB = State.cursor1.posB-1
    end
    else
    -- refuse to delete past beginning of side B
  • replacement in source_text.lua at line 266
    [6.103074][6.103074:103176]()
    if State.cursor1.posB or State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) then
    [6.103074]
    [6.103176]
    if State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) then
  • replacement in source_text.lua at line 271
    [6.103317][6.103317:103419]()
    if State.cursor1.pos and State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) then
    [6.103317]
    [6.103419]
    if State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) then
  • edit in source_text.lua at line 282
    [6.104024][6.104024:104845]()
    elseif State.cursor1.posB then
    if State.cursor1.posB <= utf8.len(State.lines[State.cursor1.line].dataB) then
    local byte_start = utf8.offset(State.lines[State.cursor1.line].dataB, State.cursor1.posB)
    local byte_end = utf8.offset(State.lines[State.cursor1.line].dataB, State.cursor1.posB+1)
    if byte_start then
    if byte_end then
    State.lines[State.cursor1.line].dataB = string.sub(State.lines[State.cursor1.line].dataB, 1, byte_start-1)..string.sub(State.lines[State.cursor1.line].dataB, byte_end)
    else
    State.lines[State.cursor1.line].dataB = string.sub(State.lines[State.cursor1.line].dataB, 1, byte_start-1)
    end
    -- no change to State.cursor1.pos
    end
    else
    -- refuse to delete past end of side B
    end
  • edit in source_text.lua at line 286
    [6.6171][6.6171:6298]()
    -- delete side B on first line
    State.lines[State.cursor1.line].dataB = State.lines[State.cursor1.line+1].dataB
  • replacement in source_text.lua at line 302
    [6.21621][6.21621:21720]()
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos, posB=State.cursor1.posB}
    [6.21621]
    [6.21720]
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}
  • replacement in source_text.lua at line 307
    [6.21770][6.21770:21869]()
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos, posB=State.cursor1.posB}
    [6.21770]
    [6.21869]
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}
  • replacement in source_text.lua at line 319
    [6.21973][6.21973:22072]()
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos, posB=State.cursor1.posB}
    [6.21973]
    [6.22072]
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}
  • replacement in source_text.lua at line 324
    [6.22122][6.22122:22221]()
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos, posB=State.cursor1.posB}
    [6.22122]
    [6.22221]
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}
  • replacement in source_text.lua at line 335
    [6.22325][6.22325:22424]()
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos, posB=State.cursor1.posB}
    [6.22325]
    [6.22424]
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}
  • replacement in source_text.lua at line 340
    [6.22474][6.22474:22573]()
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos, posB=State.cursor1.posB}
    [6.22474]
    [6.22573]
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}
  • replacement in source_text.lua at line 351
    [6.22677][6.22677:22776]()
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos, posB=State.cursor1.posB}
    [6.22677]
    [6.22776]
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}
  • replacement in source_text.lua at line 356
    [6.22826][6.22826:22925]()
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos, posB=State.cursor1.posB}
    [6.22826]
    [6.22925]
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}
  • replacement in source_text.lua at line 367
    [6.23029][6.23029:23128]()
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos, posB=State.cursor1.posB}
    [6.23029]
    [6.23128]
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}
  • replacement in source_text.lua at line 372
    [6.23178][6.23178:23277]()
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos, posB=State.cursor1.posB}
    [6.23178]
    [6.23277]
    State.selection1 = {line=State.cursor1.line, pos=State.cursor1.pos}
  • replacement in source_text.lua at line 379
    [6.106716][6.106716:106902](),[6.106902][6.6309:6489](),[6.6489][6.107069:107466](),[6.107069][6.107069:107466]()
    if State.cursor1.pos then
    -- when inserting a newline, move any B side to the new line
    local byte_offset = Text.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)
    table.insert(State.lines, State.cursor1.line+1, {mode='text', data=string.sub(State.lines[State.cursor1.line].data, byte_offset), dataB=State.lines[State.cursor1.line].dataB})
    table.insert(State.line_cache, State.cursor1.line+1, {})
    State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)
    State.lines[State.cursor1.line].dataB = nil
    Text.clear_screen_line_cache(State, State.cursor1.line)
    State.cursor1 = {line=State.cursor1.line+1, pos=1}
    else
    -- disable enter when cursor is on the B side
    end
    [6.106716]
    [6.107466]
    local byte_offset = Text.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)
    table.insert(State.lines, State.cursor1.line+1, {mode='text', data=string.sub(State.lines[State.cursor1.line].data, byte_offset)})
    table.insert(State.line_cache, State.cursor1.line+1, {})
    State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)
    Text.clear_screen_line_cache(State, State.cursor1.line)
    State.cursor1 = {line=State.cursor1.line+1, pos=1}
  • replacement in source_text.lua at line 395
    [6.107782][6.107782:107889]()
    if State.screen_top1.line == 1 and State.screen_top1.pos and State.screen_top1.pos == 1 then break end
    [6.107782]
    [6.6490]
    if State.screen_top1.line == 1 and State.screen_top1.pos == 1 then break end
  • replacement in source_text.lua at line 404
    [6.108019][6.108019:108123]()
    State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos, posB=State.screen_top1.posB}
    [6.108019]
    [6.108123]
    State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
  • edit in source_text.lua at line 412
    [6.108390]
    [6.108390]
    -- If a line/paragraph gets to a page boundary, I often want to scroll
    -- before I get to the bottom.
    -- However, only do this if it makes forward progress.
  • edit in source_text.lua at line 416
    [6.108443]
    [6.108443]
    if bot2.screen_line > 1 then
    bot2.screen_line = math.max(bot2.screen_line-10, 1)
    end
  • replacement in source_text.lua at line 423
    [6.108572][6.6770:6889]()
    State.screen_top1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos, posB=State.screen_bottom1.posB}
    [6.108572]
    [6.108659]
    State.screen_top1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos}
  • replacement in source_text.lua at line 426
    [6.108742][6.108742:108846]()
    State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos, posB=State.screen_top1.posB}
    [6.108742]
    [6.108846]
    State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
  • edit in source_text.lua at line 435
    [6.6947][6.109127:109238](),[6.109127][6.109127:109238]()
    if State.cursor1.pos then
    Text.upA(State)
    else
    Text.upB(State)
    end
    end
    function Text.upA(State)
  • edit in source_text.lua at line 474
    [6.111326][6.111326:111628](),[6.111628][6.8020:8200](),[6.8200][5.4513:4570](),[5.4570][6.8262:8919](),[6.8262][6.8262:8919](),[6.8919][6.112363:114810](),[6.112363][6.112363:114810]()
    function Text.upB(State)
    local line_cache = State.line_cache[State.cursor1.line]
    local screen_line_starting_posB, screen_line_indexB = Text.pos_at_start_of_screen_lineB(State, State.cursor1)
    assert(screen_line_indexB >= 1)
    if screen_line_indexB == 1 then
    -- move to A side of previous line
    local new_cursor_line = State.cursor1.line
    while new_cursor_line > 1 do
    new_cursor_line = new_cursor_line-1
    if State.lines[new_cursor_line].mode == 'text' then
    State.cursor1 = {line=new_cursor_line, posB=nil}
    Text.populate_screen_line_starting_pos(State, State.cursor1.line)
    local prev_line_cache = State.line_cache[State.cursor1.line]
    local prev_screen_line_starting_pos = prev_line_cache.screen_line_starting_pos[#prev_line_cache.screen_line_starting_pos]
    local prev_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, prev_screen_line_starting_pos)
    local s = string.sub(State.lines[State.cursor1.line].data, prev_screen_line_starting_byte_offset)
    State.cursor1.pos = prev_screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1
    break
    end
    end
    elseif screen_line_indexB == 2 then
    -- all-B screen-line to potentially A+B screen-line
    local xA = Margin_left + Text.screen_line_width(State, State.cursor1.line, #line_cache.screen_line_starting_pos) + AB_padding
    if State.cursor_x < xA then
    State.cursor1.posB = nil
    Text.populate_screen_line_starting_pos(State, State.cursor1.line)
    local new_screen_line_starting_pos = line_cache.screen_line_starting_pos[#line_cache.screen_line_starting_pos]
    local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos)
    local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset)
    State.cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1
    else
    Text.populate_screen_line_starting_posB(State, State.cursor1.line)
    local new_screen_line_starting_posB = line_cache.screen_line_starting_posB[screen_line_indexB-1]
    local new_screen_line_starting_byte_offsetB = Text.offset(State.lines[State.cursor1.line].dataB, new_screen_line_starting_posB)
    local s = string.sub(State.lines[State.cursor1.line].dataB, new_screen_line_starting_byte_offsetB)
    State.cursor1.posB = new_screen_line_starting_posB + Text.nearest_cursor_pos(s, State.cursor_x-xA, State.left) - 1
    end
    else
    assert(screen_line_indexB > 2)
    -- all-B screen-line to all-B screen-line
    Text.populate_screen_line_starting_posB(State, State.cursor1.line)
    local new_screen_line_starting_posB = line_cache.screen_line_starting_posB[screen_line_indexB-1]
    local new_screen_line_starting_byte_offsetB = Text.offset(State.lines[State.cursor1.line].dataB, new_screen_line_starting_posB)
    local s = string.sub(State.lines[State.cursor1.line].dataB, new_screen_line_starting_byte_offsetB)
    State.cursor1.posB = new_screen_line_starting_posB + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1
    end
    if Text.lt1(State.cursor1, State.screen_top1) then
    local top2 = Text.to2(State, State.screen_top1)
    top2 = Text.previous_screen_line(State, top2)
    State.screen_top1 = Text.to1(State, top2)
    end
    end
    -- cursor on final screen line (A or B side) => goes to next screen line on A side
    -- cursor on A side => move down one screen line (A side) in current line
    -- cursor on B side => move down one screen line (B side) in current line
  • replacement in source_text.lua at line 498
    [6.115743][6.115743:115833]()
    elseif State.cursor1.pos then
    -- move down one screen line (A side) in current line
    [6.115743]
    [6.115833]
    else
    -- move down one screen line in current line
  • edit in source_text.lua at line 514
    [6.117041][6.117041:117227]()
    end
    else
    -- move down one screen line (B side) in current line
    local scroll_down = false
    if Text.le1(State.screen_bottom1, State.cursor1) then
    scroll_down = true
  • edit in source_text.lua at line 515
    [6.117235][6.117235:118175]()
    local cursor_line = State.lines[State.cursor1.line]
    local cursor_line_cache = State.line_cache[State.cursor1.line]
    local cursor2 = Text.to2(State, State.cursor1)
    assert(cursor2.screen_lineB < #cursor_line_cache.screen_line_starting_posB)
    local screen_line_starting_posB, screen_line_indexB = Text.pos_at_start_of_screen_lineB(State, State.cursor1)
    Text.populate_screen_line_starting_posB(State, State.cursor1.line)
    local new_screen_line_starting_posB = cursor_line_cache.screen_line_starting_posB[screen_line_indexB+1]
    local new_screen_line_starting_byte_offsetB = Text.offset(cursor_line.dataB, new_screen_line_starting_posB)
    local s = string.sub(cursor_line.dataB, new_screen_line_starting_byte_offsetB)
    State.cursor1.posB = new_screen_line_starting_posB + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1
    if scroll_down then
    Text.snap_cursor_to_bottom_of_screen(State)
    end
  • replacement in source_text.lua at line 520
    [6.118378][6.118378:118472]()
    if State.cursor1.pos then
    State.cursor1.pos = 1
    else
    State.cursor1.posB = 1
    end
    [6.118378]
    [6.118472]
    State.cursor1.pos = 1
  • replacement in source_text.lua at line 522
    [6.118525][6.9405:9512]()
    State.screen_top1 = {line=State.cursor1.line, pos=State.cursor1.pos, posB=State.cursor1.posB} -- copy
    [6.118525]
    [6.118591]
    State.screen_top1 = {line=State.cursor1.line, pos=State.cursor1.pos} -- copy
  • replacement in source_text.lua at line 527
    [6.118635][6.118635:118828]()
    if State.cursor1.pos then
    State.cursor1.pos = utf8.len(State.lines[State.cursor1.line].data) + 1
    else
    State.cursor1.posB = utf8.len(State.lines[State.cursor1.line].dataB) + 1
    end
    [6.118635]
    [6.118828]
    State.cursor1.pos = utf8.len(State.lines[State.cursor1.line].data) + 1
  • replacement in source_text.lua at line 534
    [6.118961][6.118961:119794]()
    -- we can cross the fold, so check side A/B one level down
    Text.skip_whitespace_left(State)
    Text.left(State)
    Text.skip_non_whitespace_left(State)
    end
    function Text.word_right(State)
    -- we can cross the fold, so check side A/B one level down
    Text.skip_whitespace_right(State)
    Text.right(State)
    Text.skip_non_whitespace_right(State)
    if Text.cursor_out_of_screen(State) then
    Text.snap_cursor_to_bottom_of_screen(State)
    end
    end
    function Text.skip_whitespace_left(State)
    if State.cursor1.pos then
    Text.skip_whitespace_leftA(State)
    else
    Text.skip_whitespace_leftB(State)
    end
    end
    function Text.skip_non_whitespace_left(State)
    if State.cursor1.pos then
    Text.skip_non_whitespace_leftA(State)
    else
    Text.skip_non_whitespace_leftB(State)
    end
    end
    function Text.skip_whitespace_leftA(State)
    [6.118961]
    [6.119794]
    -- skip some whitespace
  • replacement in source_text.lua at line 544
    [6.120000][6.120000:120048]()
    end
    function Text.skip_whitespace_leftB(State)
    [6.120000]
    [6.120048]
    -- skip some non-whitespace
  • edit in source_text.lua at line 546
    [6.120064][6.120064:120230]()
    if State.cursor1.posB == 1 then
    break
    end
    if Text.match(State.lines[State.cursor1.line].dataB, State.cursor1.posB-1, '%S') then
    break
    end
  • edit in source_text.lua at line 547
    [6.120251][6.120251:120325]()
    end
    end
    function Text.skip_non_whitespace_leftA(State)
    while true do
  • edit in source_text.lua at line 552
    [6.120502][6.120502:120653]()
    break
    end
    Text.left(State)
    end
    end
    function Text.skip_non_whitespace_leftB(State)
    while true do
    if State.cursor1.posB == 1 then
  • edit in source_text.lua at line 554
    [6.120673][6.120673:121185]()
    assert(State.cursor1.posB > 1)
    if Text.match(State.lines[State.cursor1.line].dataB, State.cursor1.posB-1, '%s') then
    break
    end
    Text.left(State)
    end
    end
    function Text.skip_whitespace_right(State)
    if State.cursor1.pos then
    Text.skip_whitespace_rightA(State)
    else
    Text.skip_whitespace_rightB(State)
    end
    end
    function Text.skip_non_whitespace_right(State)
    if State.cursor1.pos then
    Text.skip_non_whitespace_rightA(State)
    else
    Text.skip_non_whitespace_rightB(State)
  • replacement in source_text.lua at line 557
    [6.121196][6.121196:121240]()
    function Text.skip_whitespace_rightA(State)
    [6.121196]
    [6.121240]
    function Text.word_right(State)
    -- skip some whitespace
  • edit in source_text.lua at line 568
    [6.121504][6.121504:121553]()
    end
    function Text.skip_whitespace_rightB(State)
  • edit in source_text.lua at line 569
    [6.121569][6.121569:121778]()
    if State.cursor1.posB > utf8.len(State.lines[State.cursor1.line].dataB) then
    break
    end
    if Text.match(State.lines[State.cursor1.line].dataB, State.cursor1.posB, '%S') then
    break
    end
  • edit in source_text.lua at line 570
    [6.121815][6.121815:121890]()
    end
    end
    function Text.skip_non_whitespace_rightA(State)
    while true do
  • edit in source_text.lua at line 576
    [6.122095][6.122095:122132]()
    Text.right_without_scroll(State)
  • replacement in source_text.lua at line 577
    [6.122138][6.122138:122453]()
    end
    function Text.skip_non_whitespace_rightB(State)
    while true do
    if State.cursor1.posB > utf8.len(State.lines[State.cursor1.line].dataB) then
    break
    end
    if Text.match(State.lines[State.cursor1.line].dataB, State.cursor1.posB, '%s') then
    break
    end
    Text.right_without_scroll(State)
    [6.122138]
    [6.122453]
    if Text.cursor_out_of_screen(State) then
    Text.snap_cursor_to_bottom_of_screen(State)
  • replacement in source_text.lua at line 592
    [6.122747][6.122747:122864]()
    if State.cursor1.pos then
    Text.leftA(State)
    else
    Text.leftB(State)
    end
    end
    function Text.leftA(State)
    [6.122747]
    [6.122864]
    assert(State.lines[State.cursor1.line].mode == 'text')
  • edit in source_text.lua at line 615
    [6.123324][6.123324:123792]()
    function Text.leftB(State)
    if State.cursor1.posB > 1 then
    State.cursor1.posB = State.cursor1.posB-1
    else
    -- overflow back into A side
    State.cursor1.posB = nil
    State.cursor1.pos = utf8.len(State.lines[State.cursor1.line].data) + 1
    end
    if Text.lt1(State.cursor1, State.screen_top1) then
    local top2 = Text.to2(State, State.screen_top1)
    top2 = Text.previous_screen_line(State, top2)
    State.screen_top1 = Text.to1(State, top2)
    end
    end
  • edit in source_text.lua at line 624
    [6.9925][6.123998:124163](),[6.123998][6.123998:124163]()
    if State.cursor1.pos then
    Text.right_without_scrollA(State)
    else
    Text.right_without_scrollB(State)
    end
    end
    function Text.right_without_scrollA(State)
  • edit in source_text.lua at line 638
    [6.124402][6.124402:124571](),[6.124571][6.10214:10221](),[6.10221][6.124622:124655](),[6.124622][6.124622:124655](),[6.124655][6.10222:10502](),[6.10502][6.124710:124721](),[6.124710][6.124710:124721]()
    function Text.right_without_scrollB(State)
    if State.cursor1.posB <= utf8.len(State.lines[State.cursor1.line].dataB) then
    State.cursor1.posB = State.cursor1.posB+1
    else
    -- overflow back into A side
    local new_cursor_line = State.cursor1.line
    while new_cursor_line <= #State.lines-1 do
    new_cursor_line = new_cursor_line+1
    if State.lines[new_cursor_line].mode == 'text' then
    State.cursor1 = {line=new_cursor_line, pos=1}
    break
    end
    end
    end
    end
  • edit in source_text.lua at line 650
    [6.125077][6.125077:125621]()
    function Text.pos_at_start_of_screen_lineB(State, loc1)
    Text.populate_screen_line_starting_pos(State, loc1.line)
    local line_cache = State.line_cache[loc1.line]
    local x = Margin_left + Text.screen_line_width(State, loc1.line, #line_cache.screen_line_starting_pos) + AB_padding
    Text.populate_screen_line_starting_posB(State, loc1.line, x)
    for i=#line_cache.screen_line_starting_posB,1,-1 do
    local sposB = line_cache.screen_line_starting_posB[i]
    if sposB <= loc1.posB then
    return sposB,i
    end
    end
    assert(false)
    end
  • edit in source_text.lua at line 652
    [6.125738][6.125738:125785]()
    local line = State.lines[State.cursor1.line]
  • replacement in source_text.lua at line 654
    [6.125930][6.125930:126622]()
    if (not State.expanded and not line.expanded) or
    line.dataB == nil then
    return screen_lines[#screen_lines] <= State.cursor1.pos
    end
    if State.cursor1.pos then
    -- ignore B side
    return screen_lines[#screen_lines] <= State.cursor1.pos
    end
    assert(State.cursor1.posB)
    local line_cache = State.line_cache[State.cursor1.line]
    local x = Margin_left + Text.screen_line_width(State, State.cursor1.line, #line_cache.screen_line_starting_pos) + AB_padding
    Text.populate_screen_line_starting_posB(State, State.cursor1.line, x)
    local screen_lines = State.line_cache[State.cursor1.line].screen_line_starting_posB
    return screen_lines[#screen_lines] <= State.cursor1.posB
    [6.125930]
    [6.126622]
    return screen_lines[#screen_lines] <= State.cursor1.pos
  • replacement in source_text.lua at line 682
    [6.126954][6.126954:127033]()
    --? print('to2:', State.cursor1.line, State.cursor1.pos, State.cursor1.posB)
    [6.126954]
    [6.127033]
    --? print('to2:', State.cursor1.line, State.cursor1.pos)
  • replacement in source_text.lua at line 684
    [6.127079][6.127079:127185]()
    --? print('to2: =>', top2.line, top2.screen_line, top2.screen_pos, top2.screen_lineB, top2.screen_posB)
    [6.127079]
    [6.127185]
    --? print('to2: =>', top2.line, top2.screen_line, top2.screen_pos)
  • replacement in source_text.lua at line 686
    [6.127220][6.127220:127567]()
    if top2.screen_pos then
    top2.screen_pos = 1
    else
    assert(top2.screen_posB)
    top2.screen_posB = 1
    end
    --? print('snap', State.screen_top1.line, State.screen_top1.pos, State.screen_top1.posB, State.cursor1.line, State.cursor1.pos, State.cursor1.posB, State.screen_bottom1.line, State.screen_bottom1.pos, State.screen_bottom1.posB)
    [6.127220]
    [6.127567]
    top2.screen_pos = 1 -- start of screen line
    --? print('snap', State.screen_top1.line, State.screen_top1.pos, State.screen_top1.posB, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
  • replacement in source_text.lua at line 692
    [6.127795][6.127795:127933]()
    --? print(y, 'top2:', State.lines[top2.line].data, top2.line, top2.screen_line, top2.screen_pos, top2.screen_lineB, top2.screen_posB)
    [6.127795]
    [6.127933]
    --? print(y, 'top2:', top2.line, top2.screen_line, top2.screen_pos)
  • replacement in source_text.lua at line 717
    [6.128343][6.128343:128576]()
    --? print('snap =>', State.screen_top1.line, State.screen_top1.pos, State.screen_top1.posB, State.cursor1.line, State.cursor1.pos, State.cursor1.posB, State.screen_bottom1.line, State.screen_bottom1.pos, State.screen_bottom1.posB)
    [6.128343]
    [6.128576]
    --? print('snap =>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
  • replacement in source_text.lua at line 726
    [6.128948][6.128948:130218]()
    local num_screen_lines = 0
    if line_cache.startpos then
    Text.populate_screen_line_starting_pos(State, line_index)
    num_screen_lines = num_screen_lines + #line_cache.screen_line_starting_pos - Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) + 1
    end
    --? print('#screenlines after A', num_screen_lines)
    if line.dataB and (State.expanded or line.expanded) then
    local x = Margin_left + Text.screen_line_width(State, line_index, #line_cache.screen_line_starting_pos) + AB_padding
    Text.populate_screen_line_starting_posB(State, line_index, x)
    --? print('B:', x, #line_cache.screen_line_starting_posB)
    if line_cache.startposB then
    num_screen_lines = num_screen_lines + #line_cache.screen_line_starting_posB - Text.screen_line_indexB(line_cache.screen_line_starting_posB, line_cache.startposB) -- no +1; first screen line of B side overlaps with A side
    else
    num_screen_lines = num_screen_lines + #line_cache.screen_line_starting_posB - Text.screen_line_indexB(line_cache.screen_line_starting_posB, 1) -- no +1; first screen line of B side overlaps with A side
    end
    end
    --? print('#screenlines after B', num_screen_lines)
    return y < line_cache.starty + State.line_height*num_screen_lines
    [6.128948]
    [6.130218]
    Text.populate_screen_line_starting_pos(State, line_index)
    return y < line_cache.starty + State.line_height*(#line_cache.screen_line_starting_pos - Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) + 1)
  • edit in source_text.lua at line 731
    [6.130274][6.130274:130740]()
    -- returns: pos, posB
    -- scenarios:
    -- line without B side
    -- line with B side collapsed
    -- line with B side expanded
    -- line starting rendering in A side (startpos ~= nil)
    -- line starting rendering in B side (startposB ~= nil)
    -- my on final screen line of A side
    -- mx to right of A side with no B side
    -- mx to right of A side but left of B side
    -- mx to right of B side
    -- preconditions:
    -- startpos xor startposB
    -- expanded -> dataB
  • replacement in source_text.lua at line 737
    [6.130990][6.130990:134318]()
    --? print('click', line_index, my, 'with line starting at', y, #line_cache.screen_line_starting_pos) -- , #line_cache.screen_line_starting_posB)
    if line_cache.startpos then
    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))
    local nexty = y + State.line_height
    if my < nexty then
    -- On all wrapped screen lines but the final one, clicks past end of
    -- line position cursor on final character of screen line.
    -- (The final screen line positions past end of screen line as always.)
    if screen_line_index < #line_cache.screen_line_starting_pos and mx > State.left + Text.screen_line_width(State, line_index, screen_line_index) then
    --? print('past end of non-final line; return')
    return line_cache.screen_line_starting_pos[screen_line_index+1]-1
    end
    local s = string.sub(line.data, screen_line_starting_byte_offset)
    --? print('return', mx, Text.nearest_cursor_pos(s, mx, State.left), '=>', screen_line_starting_pos + Text.nearest_cursor_pos(s, mx, State.left) - 1)
    local screen_line_posA = Text.nearest_cursor_pos(s, mx, State.left)
    if line.dataB == nil then
    -- no B side
    return screen_line_starting_pos + screen_line_posA - 1
    end
    if not State.expanded and not line.expanded then
    -- B side is not expanded
    return screen_line_starting_pos + screen_line_posA - 1
    end
    local lenA = utf8.len(s)
    if screen_line_posA < lenA then
    -- mx is within A side
    return screen_line_starting_pos + screen_line_posA - 1
    end
    local max_xA = State.left+Text.x(s, lenA+1)
    if mx < max_xA + AB_padding then
    -- mx is in the space between A and B side
    return screen_line_starting_pos + screen_line_posA - 1
    end
    mx = mx - max_xA - AB_padding
    local screen_line_posB = Text.nearest_cursor_pos(line.dataB, mx, --[[no left margin]] 0)
    return nil, screen_line_posB
    end
    y = nexty
    end
    end
    -- look in screen lines composed entirely of the B side
    assert(State.expanded or line.expanded)
    local start_screen_line_indexB
    if line_cache.startposB then
    start_screen_line_indexB = Text.screen_line_indexB(line_cache.screen_line_starting_posB, line_cache.startposB)
    else
    start_screen_line_indexB = 2 -- skip the first line of side B, which we checked above
    end
    for screen_line_indexB = start_screen_line_indexB,#line_cache.screen_line_starting_posB do
    local screen_line_starting_posB = line_cache.screen_line_starting_posB[screen_line_indexB]
    local screen_line_starting_byte_offsetB = Text.offset(line.dataB, screen_line_starting_posB)
    --? print('iter2', y, screen_line_indexB, screen_line_starting_posB, string.sub(line.dataB, screen_line_starting_byte_offsetB))
    [6.130990]
    [6.134318]
    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))
  • replacement in source_text.lua at line 747
    [6.134599][6.134599:134859]()
    --? print('aa', mx, State.left, Text.screen_line_widthB(State, line_index, screen_line_indexB))
    if screen_line_indexB < #line_cache.screen_line_starting_posB and mx > State.left + Text.screen_line_widthB(State, line_index, screen_line_indexB) then
    [6.134599]
    [6.134859]
    if screen_line_index < #line_cache.screen_line_starting_pos and mx > State.left + Text.screen_line_width(State, line_index, screen_line_index) then
  • replacement in source_text.lua at line 749
    [6.134915][6.134915:134996]()
    return nil, line_cache.screen_line_starting_posB[screen_line_indexB+1]-1
    [6.134915]
    [6.134996]
    return line_cache.screen_line_starting_pos[screen_line_index+1]-1
  • replacement in source_text.lua at line 751
    [6.135006][6.135006:135329]()
    local s = string.sub(line.dataB, screen_line_starting_byte_offsetB)
    --? print('return', mx, Text.nearest_cursor_pos(s, mx, State.left), '=>', screen_line_starting_posB + Text.nearest_cursor_pos(s, mx, State.left) - 1)
    return nil, screen_line_starting_posB + Text.nearest_cursor_pos(s, mx, State.left) - 1
    [6.135006]
    [6.135329]
    local s = string.sub(line.data, screen_line_starting_byte_offset)
    --? print('return', mx, Text.nearest_cursor_pos(s, mx, State.left), '=>', screen_line_starting_pos + Text.nearest_cursor_pos(s, mx, State.left) - 1)
    return screen_line_starting_pos + Text.nearest_cursor_pos(s, mx, State.left) - 1
  • edit in source_text.lua at line 777
    [6.136095][6.136095:137079]()
    function Text.screen_line_widthB(State, line_index, i)
    local line = State.lines[line_index]
    local line_cache = State.line_cache[line_index]
    local start_posB = line_cache.screen_line_starting_posB[i]
    local start_offsetB = Text.offset(line.dataB, start_posB)
    local screen_line
    if i < #line_cache.screen_line_starting_posB then
    --? print('non-final', i)
    local past_end_posB = line_cache.screen_line_starting_posB[i+1]
    local past_end_offsetB = Text.offset(line.dataB, past_end_posB)
    --? print('between', start_offsetB, past_end_offsetB)
    screen_line = string.sub(line.dataB, start_offsetB, past_end_offsetB-1)
    else
    --? print('final', i)
    --? print('after', start_offsetB)
    screen_line = string.sub(line.dataB, start_offsetB)
    end
    local screen_line_text = App.newText(love.graphics.getFont(), screen_line)
    --? local result = App.width(screen_line_text)
    --? print('=>', result)
    --? return result
    return App.width(screen_line_text)
    end
  • edit in source_text.lua at line 785
    [6.137267][6.137267:137538]()
    function Text.screen_line_indexB(screen_line_starting_posB, posB)
    if posB == nil then
    return 0
    end
    assert(screen_line_starting_posB)
    for i = #screen_line_starting_posB,1,-1 do
    if screen_line_starting_posB[i] <= posB then
    return i
    end
    end
    end
  • edit in source_text.lua at line 877
    [6.11942][6.139891:140028](),[6.139891][6.139891:140028]()
    if loc1.pos then
    return Text.to2A(State, loc1)
    else
    return Text.to2B(State, loc1)
    end
    end
    function Text.to2A(State, loc1)
  • edit in source_text.lua at line 889
    [6.140437][6.140437:141090]()
    return result
    end
    function Text.to2B(State, loc1)
    local result = {line=loc1.line}
    local line_cache = State.line_cache[loc1.line]
    Text.populate_screen_line_starting_pos(State, loc1.line)
    local x = Margin_left + Text.screen_line_width(State, loc1.line, #line_cache.screen_line_starting_pos) + AB_padding
    Text.populate_screen_line_starting_posB(State, loc1.line, x)
    for i=#line_cache.screen_line_starting_posB,1,-1 do
    local sposB = line_cache.screen_line_starting_posB[i]
    if sposB <= loc1.posB then
    result.screen_lineB = i
    result.screen_posB = loc1.posB - sposB + 1
    break
    end
    end
    assert(result.screen_posB)
  • edit in source_text.lua at line 893
    [6.141142][6.141142:141286]()
    if loc2.screen_pos then
    return Text.to1A(State, loc2)
    else
    return Text.to1B(State, loc2)
    end
    end
    function Text.to1A(State, loc2)
  • replacement in source_text.lua at line 900
    [6.141509][6.141509:141766]()
    function Text.to1B(State, loc2)
    local result = {line=loc2.line, posB=loc2.screen_posB}
    if loc2.screen_lineB > 1 then
    result.posB = State.line_cache[loc2.line].screen_line_starting_posB[loc2.screen_lineB] + loc2.screen_posB - 1
    end
    return result
    [6.141509]
    [6.141766]
    function Text.eq1(a, b)
    return a.line == b.line and a.pos == b.pos
  • replacement in source_text.lua at line 911
    [6.141892][6.141892:141943]()
    -- A side < B side
    if a.pos and not b.pos then
    [6.141892]
    [6.141943]
    return a.pos < b.pos
    end
    function Text.le1(a, b)
    if a.line < b.line then
  • replacement in source_text.lua at line 918
    [6.141965][6.141965:141995]()
    if not a.pos and b.pos then
    [6.141965]
    [6.141995]
    if a.line > b.line then
  • replacement in source_text.lua at line 921
    [6.142018][6.142018:142099]()
    if a.pos then
    return a.pos < b.pos
    else
    return a.posB < b.posB
    end
    [6.142018]
    [6.142099]
    return a.pos <= b.pos
  • edit in source_text.lua at line 924
    [6.142104][6.142104:142169]()
    function Text.le1(a, b)
    return eq(a, b) or Text.lt1(a, b)
    end
  • edit in source_text.lua at line 935
    [6.142409][6.142409:142604]()
    if loc2.screen_pos then
    return Text.previous_screen_lineA(State, loc2)
    else
    return Text.previous_screen_lineB(State, loc2)
    end
    end
    function Text.previous_screen_lineA(State, loc2)
  • edit in source_text.lua at line 939
    [6.142792]
    [6.142792]
    elseif State.lines[loc2.line-1].mode == 'drawing' then
    return {line=loc2.line-1, screen_line=1, screen_pos=1}
  • edit in source_text.lua at line 942
    [6.142799]
    [6.142799]
    local l = State.lines[loc2.line-1]
  • replacement in source_text.lua at line 944
    [6.142862][6.142862:144205]()
    if State.lines[loc2.line-1].dataB == nil or
    (not State.expanded and not State.lines[loc2.line-1].expanded) then
    --? print('c1', loc2.line-1, State.lines[loc2.line-1].data, '==', State.lines[loc2.line-1].dataB, State.line_cache[loc2.line-1].fragmentsB)
    return {line=loc2.line-1, screen_line=#State.line_cache[loc2.line-1].screen_line_starting_pos, screen_pos=1}
    end
    -- try to switch to B
    local prev_line_cache = State.line_cache[loc2.line-1]
    local x = Margin_left + Text.screen_line_width(State, loc2.line-1, #prev_line_cache.screen_line_starting_pos) + AB_padding
    Text.populate_screen_line_starting_posB(State, loc2.line-1, x)
    local screen_line_starting_posB = State.line_cache[loc2.line-1].screen_line_starting_posB
    --? print('c', loc2.line-1, State.lines[loc2.line-1].data, '==', State.lines[loc2.line-1].dataB, '==', #screen_line_starting_posB, 'starting from x', x)
    if #screen_line_starting_posB > 1 then
    --? print('c2')
    return {line=loc2.line-1, screen_lineB=#State.line_cache[loc2.line-1].screen_line_starting_posB, screen_posB=1}
    else
    --? print('c3')
    -- if there's only one screen line, assume it overlaps with A, so remain in A
    return {line=loc2.line-1, screen_line=#State.line_cache[loc2.line-1].screen_line_starting_pos, screen_pos=1}
    end
    [6.142862]
    [6.144205]
    return {line=loc2.line-1, screen_line=#State.line_cache[loc2.line-1].screen_line_starting_pos, screen_pos=1}
  • edit in source_text.lua at line 948
    [6.144216][6.144216:144658]()
    function Text.previous_screen_lineB(State, loc2)
    if loc2.screen_lineB > 2 then -- first screen line of B side overlaps with A side
    return {line=loc2.line, screen_lineB=loc2.screen_lineB-1, screen_posB=1}
    else
    -- switch to A side
    -- TODO: handle case where fold lands precisely at end of a new screen-line
    return {line=loc2.line, screen_line=#State.line_cache[loc2.line].screen_line_starting_pos, screen_pos=1}
    end
    end
  • replacement in source_text.lua at line 977
    [6.145797][6.145797:145985]()
    local pos,posB = Text.to_pos_on_line(State, State.screen_bottom1.line, State.right-5, App.screen.height-5)
    State.cursor1 = {line=State.screen_bottom1.line, pos=pos, posB=posB}
    [6.145797]
    [6.145985]
    State.cursor1 = {
    line=State.screen_bottom1.line,
    pos=Text.to_pos_on_line(State, State.screen_bottom1.line, State.right-5, App.screen.height-5),
    }
  • edit in source_text.lua at line 1017
    [6.146715][6.146715:146763]()
    State.line_cache[line_index].fragmentsB = nil
  • edit in source_text.lua at line 1018
    [6.146825][6.146825:146888]()
    State.line_cache[line_index].screen_line_starting_posB = nil
  • edit in source_edit.lua at line 13
    [6.18192][6.152673:152746](),[6.152673][6.152673:152746]()
    Fold_color = {r=0, g=0.6, b=0}
    Fold_background_color = {r=0, g=0.7, b=0}
  • replacement in source_edit.lua at line 29
    [6.152987][6.18407:18530]()
    -- a line is either bifold text or a drawing
    -- a line of bifold text consists of an A side and an optional B side
    [6.152987]
    [6.18530]
    -- a line is either text or a drawing
    -- a text is a table with:
  • edit in source_edit.lua at line 33
    [6.18578][6.18578:18645]()
    -- string dataB,
    -- expanded: whether to show B side
  • replacement in source_edit.lua at line 49
    [6.19494][6.19494:19576]()
    lines = {{mode='text', data='', dataB=nil, expanded=nil}}, -- array of lines
    [6.19494]
    [6.153197]
    lines = {{mode='text', data=''}}, -- array of lines
  • edit in source_edit.lua at line 64
    [6.154135][6.154135:154216]()
    -- Positions (and screen line indexes) can be in either the A or the B side.
  • replacement in source_edit.lua at line 70
    [6.154462][6.154462:154730]()
    screen_top1 = {line=1, pos=1, posB=nil}, -- position of start of screen line at top of screen
    cursor1 = {line=1, pos=1, posB=nil}, -- position of cursor
    screen_bottom1 = {line=1, pos=1, posB=nil}, -- position of start of screen line at bottom of screen
    [6.154462]
    [6.30094]
    screen_top1 = {line=1, pos=1}, -- position of start of screen line at top of screen
    cursor1 = {line=1, pos=1}, -- position of cursor
    screen_bottom1 = {line=1, pos=1}, -- position of start of screen line at bottom of screen
  • replacement in source_edit.lua at line 152
    [6.155530][6.155530:155670]()
    print(State.screen_top1.line, State.screen_top1.pos, State.screen_top1.posB, State.cursor1.line, State.cursor1.pos, State.cursor1.posB)
    [6.155530]
    [6.155670]
    print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
  • replacement in source_edit.lua at line 161
    [6.155884][6.19889:19944]()
    --? print('draw:', y, line_index, line, line.mode)
    [6.155884]
    [6.155928]
    --? print('draw:', y, line_index, line)
  • replacement in source_edit.lua at line 163
    [6.155992][6.155992:156056]()
    State.screen_bottom1 = {line=line_index, pos=nil, posB=nil}
    [6.155992]
    [6.19945]
    State.screen_bottom1 = {line=line_index, pos=nil}
  • replacement in source_edit.lua at line 165
    [6.19977][6.156056:156098](),[6.156056][6.156056:156098](),[6.156098][6.19978:20019]()
    --? print('text.draw', y, line_index)
    local startpos, startposB = 1, nil
    [6.19977]
    [6.20019]
    --? print('text.draw', y, line_index)
    local startpos = 1
  • replacement in source_edit.lua at line 168
    [6.20070][6.20070:20236]()
    if State.screen_top1.pos then
    startpos = State.screen_top1.pos
    else
    startpos, startposB = nil, State.screen_top1.posB
    end
    [6.20070]
    [6.20236]
    startpos = State.screen_top1.pos
  • replacement in source_edit.lua at line 186
    [6.156342][3.239:368]()
    y, State.screen_bottom1.pos, State.screen_bottom1.posB = Text.draw(State, line_index, y, startpos, startposB, hide_cursor)
    [6.156342]
    [6.21213]
    y, State.screen_bottom1.pos = Text.draw(State, line_index, y, startpos, hide_cursor)
  • replacement in source_edit.lua at line 249
    [6.31038][6.21646:21781](),[6.21646][6.21646:21781](),[6.21781][6.31039:31203]()
    local pos,posB = Text.to_pos_on_line(State, line_index, x, y)
    --? print(x,y, 'setting cursor:', line_index, pos, posB)
    State.selection1 = {line=line_index, pos=pos, posB=posB}
    --? print('selection', State.selection1.line, State.selection1.pos, State.selection1.posB)
    [6.31038]
    [6.21843]
    State.selection1 = {
    line=line_index,
    pos=Text.to_pos_on_line(State, line_index, x, y),
    }
    --? print('selection', State.selection1.line, State.selection1.pos)
  • replacement in source_edit.lua at line 284
    [6.31387][6.31387:31612]()
    local pos,posB = Text.to_pos_on_line(State, line_index, x, y)
    State.cursor1 = {line=line_index, pos=pos, posB=posB}
    --? print('cursor', State.cursor1.line, State.cursor1.pos, State.cursor1.posB)
    [6.31387]
    [6.31612]
    State.cursor1 = {
    line=line_index,
    pos=Text.to_pos_on_line(State, line_index, x, y),
    }
    --? print('cursor', State.cursor1.line, State.cursor1.pos)
  • replacement in source_edit.lua at line 357
    [6.158906][6.158906:159057]()
    if State.cursor1.pos then
    State.cursor1.pos = State.cursor1.pos+1
    else
    State.cursor1.posB = State.cursor1.posB+1
    end
    [6.158906]
    [6.159057]
    State.cursor1.pos = State.cursor1.pos+1
  • replacement in source_edit.lua at line 366
    [6.159254][6.159254:159446]()
    cursor={line=State.cursor1.line, pos=State.cursor1.pos, posB=State.cursor1.posB},
    screen_top={line=State.screen_top1.line, pos=State.screen_top1.pos, posB=State.screen_top1.posB},
    [6.159254]
    [6.159446]
    cursor={line=State.cursor1.line, pos=State.cursor1.pos},
    screen_top={line=State.screen_top1.line, pos=State.screen_top1.pos},
  • edit in source_edit.lua at line 370
    [6.159489][6.159489:159506](),[6.159506][2.11:40](),[2.40][6.159535:159775](),[6.159535][6.159535:159775](),[6.159775][2.41:70](),[2.70][6.159804:160392](),[6.23031][6.159804:160392](),[6.159804][6.159804:160392]()
    -- bifold text
    elseif chord == 'M-b' then
    State.expanded = not State.expanded
    Text.redraw_all(State)
    if not State.expanded then
    for _,line in ipairs(State.lines) do
    line.expanded = nil
    end
    edit.eradicate_locations_after_the_fold(State)
    end
    elseif chord == 'M-d' then
    if State.cursor1.posB == nil then
    local before = snapshot(State, State.cursor1.line)
    if State.lines[State.cursor1.line].dataB == nil then
    State.lines[State.cursor1.line].dataB = ''
    end
    State.lines[State.cursor1.line].expanded = true
    State.cursor1.pos = nil
    State.cursor1.posB = 1
    if Text.cursor_out_of_screen(State) then
    Text.snap_cursor_to_bottom_of_screen(State, State.left, State.right)
    end
    schedule_save(State)
    record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})
    end
  • replacement in source_edit.lua at line 417
    [4.233][4.233:331]()
    State.cursor1 = {line=#State.lines, pos=utf8.len(State.lines[#State.lines].data)+1, posB=nil}
    [4.233]
    [6.161829]
    State.cursor1 = {line=#State.lines, pos=utf8.len(State.lines[#State.lines].data)+1}
  • edit in source_edit.lua at line 492
    [6.163286][6.163286:163849]()
    function edit.eradicate_locations_after_the_fold(State)
    -- eradicate side B from any locations we track
    if State.cursor1.posB then
    State.cursor1.posB = nil
    State.cursor1.pos = utf8.len(State.lines[State.cursor1.line].data)
    State.cursor1.pos = Text.pos_at_start_of_screen_line(State, State.cursor1)
    end
    if State.screen_top1.posB then
    State.screen_top1.posB = nil
    State.screen_top1.pos = utf8.len(State.lines[State.screen_top1.line].data)
    State.screen_top1.pos = Text.pos_at_start_of_screen_line(State, State.screen_top1)
    end
    end
  • replacement in source.lua at line 78
    [6.167321][6.167321:167372]()
    -- environment for a mutable file of bifolded text
    [6.167321]
    [6.167372]
    -- environment for a mutable file
  • edit in source.lua at line 92
    [6.167894][6.167894:168053]()
    -- We currently start out with side B collapsed.
    -- Other options:
    -- * save all expanded state by line
    -- * expand all if any location is in side B
  • edit in source.lua at line 98
    [6.168275][6.168275:168331]()
    edit.eradicate_locations_after_the_fold(Editor_state)
  • edit in source.lua at line 251
    [6.173453][6.173453:173488]()
    -- convert any bifold files here
  • edit in source.lua at line 253
    [6.173493][6.173493:173700]()
    function source.convert_bifold_text(infilename, outfilename)
    local contents = love.filesystem.read(infilename)
    contents = contents:gsub('\u{1e}', ';')
    love.filesystem.write(outfilename, contents)
    end