Merge lines.love

[?]
Sep 18, 2022, 8:56 AM
VBU5YHLRO5ZSKFWBJRX7DWQGWPEHEWZMRRVV2WMWDJ54PKUNYCNQC

Dependencies

  • [2] UI3IP45F Merge lines.love
  • [3] N7VXEGLG yet another bugfix in log parsing
  • [4] BYKXF3YY bugfix: draw menu after everything else
  • [5] D3FLL7SL start showing source menu file navigation state graphically
  • [6] 3QWK3GSA support mouse clicks in file navigator
  • [7] B4JEWKWI hide editor cursor while in file navigator
  • [8] RQUVBX62 filter candidates in file navigator
  • [9] SXTQMD44 include some missing source files
  • [10] T4FRZSYL delete an ancient, unused file
  • [11] MFZW24AN bugfix: disable typing while file navigator is open
  • [12] BULPIBEG beginnings of a module for the text editor
  • [13] BJ5X5O4A let's prevent the text cursor from ever getting on a drawing
  • [14] K2X6G75Z start writing some tests for drawings
  • [15] 66X36NZN a little more prose describing manual_tests
  • [16] K464QQR4 more defensive resize handling
  • [17] IOCATA27 bugfix: never skip files in file navigator
  • [18] VXORMHME delete experimental REPL
  • [19] AKZWDWIA Merge lines.love
  • [20] TVCPXAAU rename
  • [21] 73OCE2MC after much struggle, a brute-force undo
  • [22] ZQZX364V use a helper
  • [23] 2L5MEZV3 experiment: new edit namespace
  • [24] OI4FPFIN support drawings in the source editor
  • [25] FS2ITYYH record a known issue
  • [26] QZ2SXLHF some debug prints
  • [27] QFTLLRGL indent
  • [28] TCWGD2RK delete a duplicate filename candidate
  • [29] X3CQLBTR set window title within each app
  • [30] AYX33NBC Merge lines.love
  • [31] O2PYNFUB use existing local
  • [32] SDRXK4X5 move
  • [33] GUOQRUL7 Merge lines.love
  • [34] VHQCNMAR several more modules
  • [35] 4VQGE7RA new test
  • [36] KKMFQDR4 editing source code from within the app
  • [37] 6DYSB5DY bugfix: perform matches in the right order
  • [38] 2CTN2IEF Merge lines.love
  • [39] ENENSZLK bugfix: source margins when toggling log browser
  • [40] D2GCFTTT clean up repl functionality
  • [41] OTIBCAUJ love2d scaffold
  • [42] LXTTOB33 extract a couple of files
  • [43] 4YDBYBA4 clean up memory leak experiments
  • [44] JMUD7T3O get rid of ugly side-effects in tests
  • [45] AVTNUQYR basic test-enabled framework
  • [46] CE4LZV4T drop last couple of manual tests
  • [47] R5QXEHUI somebody stop me
  • [48] ME7WBLF5 bugfix: log filenames can have 2 formats
  • [49] TLOAPLBJ add a license
  • [50] 3QNOKBFM beginnings of a test harness
  • [51] MD3W5IRA new fork: rip out drawing support
  • [52] C3NYQP57 Merge lines.love
  • [53] 32V6ZHQB Merge lines.love
  • [54] 6LJZN727 handle chords
  • [55] YGCT2D2O start loading settings as applicable
  • [56] D4B52CQ2 Merge lines.love
  • [57] VHUNJHXB Merge lines.love
  • [58] BLWAYPKV extract a module
  • [59] LZYLOTP3 rename
  • [60] CQYKYJJU remember window positions across restart/ctrl+e
  • [61] MDGHRTIF source: up/down in file navigator
  • [62] P3K7UH5C Merge lines.love
  • [63] GQKUD7QX delete some logs
  • [64] JOPVPUSA editing source code from within the app
  • [65] RSZD5A7G forgot to add json.lua
  • [66] KMSL74GA support selections in the source editor
  • [67] XW7ANEJX switch shortcuts for bifold text
  • [68] XX7G2FFJ intermingle freehand line drawings with text
  • [69] OGUV4HSA remove some memory leaks from rendered fragments
  • [70] 3PSFWAIL Merge lines.love
  • [71] NYRESFK6 source: show all files in navigator

