7P6XKA2DCFG5YJAVTCAZMQNNDJ6K6OO5AILBBCVYV37XKZNYKHPAC A5ZROUM4HAX4DI6PKZF4XTS362MIJ52OOQRZVYJWGIVOH5XLZREAC N7VXEGLGP4T4BQXWBTTSWKA4BUM72DV4QVNFSETCJIF2QIG4BPHAC BYKXF3YYVPNVVMZVEQ72TK5WQ757IJZ6TUYPCHSVEGOBF6QIV3RAC D3FLL7SL45CAFMGVOVABX75JBG4LGL4O35R5AIF3INW7N4VUP4DAC 3QWK3GSA5KTVQJKXQ65OGZA2AORHFALLBXWP2A4MN6YDE7VV4PUAC B4JEWKWIFIOJ7MIWNDPR3L34NIH4WORBELAWFOPAMWL32SVIJ4YQC RQUVBX627HPVMS77HCERQGTGFNP6JXSBBAZNR2PTNT6B7LRRGQIAC R5QXEHUIZLELJGGCZAE7ATNS3CLRJ7JFRENMGH4XXH24C5WABZDQC KKMFQDR43ZWVCDRHQLWWX3FCWCFA3ZSXYOBRJNPHUQZR2XPKWULAC C7CQOQ6ZDF3O66KAPNWO4QZWXGCYY6VKU3J7RUQKQTF46JTATRGAC 34QHL4KCLCWXMXY7CBWZIEMKL6BX62DACWANNPQPYUHSV2INSE2QC OI4FPFINEROK6GNDEMOBTGSPYIULCLRGGT5W3H7VLM7VFH22GMWQC QZ2SXLHF6G3RBSLFIOYM3AQYWLGPWRTASNNQMOQBI5ASUAYCO6UAC MFZW24ANL4FHCSUNNY2DQFMKY7CE73F4WL2MGSSU7UKLHVXNRAEAC D2KRR2KTJJQCVGQYIEJDLV4NKMXBSNN6WOB62BVZ67KKMBRITD7AC GBSRQUT4QF5WCFVSTGSOU3BM6VCGPNBBG5WKDEGDGGCOUWTPEC2AC 6DYSB5DYWZJ2MRV3O7EZHMDWJFD5QN2TECWJFSRQESMKFDXSPLCAC ME7WBLF56X2WYKJODY47NDN4FELKIL5MYRT2MXCMZZLFKHOJMC2QC ZQZX364VYRZOGYDPN25FIQLCKIAK2DJ2U6FJUP54X72HXQAGG5VQC NYRESFK6T6NNVQRPVQEJP37Z4XUOUDBQWWU7DMQ23Y3GX7U2A4XQC MDGHRTIFMMWBQZPIUCPE6ZM65Z4UOEQOHYDGH6J3M7MNQ6DCMR4AC O2PYNFUBF2IQQIJJSCSKKRRTV6WMRVYAILJXMEQWFFJ5WQJNZTNQC LZYLOTP343AXV7GSVRCCS6Z5XNS6BXFPTCOPRL47LC6LYPT5BUWAC IOCATA27VTQIYYFV5HZBKDKKYOSEIJ5SS742L3SUUCJXR26PXTGAC QFTLLRGLZ2LV4CWD23WKPM4NU4HU7NX4P73BN2EFHVVQEJUXH74AC JOPVPUSAMMU6RFVDQR4NJC4GNNUFB7GPKVH7OS5FKCYS5QZ53VLQC QEXZHD2VPCM4TAPP7PR2K2PIR4BVES5IZWC3T6ZRNJWKWOXFILNQC OTIBCAUJ3KDQJLVDN3A536DLZGNRYMGJLORZVR3WLCGXGO6UGO6AC function Text.draw(State, line_index, y, startpos, startposB, hide_cursor)if not hide_cursor and not State.search_term thenif 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) endif line_index == State.cursor1.line and State.cursor1.posB == pos thenText.draw_cursor(State, x, y)endendendreturn y, nil, screen_line_starting_posend-- 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 drawnfunction 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 = startposText.compute_fragments(State, line_index)local pos = 1initialize_color()for _, f in ipairs(line_cache.fragments) doApp.color(Text_color)local frag, frag_text = f.data, f.textselect_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 fragmentlocal frag_width = App.width(frag_text)if x + frag_width > State.right thenassert(x > State.left) -- no overfull linesy = y + State.line_heightif y + State.line_height > App.screen.height thenreturn --[[screen filled]] true, x,y, pos, screen_line_starting_posendscreen_line_starting_pos = posx = State.leftif line_index == State.cursor1.line and State.cursor1.pos == pos thenText.draw_cursor(State, x, y)endendendelsex = State.leftend-- check for B side--? if line_index == 8 then print('checking for B side') endif line.dataB == nil thenassert(y)assert(screen_line_starting_pos)--? if line_index == 8 then print('return 1') endreturn y, screen_line_starting_posendif not State.expanded and not line.expanded thenassert(y)assert(screen_line_starting_pos)--? if line_index == 8 then print('return 2') endbutton(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 = trueend,})return y, screen_line_starting_posend-- draw B side--? if line_index == 8 then print('drawing B side') endApp.color(Fold_color)local line = State.lines[line_index]local line_cache = State.line_cache[line_index]line_cache.starty = yline_cache.startpos = startposline_cache.startposB = startposB-- draw A sidelocal overflows_screen, x, pos, screen_line_starting_posif startpos thenoverflows_screen, x, y, pos, screen_line_starting_pos = Text.draw_wrapping_line(State, line_index, State.left, y, startpos)if overflows_screen thenreturn y, screen_line_starting_posendif Focus == 'edit' and State.cursor1.pos then
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' theny = y+Drawing_padding_topDrawing.draw(State, line_index, y)y = y + Drawing.pixels(line.h, State.width) + Drawing_padding_bottomelseprint(line.mode)assert(false)function edit.draw(State, hide_cursor)State.button_handlers = {}App.color(Text_color)
log_new('source')edit.draw(Editor_state, --[[hide cursor?]] Show_file_navigator)if Show_log_browser_side then-- dividerApp.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)endsource.draw_menu_bar()if Show_file_navigator thentextinput_on_file_navigator(t)returnendif Focus == 'edit' thenreturn edit.textinput(Editor_state, t)elsereturn log_browser.textinput(Log_browser_state, t)endendfunction source.keychord_pressed(chord, key)Cursor_time = 0 -- ensure cursor is visible immediately after it moves--? print('source keychord')if Show_file_navigator thenkeychord_pressed_on_file_navigator(chord, key)returnendif chord == 'C-l' then--? print('C-l')Show_log_browser_side = not Show_log_browser_sideif Show_log_browser_side thenendfunction source.update(dt)Cursor_time = Cursor_time + dtif App.mouse_x() < Editor_state.right thenedit.update(Editor_state, dt)elseif Show_log_browser_side thenlog_browser.update(Log_browser_state, dt)endendfunction source.quit()edit.quit(Editor_state)log_browser.quit(Log_browser_state)-- convert any bifold files hereendfunction source.convert_bifold_text(infilename, outfilename)local contents = love.filesystem.read(infilename)contents = contents:gsub('\u{1e}', ';')love.filesystem.write(outfilename, contents)endfunction source.settings()if Current_app == 'source' then--? print('reading source window position')love.keyboard.setTextInput(true) -- bring up keyboard on touch screenlove.keyboard.setKeyRepeat(true)love.graphics.setBackgroundColor(1,1,1)if Settings and Settings.source thensource.load_settings()elsesource.initialize_default_settings()endsource.initialize_edit_side{'run.lua'}source.initialize_log_browser_side()Menu_status_bar_height = 5 + Editor_state.line_height + 5Editor_state.top = Editor_state.top + Menu_status_bar_heightLog_browser_state.top = Log_browser_state.top + Menu_status_bar_heightlog_render = {}filter = '',File_navigation.candidates = File_navigation.all_candidates -- modified with filter}Editor_state = {}-- called both in tests and real runfunction source.initialize_globals()-- tests currently mostly clear their own stateShow_log_browser_side = falseFocus = 'edit'Show_file_navigator = falseFile_navigation = {all_candidates = {
log_new('run')love.keyboard.setTextInput(true) -- bring up keyboard on touch screenlove.keyboard.setKeyRepeat(true)love.graphics.setBackgroundColor(1,1,1)if Settings thenrun.load_settings()elserun.initialize_default_settings()endif #arg > 0 thenEditor_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)elseload_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' thenedit.fixup_cursor(Editor_state)endendlove.window.setTitle('lines.love - '..Editor_state.filename)if #arg > 1 thenprint('ignoring commandline args after '..arg[1])endif rawget(_G, 'jit') thenjit.off()jit.flush()endendfunction run.load_settings()love.graphics.setFont(love.graphics.newFont(Settings.font_height))-- maximize window to determine maximum allowable dimensionsApp.screen.width, App.screen.height, App.screen.flags = love.window.getMode()-- set up desired window dimensions
local restline.filename, line.line_number, rest = line.data:match('%[string "([^:]*)"%]:([^:]*):%s*(.*)')if line.filename == nil thenendline.filename = guess_source(line.filename)line.line_number = tonumber(line.line_number)if line.data:sub(1,1) == '{' thenlocal data = json.decode(line.data)if log_render[data.name] thenline.data = dataendline.section_stack = table.shallowcopy(Section_stack)elseif line.data:match('\u{250c}') thenline.section_stack = table.shallowcopy(Section_stack) -- as it is at the beginninglocal section_name = line.data:match('\u{250c}%s*(.*)')table.insert(Section_stack, {name=section_name})line.section_begin = trueline.section_name = section_nameline.data = nilelseif line.data:match('\u{2518}') thenlocal section_name = line.data:match('\u{2518}%s*(.*)')if array.find(Section_stack, function(x) return x.name == section_name end) thenwhile table.remove(Section_stack).name ~= section_name do--endline.section_end = trueline.section_name = section_nameline.data = nilendline.section_stack = table.shallowcopy(Section_stack)else-- stringline.section_stack = table.shallowcopy(Section_stack)endelseline.section_stack = {}endendendfunction table.shallowcopy(x)return {unpack(x)}endfunction guess_source(filename)local possible_source = filename:gsub('%.lua$', '%.splua')if file_exists(possible_source) thenreturn possible_sourceelsereturn filenameendendfunction 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.topfor line_index = State.screen_top1.line,#State.lines doApp.color(Text_color)local line = State.lines[line_index]if y + State.line_height > App.screen.height then break endlocal height = State.line_heightif should_show(line) thenlocal 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 thenApp.color(Section_border_color)local section_text = to_text(line.section_name)if line.section_begin thenlocal sectiony = y+Section_border_padding_verticallove.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_verticallove.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)endelseif type(line.data) == 'string' thenlocal old_left, old_right = State.left,State.rightState.left,State.right = xleft,xrighty = Text.draw(State, line_index, y, --[[startpos]] 1)State.left,State.right = old_left,old_rightelseheight = log_render[line.data.name](line.data, xleft, y, xright-xleft)endendif App.mouse_x() > Log_browser_state.left and line_index == mouse_line_index thenApp.color(Cursor_line_background_color)love.graphics.rectangle('fill', xleft,y, xright-xleft, height)endy = y + heightendendendfunction render_stack_left_margin(State, line_index, line, y)if line.section_stack == nil then-- assertion messagefor k,v in pairs(line) doprint(k)endendApp.color(Section_border_color)for i=1,#line.section_stack dolocal x = State.left + (i-1)*Section_border_padding_horizontallove.graphics.line(x,y, x,y+log_browser.height(State, line_index))if y < 30 thenlove.graphics.print(line.section_stack[i].name, x+State.font_height+5, y+5, --[[vertically]] math.pi/2)endif y > App.screen.height-log_browser.height(State, line_index) thenlove.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)endendreturn log_browser.left_margin(State, line)endfunction render_stack_right_margin(State, line_index, line, y)App.color(Section_border_color)for i=1,#line.section_stack dolocal x = State.right - (i-1)*Section_border_padding_horizontallove.graphics.line(x,y, x,y+log_browser.height(State, line_index))if y < 30 thenlove.graphics.print(line.section_stack[i].name, x, y+5, --[[vertically]] math.pi/2)endif y > App.screen.height-log_browser.height(State, line_index) thenlove.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)endendreturn log_browser.right_margin(State, line)endfunction should_show(line)-- Show a line if every single section it's in is expanded.for i=1,#line.section_stack dolocal section = line.section_stack[i]if not section.expanded thenreturn falseendendreturn trueendfunction log_browser.left_margin(State, line)return State.left + #line.section_stack*Section_border_padding_horizontalendfunction log_browser.right_margin(State, line)return State.right - #line.section_stack*Section_border_padding_horizontalendfunction log_browser.update(State, dt)endfunction log_browser.quit(State)endfunction 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 marginreturnend-- leave some space to click without focusinglocal 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 thenreturnend-- 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 linelocal new_sectionif line.section_begin thenif line_index < #State.lines thenlocal next_section_stack = State.lines[line_index+1].section_stackif next_section_stack thennew_section = next_section_stack[#next_section_stack]endendelseif line.section_end thenif line_index > 1 thenlocal previous_section_stack = State.lines[line_index-1].section_stackif previous_section_stack thennew_section = previous_section_stack[#previous_section_stack]endendendif new_section and new_section.expanded == nil thennew_section.expanded = truereturnendend-- open appropriate file in source sideif line.filename ~= Editor_state.filename thensource.switch_to_file(line.filename)end-- set cursorEditor_state.cursor1 = {line=line.line_number, pos=1, posB=nil}-- make sure it's visible-- TODO: handle extremely long linesEditor_state.screen_top1.line = math.max(0, Editor_state.cursor1.line-5)-- show cursorFocus = 'edit'-- expand B sideEditor_state.expanded = trueendfunction log_browser.line_index(State, mx,my)-- duplicate some logic from log_browser.drawlocal y = State.topfor line_index = State.screen_top1.line,#State.lines dolocal line = State.lines[line_index]if should_show(line) theny = y + log_browser.height(State, line_index)if my < y thenreturn line_indexendif y > App.screen.height then break endendendendfunction log_browser.mouse_released(State, x,y, mouse_button)endfunction log_browser.textinput(State, t)endfunction log_browser.keychord_pressed(State, chord, key)-- moveif chord == 'up' thenwhile State.screen_top1.line > 1 doState.screen_top1.line = State.screen_top1.line-1if should_show(State.lines[State.screen_top1.line]) thenbreakendendelseif chord == 'down' thenwhile State.screen_top1.line < #State.lines doState.screen_top1.line = State.screen_top1.line+1if should_show(State.lines[State.screen_top1.line]) thenbreakendendelseif chord == 'pageup' thenlocal y = 0while State.screen_top1.line > 1 and y < App.screen.height - 100 doState.screen_top1.line = State.screen_top1.line - 1if should_show(State.lines[State.screen_top1.line]) theny = y + log_browser.height(State, State.screen_top1.line)endendelseif chord == 'pagedown' thenlocal y = 0while State.screen_top1.line < #State.lines and y < App.screen.height - 100 doif should_show(State.lines[State.screen_top1.line]) theny = y + log_browser.height(State, State.screen_top1.line)endState.screen_top1.line = State.screen_top1.line + 1endendendfunction log_browser.height(State, line_index)local line = State.lines[line_index]if line.data == nil then-- section headerreturn State.line_heightelseif type(line.data) == 'string' thenreturn State.line_heightelseif 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)endreturn line.heightendendfunction log_browser.keyreleased(State, key, scancode)endline.filename, line.line_number, rest = line.data:match('([^:]*):([^:]*):%s*(.*)')endif rest thenline.data = rest
local s_text = to_text(s)local width = App.width(s_text)App.screen.draw(s_text, Menu_cursor,5)App.screen.draw(s_text, x,y)x = x + width + 30return x,yendfunction keychord_pressed_on_file_navigator(chord, key)log(2, 'file navigator: '..chord)endelseif chord == 'down' thenfile_navigator_down()elseif chord == 'up' thenfile_navigator_up()endendfunction log_render.file_navigator_state(o, x,y, w)-- duplicate structure of source.draw_file_navigatorlocal num_lines = source.num_lines_for_file_navigator(o.files)local h = num_lines * Editor_state.line_heightApp.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,0local width = 0for i,filename in ipairs(o.files) dolocal filename_text = to_text(filename)width = App.width(filename_text)if x2 + width > App.screen.width - 5 theny2 = y2 + Editor_state.line_heightx2 = 0endif i == o.index thenbreakendx2 = x2 + width + 30end-- figure out how much of the menu to displaylocal menu_xmin = math.max(0, x2-w/2)local menu_xmax = math.min(App.screen.width, x2+w/2)-- now selectively print out entrieslocal x3,y3 = 0,y -- x3 is relative, y3 is absolutelocal width = 0for i,filename in ipairs(o.files) dolocal filename_text = to_text(filename)width = App.width(filename_text)if x3 + width > App.screen.width - 5 theny3 = y3 + Editor_state.line_heightx3 = 0endif i == o.index thenApp.color(Menu_highlight_color)love.graphics.rectangle('fill', x + x3-menu_xmin - 5, y3-2, width+5*2, Editor_state.line_height+2*2)endif x3 >= menu_xmin and x3 + width < menu_xmax thenApp.color(Menu_command_color)App.screen.draw(filename_text, x + x3-menu_xmin, y3)endx3 = x3 + width + 30end--return h+20endfunction 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 thenFile_navigation.index = indexendendfunction 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 thenFile_navigation.index = indexendendfunction file_coord(index)local y,x = Menu_status_bar_height, 5for i,filename in ipairs(File_navigation.candidates) dolocal width = App.width(to_text(filename))if x + width > App.screen.width - 5 theny = y + Editor_state.line_heightx = 5endif i == index thenreturn y, x, widthendx = x + width + 30endendfunction 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, 5local best_guess, best_guess_x, best_guess_widthfor i,filename in ipairs(File_navigation.candidates) dolocal width = App.width(to_text(filename))if x + width > App.screen.width - 5 theny = y + Editor_state.line_heightx = 5if y == fy thenlog(2, ('%d: correct row; considering %d %s %d %d'):format(y, i, filename, x, width))if best_guess == nil thenlog(2, 'nil')best_guess = ibest_guess_x = xbest_guess_width = widthelseif math.abs(fx + fwidth/2 - x - width/2) < math.abs(fx + fwidth/2 - best_guess_x - best_guess_width/2) thenbest_guess = ibest_guess_x = xbest_guess_width = widthendlog(2, ('best guess now %d %s %d %d'):format(best_guess, File_navigation.candidates[best_guess], best_guess_x, best_guess_width))endx = x + width + 30log_end('file index')return best_guessendfunction textinput_on_file_navigator(t)File_navigation.filter = File_navigation.filter..tFile_navigation.candidates = source.file_navigator_candidates()endendendlog(2, {name='file_navigator_state', files=File_navigation.candidates, index=File_navigation.index})if chord == 'escape' thenShow_file_navigator = falseFile_navigation.index = 1File_navigation.filter = ''File_navigation.candidates = File_navigation.all_candidatesFile_navigation.index = 1File_navigation.filter = ''File_navigation.candidates = File_navigation.all_candidateselseif chord == 'backspace' thenlocal 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 = 1File_navigation.candidates = source.file_navigator_candidates()elseif chord == 'left' thenif File_navigation.index > 1 thenFile_navigation.index = File_navigation.index-1endelseif chord == 'right' thenif File_navigation.index < #File_navigation.candidates thenFile_navigation.index = File_navigation.index+1elseif chord == 'return' thenlocal candidate = guess_source(File_navigation.candidates[File_navigation.index]..'.lua')source.switch_to_file(candidate)Show_file_navigator = falselocal s_text = to_text(s)local width = App.width(s_text)if x + width > App.screen.width - 5 theny = y + Editor_state.line_heightx = 5endbutton(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 = falseend})App.color(Menu_command_color)local color = Menu_background_colorif cursor_highlight thencolor = Menu_highlight_colorendif Menu_cursor >= App.screen.width - 5 thenbreakendendendfunction draw_cursor(x, y)-- blink every 0.5sif math.floor(Cursor_time*2)%2 == 0 thenApp.color(Cursor_color)love.graphics.rectangle('fill', x,y, 3,Editor_state.line_height)endendfunction source.file_navigator_candidates()if File_navigation.filter == '' thenreturn File_navigation.all_candidatesendlocal result = {}for _,filename in ipairs(File_navigation.all_candidates) doif starts_with(filename, File_navigation.filter) thentable.insert(result, filename)endendreturn resultendfunction source.num_lines_for_file_navigator(candidates)local result = 1local x = 5endreturn resultendfunction add_file_to_menu(x,y, s, cursor_highlight)local width = App.width(to_text(filename))if x + width > App.screen.width - 5 thenresult = result+1x = 5 + widthelsex = x + width + 30endfor i,filename in ipairs(candidates) doMenu_cursor = Menu_cursor + width + 30endfunction source.draw_file_navigator()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 thenendApp.color(Menu_background_color)local x,y = 5, Menu_status_bar_heightlove.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)for i,filename in ipairs(File_navigation.candidates) doif filename == 'source' thenApp.color(Menu_border_color)love.graphics.line(Menu_cursor-10,2, Menu_cursor-10,Menu_status_bar_height-2)endx,y = add_file_to_menu(x,y, filename, i == File_navigation.index)if Menu_cursor + width > App.screen.width - 5 thenreturnendApp.color(Menu_command_color)
line.filename, line.line_number, line.data = line.data:match('%[string "([^:]*)"%]:([^:]*):%s*(.*)')
local restline.filename, line.line_number, rest = line.data:match('%[string "([^:]*)"%]:([^:]*):%s*(.*)')if line.filename == nil thenline.filename, line.line_number, rest = line.data:match('([^:]*):([^:]*):%s*(.*)')endif rest thenline.data = restend
for i,file in ipairs(File_navigation.candidates) doif file == 'source' then
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 thenFile_navigation.num_lines = source.num_lines_for_file_navigator(File_navigation.candidates)endApp.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_heightfor i,filename in ipairs(File_navigation.candidates) doif filename == 'source' then
function add_file_to_menu(s, cursor_highlight)if Text_cache[s] == nil thenText_cache[s] = App.newText(love.graphics.getFont(), s)
function draw_cursor(x, y)-- blink every 0.5sif math.floor(Cursor_time*2)%2 == 0 thenApp.color(Cursor_color)love.graphics.rectangle('fill', x,y, 3,Editor_state.line_height)
local width = App.width(Text_cache[s])if Menu_cursor + width > App.screen.width - 5 thenreturn
endfunction source.file_navigator_candidates()if File_navigation.filter == '' thenreturn File_navigation.all_candidatesendlocal result = {}for _,filename in ipairs(File_navigation.all_candidates) doif starts_with(filename, File_navigation.filter) thentable.insert(result, filename)endendreturn resultendfunction source.num_lines_for_file_navigator(candidates)local result = 1local x = 5for i,filename in ipairs(candidates) dolocal width = App.width(to_text(filename))if x + width > App.screen.width - 5 thenresult = result+1x = 5 + widthelsex = x + width + 30endendreturn resultendfunction 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 theny = y + Editor_state.line_heightx = 5
File_navigation.index = 1File_navigation.filter = ''File_navigation.candidates = File_navigation.all_candidateselseif chord == 'backspace' thenlocal 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 = 1File_navigation.candidates = source.file_navigator_candidates()
function log_render.file_navigator_state(o, x,y, w)-- duplicate structure of source.draw_file_navigatorlocal num_lines = source.num_lines_for_file_navigator(o.files)local h = num_lines * Editor_state.line_heightApp.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,0local width = 0for i,filename in ipairs(o.files) dolocal filename_text = to_text(filename)width = App.width(filename_text)if x2 + width > App.screen.width - 5 theny2 = y2 + Editor_state.line_heightx2 = 0endif i == o.index thenbreakendx2 = x2 + width + 30end-- figure out how much of the menu to displaylocal menu_xmin = math.max(0, x2-w/2)local menu_xmax = math.min(App.screen.width, x2+w/2)-- now selectively print out entrieslocal x3,y3 = 0,y -- x3 is relative, y3 is absolutelocal width = 0for i,filename in ipairs(o.files) dolocal filename_text = to_text(filename)width = App.width(filename_text)if x3 + width > App.screen.width - 5 theny3 = y3 + Editor_state.line_heightx3 = 0endif i == o.index thenApp.color(Menu_highlight_color)love.graphics.rectangle('fill', x + x3-menu_xmin - 5, y3-2, width+5*2, Editor_state.line_height+2*2)endif x3 >= menu_xmin and x3 + width < menu_xmax thenApp.color(Menu_command_color)App.screen.draw(filename_text, x + x3-menu_xmin, y3)endx3 = x3 + width + 30end--return h+20endfunction 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 thenFile_navigation.index = indexendendfunction 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 thenFile_navigation.index = indexendendfunction file_coord(index)local y,x = Menu_status_bar_height, 5for i,filename in ipairs(File_navigation.candidates) dolocal width = App.width(to_text(filename))if x + width > App.screen.width - 5 theny = y + Editor_state.line_heightx = 5endif i == index thenreturn y, x, widthendx = x + width + 30endendfunction 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, 5local best_guess, best_guess_x, best_guess_widthfor i,filename in ipairs(File_navigation.candidates) dolocal width = App.width(to_text(filename))if x + width > App.screen.width - 5 theny = y + Editor_state.line_heightx = 5endif y == fy thenlog(2, ('%d: correct row; considering %d %s %d %d'):format(y, i, filename, x, width))if best_guess == nil thenlog(2, 'nil')best_guess = ibest_guess_x = xbest_guess_width = widthelseif math.abs(fx + fwidth/2 - x - width/2) < math.abs(fx + fwidth/2 - best_guess_x - best_guess_width/2) thenbest_guess = ibest_guess_x = xbest_guess_width = widthendlog(2, ('best guess now %d %s %d %d'):format(best_guess, File_navigation.candidates[best_guess], best_guess_x, best_guess_width))endx = x + width + 30endlog_end('file index')return best_guessendfunction textinput_on_file_navigator(t)File_navigation.filter = File_navigation.filter..tFile_navigation.candidates = source.file_navigator_candidates()end