Change contents

  • file deletion: source_text.lua (----------)source_text.lua (----------)
    [9.2][9.147062:147101](),[9.2][9.147062:147101](),[9.147101][9.83723:83723]()
    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..'$')
    local screen_line_starting_pos = startpos
    Text.compute_fragments(State, line_index)
    local pos = 1
    initialize_color()
    for _, f in ipairs(line_cache.fragments) do
    App.color(Text_color)
    local frag, frag_text = f.data, f.text
    select_color(frag)
    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
    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)
    function Text.draw(State, line_index, y, startpos, startposB, hide_cursor)
    local line = State.lines[line_index]
    local line_cache = State.line_cache[line_index]
    line_cache.starty = y
    line_cache.startpos = startpos
    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
  • file deletion: source_edit.lua (----------)source_edit.lua (----------)
    [9.2][9.165725:165764](),[9.2][9.165725:165764](),[9.165764][9.152440:152440]()
    y, State.screen_bottom1.pos, State.screen_bottom1.posB = Text.draw(State, line_index, y, startpos, startposB, hide_cursor)
    y = y + State.line_height
    --? print('=> y', y)
    elseif line.mode == 'drawing' then
    y = y+Drawing_padding_top
    Drawing.draw(State, line_index, y)
    y = y + Drawing.pixels(line.h, State.width) + Drawing_padding_bottom
    else
    print(line.mode)
    assert(false)
    function edit.draw(State, hide_cursor)
    State.button_handlers = {}
    App.color(Text_color)
  • file deletion: source.lua (----------)source.lua (----------)
    [9.2][9.177652:177686](),[9.2][9.177652:177686](),[9.177686][9.165766:165766]()
    log_new('source')
    edit.draw(Editor_state, --[[hide cursor?]] Show_file_navigator)
    if Show_log_browser_side then
    -- divider
    App.color(Divider_color)
    love.graphics.rectangle('fill', App.screen.width/2-1,Menu_status_bar_height, 3,App.screen.height)
    --
    log_browser.draw(Log_browser_state)
    end
    source.draw_menu_bar()
    if Show_file_navigator then
    textinput_on_file_navigator(t)
    return
    end
    if Focus == 'edit' then
    return edit.textinput(Editor_state, t)
    else
    return log_browser.textinput(Log_browser_state, t)
    end
    end
    function source.keychord_pressed(chord, key)
    Cursor_time = 0 -- ensure cursor is visible immediately after it moves
    --? print('source keychord')
    if Show_file_navigator then
    keychord_pressed_on_file_navigator(chord, key)
    return
    end
    if chord == 'C-l' then
    --? print('C-l')
    Show_log_browser_side = not Show_log_browser_side
    if Show_log_browser_side then
    end
    function source.update(dt)
    Cursor_time = Cursor_time + dt
    if App.mouse_x() < Editor_state.right then
    edit.update(Editor_state, dt)
    elseif Show_log_browser_side then
    log_browser.update(Log_browser_state, dt)
    end
    end
    function source.quit()
    edit.quit(Editor_state)
    log_browser.quit(Log_browser_state)
    -- convert any bifold files here
    end
    function source.convert_bifold_text(infilename, outfilename)
    local contents = love.filesystem.read(infilename)
    contents = contents:gsub('\u{1e}', ';')
    love.filesystem.write(outfilename, contents)
    end
    function source.settings()
    if Current_app == 'source' then
    --? print('reading source window position')
    love.keyboard.setTextInput(true) -- bring up keyboard on touch screen
    love.keyboard.setKeyRepeat(true)
    love.graphics.setBackgroundColor(1,1,1)
    if Settings and Settings.source then
    source.load_settings()
    else
    source.initialize_default_settings()
    end
    source.initialize_edit_side{'run.lua'}
    source.initialize_log_browser_side()
    Menu_status_bar_height = 5 + Editor_state.line_height + 5
    Editor_state.top = Editor_state.top + Menu_status_bar_height
    Log_browser_state.top = Log_browser_state.top + Menu_status_bar_height
    log_render = {}
    File_navigation.candidates = File_navigation.all_candidates -- modified with filter
    filter = '',
    }
    all_candidates = {
    Editor_state = {}
    -- called both in tests and real run
    function source.initialize_globals()
    -- tests currently mostly clear their own state
    Show_log_browser_side = false
    Focus = 'edit'
    Show_file_navigator = false
    File_navigation = {
  • file deletion: run.lua (----------)run.lua (----------)
    [9.2][9.184046:184077](),[9.2][9.184046:184077](),[9.184077][9.178044:178044]()
    log_new('run')
    love.keyboard.setTextInput(true) -- bring up keyboard on touch screen
    love.keyboard.setKeyRepeat(true)
    love.graphics.setBackgroundColor(1,1,1)
    if Settings then
    run.load_settings()
    else
    run.initialize_default_settings()
    end
    if #arg > 0 then
    Editor_state.filename = arg[1]
    load_from_disk(Editor_state)
    Text.redraw_all(Editor_state)
    Editor_state.screen_top1 = {line=1, pos=1}
    Editor_state.cursor1 = {line=1, pos=1}
    edit.fixup_cursor(Editor_state)
    else
    load_from_disk(Editor_state)
    Text.redraw_all(Editor_state)
    if Editor_state.cursor1.line > #Editor_state.lines or Editor_state.lines[Editor_state.cursor1.line].mode ~= 'text' then
    edit.fixup_cursor(Editor_state)
    end
    end
    love.window.setTitle('lines.love - '..Editor_state.filename)
    if #arg > 1 then
    print('ignoring commandline args after '..arg[1])
    end
    if rawget(_G, 'jit') then
    jit.off()
    jit.flush()
    end
    end
    function run.load_settings()
    love.graphics.setFont(love.graphics.newFont(Settings.font_height))
    -- maximize window to determine maximum allowable dimensions
    App.screen.width, App.screen.height, App.screen.flags = love.window.getMode()
    -- set up desired window dimensions
  • file deletion: log_browser.lua (----------)log_browser.lua (----------)
    [9.2][9.203223:203262](),[9.2][9.203223:203262](),[9.203262][9.191782:191782]()
    local rest
    line.filename, line.line_number, rest = line.data:match('%[string "([^:]*)"%]:([^:]*):%s*(.*)')
    if line.filename == nil then
    end
    line.filename = guess_source(line.filename)
    line.line_number = tonumber(line.line_number)
    if line.data:sub(1,1) == '{' then
    local data = json.decode(line.data)
    if log_render[data.name] then
    line.data = data
    end
    line.section_stack = table.shallowcopy(Section_stack)
    elseif line.data:match('\u{250c}') then
    line.section_stack = table.shallowcopy(Section_stack) -- as it is at the beginning
    local section_name = line.data:match('\u{250c}%s*(.*)')
    table.insert(Section_stack, {name=section_name})
    line.section_begin = true
    line.section_name = section_name
    line.data = nil
    elseif line.data:match('\u{2518}') then
    local section_name = line.data:match('\u{2518}%s*(.*)')
    if array.find(Section_stack, function(x) return x.name == section_name end) then
    while table.remove(Section_stack).name ~= section_name do
    --
    end
    line.section_end = true
    line.section_name = section_name
    line.data = nil
    end
    line.section_stack = table.shallowcopy(Section_stack)
    else
    -- string
    line.section_stack = table.shallowcopy(Section_stack)
    end
    else
    line.section_stack = {}
    end
    end
    end
    function table.shallowcopy(x)
    return {unpack(x)}
    end
    function guess_source(filename)
    local possible_source = filename:gsub('%.lua$', '%.splua')
    if file_exists(possible_source) then
    return possible_source
    else
    return filename
    end
    end
    function log_browser.draw(State)
    assert(#State.lines == #State.line_cache)
    local mouse_line_index = log_browser.line_index(State, App.mouse_x(), App.mouse_y())
    local y = State.top
    for line_index = State.screen_top1.line,#State.lines do
    App.color(Text_color)
    local line = State.lines[line_index]
    if y + State.line_height > App.screen.height then break end
    local height = State.line_height
    if should_show(line) then
    local xleft = render_stack_left_margin(State, line_index, line, y)
    local xright = render_stack_right_margin(State, line_index, line, y)
    if line.section_name then
    App.color(Section_border_color)
    local section_text = to_text(line.section_name)
    if line.section_begin then
    local sectiony = y+Section_border_padding_vertical
    love.graphics.line(xleft,sectiony, xleft,y+State.line_height)
    love.graphics.line(xright,sectiony, xright,y+State.line_height)
    love.graphics.line(xleft,sectiony, xleft+50-2,sectiony)
    love.graphics.draw(section_text, xleft+50,y)
    love.graphics.line(xleft+50+App.width(section_text)+2,sectiony, xright,sectiony)
    else assert(line.section_end)
    local sectiony = y+State.line_height-Section_border_padding_vertical
    love.graphics.line(xleft,y, xleft,sectiony)
    love.graphics.line(xright,y, xright,sectiony)
    love.graphics.line(xleft,sectiony, xleft+50-2,sectiony)
    love.graphics.draw(section_text, xleft+50,y)
    love.graphics.line(xleft+50+App.width(section_text)+2,sectiony, xright,sectiony)
    end
    else
    if type(line.data) == 'string' then
    local old_left, old_right = State.left,State.right
    State.left,State.right = xleft,xright
    y = Text.draw(State, line_index, y, --[[startpos]] 1)
    State.left,State.right = old_left,old_right
    else
    height = log_render[line.data.name](line.data, xleft, y, xright-xleft)
    end
    end
    if App.mouse_x() > Log_browser_state.left and line_index == mouse_line_index then
    App.color(Cursor_line_background_color)
    love.graphics.rectangle('fill', xleft,y, xright-xleft, height)
    end
    y = y + height
    end
    end
    end
    function render_stack_left_margin(State, line_index, line, y)
    if line.section_stack == nil then
    -- assertion message
    for k,v in pairs(line) do
    print(k)
    end
    end
    App.color(Section_border_color)
    for i=1,#line.section_stack do
    local x = State.left + (i-1)*Section_border_padding_horizontal
    love.graphics.line(x,y, x,y+log_browser.height(State, line_index))
    if y < 30 then
    love.graphics.print(line.section_stack[i].name, x+State.font_height+5, y+5, --[[vertically]] math.pi/2)
    end
    if y > App.screen.height-log_browser.height(State, line_index) then
    love.graphics.print(line.section_stack[i].name, x+State.font_height+5, App.screen.height-App.width(to_text(line.section_stack[i].name))-5, --[[vertically]] math.pi/2)
    end
    end
    return log_browser.left_margin(State, line)
    end
    function render_stack_right_margin(State, line_index, line, y)
    App.color(Section_border_color)
    for i=1,#line.section_stack do
    local x = State.right - (i-1)*Section_border_padding_horizontal
    love.graphics.line(x,y, x,y+log_browser.height(State, line_index))
    if y < 30 then
    love.graphics.print(line.section_stack[i].name, x, y+5, --[[vertically]] math.pi/2)
    end
    if y > App.screen.height-log_browser.height(State, line_index) then
    love.graphics.print(line.section_stack[i].name, x, App.screen.height-App.width(to_text(line.section_stack[i].name))-5, --[[vertically]] math.pi/2)
    end
    end
    return log_browser.right_margin(State, line)
    end
    function should_show(line)
    -- Show a line if every single section it's in is expanded.
    for i=1,#line.section_stack do
    local section = line.section_stack[i]
    if not section.expanded then
    return false
    end
    end
    return true
    end
    function log_browser.left_margin(State, line)
    return State.left + #line.section_stack*Section_border_padding_horizontal
    end
    function log_browser.right_margin(State, line)
    return State.right - #line.section_stack*Section_border_padding_horizontal
    end
    function log_browser.update(State, dt)
    end
    function log_browser.quit(State)
    end
    function log_browser.mouse_pressed(State, x,y, mouse_button)
    local line_index = log_browser.line_index(State, x,y)
    if line_index == nil then
    -- below lower margin
    return
    end
    -- leave some space to click without focusing
    local line = State.lines[line_index]
    local xleft = log_browser.left_margin(State, line)
    local xright = log_browser.right_margin(State, line)
    if x < xleft or x > xright then
    return
    end
    -- if it's a section begin/end and the section is collapsed, expand it
    -- TODO: how to collapse?
    if line.section_begin or line.section_end then
    -- HACK: get section reference from next/previous line
    local new_section
    if line.section_begin then
    if line_index < #State.lines then
    local next_section_stack = State.lines[line_index+1].section_stack
    if next_section_stack then
    new_section = next_section_stack[#next_section_stack]
    end
    end
    elseif line.section_end then
    if line_index > 1 then
    local previous_section_stack = State.lines[line_index-1].section_stack
    if previous_section_stack then
    new_section = previous_section_stack[#previous_section_stack]
    end
    end
    end
    if new_section and new_section.expanded == nil then
    new_section.expanded = true
    return
    end
    end
    -- open appropriate file in source side
    if line.filename ~= Editor_state.filename then
    source.switch_to_file(line.filename)
    end
    -- set cursor
    Editor_state.cursor1 = {line=line.line_number, pos=1, posB=nil}
    -- make sure it's visible
    -- TODO: handle extremely long lines
    Editor_state.screen_top1.line = math.max(0, Editor_state.cursor1.line-5)
    -- show cursor
    Focus = 'edit'
    -- expand B side
    Editor_state.expanded = true
    end
    function log_browser.line_index(State, mx,my)
    -- duplicate some logic from log_browser.draw
    local y = State.top
    for line_index = State.screen_top1.line,#State.lines do
    local line = State.lines[line_index]
    if should_show(line) then
    y = y + log_browser.height(State, line_index)
    if my < y then
    return line_index
    end
    if y > App.screen.height then break end
    end
    end
    end
    function log_browser.mouse_released(State, x,y, mouse_button)
    end
    function log_browser.textinput(State, t)
    end
    function log_browser.keychord_pressed(State, chord, key)
    -- move
    if chord == 'up' then
    while State.screen_top1.line > 1 do
    State.screen_top1.line = State.screen_top1.line-1
    if should_show(State.lines[State.screen_top1.line]) then
    break
    end
    end
    elseif chord == 'down' then
    while State.screen_top1.line < #State.lines do
    State.screen_top1.line = State.screen_top1.line+1
    if should_show(State.lines[State.screen_top1.line]) then
    break
    end
    end
    elseif chord == 'pageup' then
    local y = 0
    while State.screen_top1.line > 1 and y < App.screen.height - 100 do
    State.screen_top1.line = State.screen_top1.line - 1
    if should_show(State.lines[State.screen_top1.line]) then
    y = y + log_browser.height(State, State.screen_top1.line)
    end
    end
    elseif chord == 'pagedown' then
    local y = 0
    while State.screen_top1.line < #State.lines and y < App.screen.height - 100 do
    if should_show(State.lines[State.screen_top1.line]) then
    y = y + log_browser.height(State, State.screen_top1.line)
    end
    State.screen_top1.line = State.screen_top1.line + 1
    end
    end
    end
    function log_browser.height(State, line_index)
    local line = State.lines[line_index]
    if line.data == nil then
    -- section header
    return State.line_height
    elseif type(line.data) == 'string' then
    return State.line_height
    else
    if line.height == nil then
    --? print('nil line height! rendering off screen to calculate')
    line.height = log_render[line.data.name](line.data, State.left, App.screen.height, State.right-State.left)
    end
    return line.height
    end
    end
    function log_browser.keyreleased(State, key, scancode)
    end
    line.filename, line.line_number, rest = line.data:match('([^:]*):([^:]*):%s*(.*)')
    end
    if rest then
    line.data = rest
  • file deletion: commands.lua (----------)commands.lua (----------)
    [9.2][9.207726:207762](),[9.2][9.207726:207762](),[9.207762][9.204370:204370]()
    App.screen.draw(s_text, Menu_cursor,5)
    App.screen.draw(s_text, x,y)
    x = x + width + 30
    return x,y
    log_end('file index')
    return best_guess
    function textinput_on_file_navigator(t)
    File_navigation.filter = File_navigation.filter..t
    File_navigation.candidates = source.file_navigator_candidates()
    end
    end
    if y == fy then
    log(2, ('%d: correct row; considering %d %s %d %d'):format(y, i, filename, x, width))
    if best_guess == nil then
    log(2, 'nil')
    best_guess = i
    best_guess_x = x
    best_guess_width = width
    elseif math.abs(fx + fwidth/2 - x - width/2) < math.abs(fx + fwidth/2 - best_guess_x - best_guess_width/2) then
    best_guess = i
    best_guess_x = x
    best_guess_width = width
    end
    log(2, ('best guess now %d %s %d %d'):format(best_guess, File_navigation.candidates[best_guess], best_guess_x, best_guess_width))
    end
    x = x + width + 30
    end
    end
    elseif chord == 'down' then
    file_navigator_down()
    elseif chord == 'up' then
    file_navigator_up()
    end
    end
    function log_render.file_navigator_state(o, x,y, w)
    -- duplicate structure of source.draw_file_navigator
    local num_lines = source.num_lines_for_file_navigator(o.files)
    local h = num_lines * Editor_state.line_height
    App.color(Menu_background_color)
    love.graphics.rectangle('fill', x,y, w,h)
    -- compute the x,y,width of the current index (in offsets from top left)
    local x2,y2 = 0,0
    local width = 0
    for i,filename in ipairs(o.files) do
    local filename_text = to_text(filename)
    width = App.width(filename_text)
    if x2 + width > App.screen.width - 5 then
    y2 = y2 + Editor_state.line_height
    x2 = 0
    end
    if i == o.index then
    break
    end
    x2 = x2 + width + 30
    end
    -- figure out how much of the menu to display
    local menu_xmin = math.max(0, x2-w/2)
    local menu_xmax = math.min(App.screen.width, x2+w/2)
    -- now selectively print out entries
    local x3,y3 = 0,y -- x3 is relative, y3 is absolute
    local width = 0
    for i,filename in ipairs(o.files) do
    local filename_text = to_text(filename)
    width = App.width(filename_text)
    if x3 + width > App.screen.width - 5 then
    y3 = y3 + Editor_state.line_height
    x3 = 0
    end
    if i == o.index then
    App.color(Menu_highlight_color)
    love.graphics.rectangle('fill', x + x3-menu_xmin - 5, y3-2, width+5*2, Editor_state.line_height+2*2)
    end
    if x3 >= menu_xmin and x3 + width < menu_xmax then
    App.color(Menu_command_color)
    App.screen.draw(filename_text, x + x3-menu_xmin, y3)
    end
    x3 = x3 + width + 30
    end
    --
    return h+20
    end
    function file_navigator_up()
    local y, x, width = file_coord(File_navigation.index)
    local index = file_index(y-Editor_state.line_height, x, width)
    if index then
    File_navigation.index = index
    end
    end
    function file_navigator_down()
    local y, x, width = file_coord(File_navigation.index)
    local index = file_index(y+Editor_state.line_height, x, width)
    if index then
    File_navigation.index = index
    end
    end
    function file_coord(index)
    local y,x = Menu_status_bar_height, 5
    for i,filename in ipairs(File_navigation.candidates) do
    local width = App.width(to_text(filename))
    if x + width > App.screen.width - 5 then
    y = y + Editor_state.line_height
    x = 5
    end
    if i == index then
    return y, x, width
    end
    x = x + width + 30
    end
    end
    function file_index(fy, fx, fwidth)
    log_start('file index')
    log(2, ('for %d %d %d'):format(fy, fx, fwidth))
    local y,x = Menu_status_bar_height, 5
    local best_guess, best_guess_x, best_guess_width
    for i,filename in ipairs(File_navigation.candidates) do
    local width = App.width(to_text(filename))
    if x + width > App.screen.width - 5 then
    y = y + Editor_state.line_height
    x = 5
    end
    log(2, 'file navigator: '..chord)
    log(2, {name='file_navigator_state', files=File_navigation.candidates, index=File_navigation.index})
    File_navigation.index = 1
    File_navigation.filter = ''
    File_navigation.candidates = File_navigation.all_candidates
    elseif chord == 'backspace' then
    local len = utf8.len(File_navigation.filter)
    local byte_offset = Text.offset(File_navigation.filter, len)
    File_navigation.filter = string.sub(File_navigation.filter, 1, byte_offset-1)
    File_navigation.index = 1
    File_navigation.candidates = source.file_navigator_candidates()
    elseif chord == 'left' then
    if File_navigation.index > 1 then
    File_navigation.index = File_navigation.index-1
    end
    elseif chord == 'right' then
    if File_navigation.index < #File_navigation.candidates then
    File_navigation.index = File_navigation.index+1
    File_navigation.index = 1
    File_navigation.filter = ''
    File_navigation.candidates = File_navigation.all_candidates
    elseif chord == 'return' then
    local candidate = guess_source(File_navigation.candidates[File_navigation.index]..'.lua')
    source.switch_to_file(candidate)
    Show_file_navigator = false
    if chord == 'escape' then
    Show_file_navigator = false
    end
    function keychord_pressed_on_file_navigator(chord, key)
    App.color(Menu_command_color)
    local filter_text = to_text(File_navigation.filter)
    App.screen.draw(filter_text, 5, 5)
    draw_cursor(5 + App.width(filter_text), 5)
    local s_text = to_text(s)
    local width = App.width(s_text)
    if x + width > App.screen.width - 5 then
    y = y + Editor_state.line_height
    x = 5
    end
    button(Editor_state, 'menu', {x=x-5, y=y-2, w=width+5*2, h=Editor_state.line_height+2*2, color=colortable(color),
    onpress1 = function()
    local candidate = guess_source(s..'.lua')
    source.switch_to_file(candidate)
    Show_file_navigator = false
    end
    })
    App.color(Menu_command_color)
    local color = Menu_background_color
    color = Menu_highlight_color
    end
    if cursor_highlight then
    if File_navigation.num_lines == nil then
    end
    App.color(Menu_background_color)
    local x,y = 5, Menu_status_bar_height
    love.graphics.rectangle('fill', 0,Menu_status_bar_height, App.screen.width, File_navigation.num_lines * Editor_state.line_height + --[[highlight padding]] 2)
    File_navigation.num_lines = source.num_lines_for_file_navigator(File_navigation.candidates)
    if Menu_cursor >= App.screen.width - 5 then
    break
    end
    end
    end
    function draw_cursor(x, y)
    -- blink every 0.5s
    if math.floor(Cursor_time*2)%2 == 0 then
    App.color(Cursor_color)
    love.graphics.rectangle('fill', x,y, 3,Editor_state.line_height)
    end
    end
    function source.file_navigator_candidates()
    if File_navigation.filter == '' then
    return File_navigation.all_candidates
    end
    local result = {}
    for _,filename in ipairs(File_navigation.all_candidates) do
    if starts_with(filename, File_navigation.filter) then
    table.insert(result, filename)
    end
    return result
    end
    local result = 1
    local x = 5
    end
    return result
    end
    function add_file_to_menu(x,y, s, cursor_highlight)
    local width = App.width(to_text(filename))
    if x + width > App.screen.width - 5 then
    result = result+1
    x = 5 + width
    else
    x = x + width + 30
    end
    for i,filename in ipairs(candidates) do
    function source.num_lines_for_file_navigator(candidates)
    end
    for i,filename in ipairs(File_navigation.candidates) do
    if filename == 'source' then
    x,y = add_file_to_menu(x,y, filename, i == File_navigation.index)
    App.color(Menu_border_color)
    love.graphics.line(Menu_cursor-10,2, Menu_cursor-10,Menu_status_bar_height-2)
    end
    Menu_cursor = Menu_cursor + width + 30
    end
    function source.draw_file_navigator()
    local s_text = to_text(s)
    local width = App.width(s_text)
    if Menu_cursor + width > App.screen.width - 5 then
    return
    end
    App.color(Menu_command_color)
  • replacement in source_text.lua at line 7
    [9.84099][9.84099:84161]()
    function Text.draw(State, line_index, y, startpos, startposB)
    [9.84099]
    [9.84161]
    function Text.draw(State, line_index, y, startpos, startposB, hide_cursor)
  • replacement in source_text.lua at line 21
    [9.84693][9.84693:84732]()
    if State.search_term == nil then
    [9.84693]
    [9.84732]
    if not hide_cursor and not State.search_term then
  • replacement in source_text.lua at line 67
    [9.86564][9.86564:86601]()
    if State.search_term == nil then
    [9.86564]
    [9.86601]
    if not hide_cursor and not State.search_term then
  • replacement in source_edit.lua at line 127
    [9.18438][9.155413:155439](),[9.155413][9.155413:155439]()
    function edit.draw(State)
    [9.18438]
    [9.155439]
    function edit.draw(State, hide_cursor)
  • replacement in source_edit.lua at line 173
    [9.156405][9.19647:19763]()
    y, State.screen_bottom1.pos, State.screen_bottom1.posB = Text.draw(State, line_index, y, startpos, startposB)
    [9.156405]
    [9.19763]
    y, State.screen_bottom1.pos, State.screen_bottom1.posB = Text.draw(State, line_index, y, startpos, startposB, hide_cursor)
  • edit in source.lua at line 2
    [9.165842]
    [9.165842]
    log_render = {}
  • replacement in source.lua at line 14
    [9.166088][9.166088:166107]()
    candidates = {
    [9.166088]
    [9.166107]
    all_candidates = {
  • edit in source.lua at line 47
    [9.166577]
    [9.166577]
    filter = '',
  • edit in source.lua at line 49
    [9.166581]
    [9.166581]
    File_navigation.candidates = File_navigation.all_candidates -- modified with filter
  • edit in source.lua at line 62
    [9.166829]
    [9.166829]
    log_new('source')
  • replacement in source.lua at line 226
    [9.172910][9.172910:172961]()
    source.draw_menu_bar()
    edit.draw(Editor_state)
    [9.172910]
    [9.172961]
    edit.draw(Editor_state, --[[hide cursor?]] Show_file_navigator)
  • edit in source.lua at line 234
    [9.173192]
    [9.173192]
    source.draw_menu_bar()
  • edit in source.lua at line 311
    [9.175928]
    [9.175928]
    if Show_file_navigator then
    textinput_on_file_navigator(t)
    return
    end
  • edit in source.lua at line 352
    [9.177028][9.177028:177058]()
    File_navigation.index = 1
  • edit in run.lua at line 18
    [9.178452]
    [9.178452]
    log_new('run')
  • edit in main.lua at line 29
    [9.184750][9.29:29]()
  • resurrect zombie in main.lua at line 29
    [9.184929][9.71:92](),[9.184929][9.71:92]()
    log_new('session')
  • resolve order conflict in main.lua at line 29
    [9.184750]
    [9.71]
  • replacement in log_browser.lua at line 30
    [9.192573][9.192573:192680]()
    line.filename, line.line_number, line.data = line.data:match('%[string "([^:]*)"%]:([^:]*):%s*(.*)')
    [9.192573]
    [9.192680]
    local rest
    line.filename, line.line_number, rest = line.data:match('%[string "([^:]*)"%]:([^:]*):%s*(.*)')
    if line.filename == nil then
    line.filename, line.line_number, rest = line.data:match('([^:]*):([^:]*):%s*(.*)')
    end
    if rest then
    line.data = rest
    end
  • replacement in commands.lua at line 45
    [9.205361][9.205361:205499]()
    if Text_cache[s] == nil then
    Text_cache[s] = App.newText(love.graphics.getFont(), s)
    end
    local width = App.width(Text_cache[s])
    [9.205361]
    [9.205499]
    local s_text = to_text(s)
    local width = App.width(s_text)
  • replacement in commands.lua at line 51
    [9.205601][9.205601:205649]()
    App.screen.draw(Text_cache[s], Menu_cursor,5)
    [9.205601]
    [9.205649]
    App.screen.draw(s_text, Menu_cursor,5)
  • replacement in commands.lua at line 56
    [9.205733][9.205733:205816]()
    for i,file in ipairs(File_navigation.candidates) do
    if file == 'source' then
    [9.205733]
    [9.205816]
    App.color(Menu_command_color)
    local filter_text = to_text(File_navigation.filter)
    App.screen.draw(filter_text, 5, 5)
    draw_cursor(5 + App.width(filter_text), 5)
    if File_navigation.num_lines == nil then
    File_navigation.num_lines = source.num_lines_for_file_navigator(File_navigation.candidates)
    end
    App.color(Menu_background_color)
    love.graphics.rectangle('fill', 0,Menu_status_bar_height, App.screen.width, File_navigation.num_lines * Editor_state.line_height + --[[highlight padding]] 2)
    local x,y = 5, Menu_status_bar_height
    for i,filename in ipairs(File_navigation.candidates) do
    if filename == 'source' then
  • replacement in commands.lua at line 71
    [9.205943][9.205943:205998]()
    add_file_to_menu(file, i == File_navigation.index)
    [9.205943]
    [9.205998]
    x,y = add_file_to_menu(x,y, filename, i == File_navigation.index)
    if Menu_cursor >= App.screen.width - 5 then
    break
    end
  • replacement in commands.lua at line 78
    [9.206009][9.206009:206147]()
    function add_file_to_menu(s, cursor_highlight)
    if Text_cache[s] == nil then
    Text_cache[s] = App.newText(love.graphics.getFont(), s)
    [9.206009]
    [9.206147]
    function draw_cursor(x, y)
    -- blink every 0.5s
    if math.floor(Cursor_time*2)%2 == 0 then
    App.color(Cursor_color)
    love.graphics.rectangle('fill', x,y, 3,Editor_state.line_height)
  • replacement in commands.lua at line 84
    [9.206153][9.206153:206258]()
    local width = App.width(Text_cache[s])
    if Menu_cursor + width > App.screen.width - 5 then
    return
    [9.206153]
    [9.206258]
    end
    function source.file_navigator_candidates()
    if File_navigation.filter == '' then
    return File_navigation.all_candidates
    end
    local result = {}
    for _,filename in ipairs(File_navigation.all_candidates) do
    if starts_with(filename, File_navigation.filter) then
    table.insert(result, filename)
    end
    end
    return result
    end
    function source.num_lines_for_file_navigator(candidates)
    local result = 1
    local x = 5
    for i,filename in ipairs(candidates) do
    local width = App.width(to_text(filename))
    if x + width > App.screen.width - 5 then
    result = result+1
    x = 5 + width
    else
    x = x + width + 30
    end
    end
    return result
    end
    function add_file_to_menu(x,y, s, cursor_highlight)
    local s_text = to_text(s)
    local width = App.width(s_text)
    if x + width > App.screen.width - 5 then
    y = y + Editor_state.line_height
    x = 5
  • edit in commands.lua at line 121
    [9.206264]
    [9.206264]
    local color = Menu_background_color
  • replacement in commands.lua at line 123
    [9.206291][9.206291:206441]()
    App.color(Menu_highlight_color)
    love.graphics.rectangle('fill', Menu_cursor-5,5-2, App.width(Text_cache[s])+5*2,Editor_state.line_height+2*2)
    [9.206291]
    [9.206441]
    color = Menu_highlight_color
  • edit in commands.lua at line 125
    [9.206447]
    [9.206447]
    button(Editor_state, 'menu', {x=x-5, y=y-2, w=width+5*2, h=Editor_state.line_height+2*2, color=colortable(color),
    onpress1 = function()
    local candidate = guess_source(s..'.lua')
    source.switch_to_file(candidate)
    Show_file_navigator = false
    end
    })
  • replacement in commands.lua at line 133
    [9.206479][9.206479:206568]()
    App.screen.draw(Text_cache[s], Menu_cursor,5)
    Menu_cursor = Menu_cursor + width + 30
    [9.206479]
    [9.206568]
    App.screen.draw(s_text, x,y)
    x = x + width + 30
    return x,y
  • edit in commands.lua at line 139
    [9.206629]
    [9.206629]
    log(2, 'file navigator: '..chord)
    log(2, {name='file_navigator_state', files=File_navigation.candidates, index=File_navigation.index})
  • edit in commands.lua at line 143
    [9.206689]
    [9.206689]
    File_navigation.index = 1
    File_navigation.filter = ''
    File_navigation.candidates = File_navigation.all_candidates
  • edit in commands.lua at line 150
    [9.206884]
    [9.206884]
    File_navigation.index = 1
    File_navigation.filter = ''
    File_navigation.candidates = File_navigation.all_candidates
    elseif chord == 'backspace' then
    local len = utf8.len(File_navigation.filter)
    local byte_offset = Text.offset(File_navigation.filter, len)
    File_navigation.filter = string.sub(File_navigation.filter, 1, byte_offset-1)
    File_navigation.index = 1
    File_navigation.candidates = source.file_navigator_candidates()
  • edit in commands.lua at line 167
    [9.207171]
    [9.207171]
    elseif chord == 'down' then
    file_navigator_down()
    elseif chord == 'up' then
    file_navigator_up()
  • edit in commands.lua at line 173
    [9.207181]
    function log_render.file_navigator_state(o, x,y, w)
    -- duplicate structure of source.draw_file_navigator
    local num_lines = source.num_lines_for_file_navigator(o.files)
    local h = num_lines * Editor_state.line_height
    App.color(Menu_background_color)
    love.graphics.rectangle('fill', x,y, w,h)
    -- compute the x,y,width of the current index (in offsets from top left)
    local x2,y2 = 0,0
    local width = 0
    for i,filename in ipairs(o.files) do
    local filename_text = to_text(filename)
    width = App.width(filename_text)
    if x2 + width > App.screen.width - 5 then
    y2 = y2 + Editor_state.line_height
    x2 = 0
    end
    if i == o.index then
    break
    end
    x2 = x2 + width + 30
    end
    -- figure out how much of the menu to display
    local menu_xmin = math.max(0, x2-w/2)
    local menu_xmax = math.min(App.screen.width, x2+w/2)
    -- now selectively print out entries
    local x3,y3 = 0,y -- x3 is relative, y3 is absolute
    local width = 0
    for i,filename in ipairs(o.files) do
    local filename_text = to_text(filename)
    width = App.width(filename_text)
    if x3 + width > App.screen.width - 5 then
    y3 = y3 + Editor_state.line_height
    x3 = 0
    end
    if i == o.index then
    App.color(Menu_highlight_color)
    love.graphics.rectangle('fill', x + x3-menu_xmin - 5, y3-2, width+5*2, Editor_state.line_height+2*2)
    end
    if x3 >= menu_xmin and x3 + width < menu_xmax then
    App.color(Menu_command_color)
    App.screen.draw(filename_text, x + x3-menu_xmin, y3)
    end
    x3 = x3 + width + 30
    end
    --
    return h+20
    end
    function file_navigator_up()
    local y, x, width = file_coord(File_navigation.index)
    local index = file_index(y-Editor_state.line_height, x, width)
    if index then
    File_navigation.index = index
    end
    end
    function file_navigator_down()
    local y, x, width = file_coord(File_navigation.index)
    local index = file_index(y+Editor_state.line_height, x, width)
    if index then
    File_navigation.index = index
    end
    end
    function file_coord(index)
    local y,x = Menu_status_bar_height, 5
    for i,filename in ipairs(File_navigation.candidates) do
    local width = App.width(to_text(filename))
    if x + width > App.screen.width - 5 then
    y = y + Editor_state.line_height
    x = 5
    end
    if i == index then
    return y, x, width
    end
    x = x + width + 30
    end
    end
    function file_index(fy, fx, fwidth)
    log_start('file index')
    log(2, ('for %d %d %d'):format(fy, fx, fwidth))
    local y,x = Menu_status_bar_height, 5
    local best_guess, best_guess_x, best_guess_width
    for i,filename in ipairs(File_navigation.candidates) do
    local width = App.width(to_text(filename))
    if x + width > App.screen.width - 5 then
    y = y + Editor_state.line_height
    x = 5
    end
    if y == fy then
    log(2, ('%d: correct row; considering %d %s %d %d'):format(y, i, filename, x, width))
    if best_guess == nil then
    log(2, 'nil')
    best_guess = i
    best_guess_x = x
    best_guess_width = width
    elseif math.abs(fx + fwidth/2 - x - width/2) < math.abs(fx + fwidth/2 - best_guess_x - best_guess_width/2) then
    best_guess = i
    best_guess_x = x
    best_guess_width = width
    end
    log(2, ('best guess now %d %s %d %d'):format(best_guess, File_navigation.candidates[best_guess], best_guess_x, best_guess_width))
    end
    x = x + width + 30
    end
    log_end('file index')
    return best_guess
    end
    function textinput_on_file_navigator(t)
    File_navigation.filter = File_navigation.filter..t
    File_navigation.candidates = source.file_navigator_candidates()
    end