consistent names in a few more places
[view.love.git] / log_browser.lua
blob76596f256468bfd5ea75d4a15ef459f88f0965cf
1 -- environment for immutable logs
2 -- optionally reads extensions for rendering some types from the source codebase that generated them
3 --
4 -- We won't care too much about long, wrapped lines. If they lines get too
5 -- long to manage, you need a better, graphical rendering for them. Load
6 -- functions to render them into the log_render namespace.
8 function source.initialize_log_browser_side()
9 Log_browser_state = edit.initialize_state(Margin_top, Editor_state.right + Margin_right + Margin_left, (Editor_state.right+Margin_right)*2, Editor_state.font_height, Editor_state.line_height)
10 Log_browser_state.filename = 'log'
11 load_from_disk(Log_browser_state) -- TODO: pay no attention to Fold
12 log_browser.parse(Log_browser_state)
13 Text.redraw_all(Log_browser_state)
14 Log_browser_state.screen_top1 = {line=1, pos=1}
15 Log_browser_state.cursor1 = {line=1, pos=nil}
16 end
18 Section_stack = {}
19 Section_border_color = {r=0.7, g=0.7, b=0.7}
20 Cursor_line_background_color = {r=0.7, g=0.7, b=0, a=0.1}
22 Section_border_padding_horizontal = 30 -- TODO: adjust this based on font height (because we draw text vertically along the borders
23 Section_border_padding_vertical = 15 -- TODO: adjust this based on font height
25 log_browser = {}
27 function log_browser.parse(State)
28 for _,line in ipairs(State.lines) do
29 if line.data ~= '' then
30 local rest
31 line.filename, line.line_number, rest = line.data:match('%[string "([^:]*)"%]:([^:]*):%s*(.*)')
32 if line.filename == nil then
33 line.filename, line.line_number, rest = line.data:match('([^:]*):([^:]*):%s*(.*)')
34 end
35 if rest then
36 line.data = rest
37 end
38 line.filename = guess_source(line.filename)
39 line.line_number = tonumber(line.line_number)
40 if line.data:sub(1,1) == '{' then
41 local data = json.decode(line.data)
42 if log_render[data.name] then
43 line.data = data
44 end
45 line.section_stack = table.shallowcopy(Section_stack)
46 elseif line.data:match('\u{250c}') then
47 line.section_stack = table.shallowcopy(Section_stack) -- as it is at the beginning
48 local section_name = line.data:match('\u{250c}%s*(.*)')
49 table.insert(Section_stack, {name=section_name})
50 line.section_begin = true
51 line.section_name = section_name
52 line.data = nil
53 elseif line.data:match('\u{2518}') then
54 local section_name = line.data:match('\u{2518}%s*(.*)')
55 if array.find(Section_stack, function(x) return x.name == section_name end) then
56 while table.remove(Section_stack).name ~= section_name do
58 end
59 line.section_end = true
60 line.section_name = section_name
61 line.data = nil
62 end
63 line.section_stack = table.shallowcopy(Section_stack)
64 else
65 -- string
66 line.section_stack = table.shallowcopy(Section_stack)
67 end
68 else
69 line.section_stack = {}
70 end
71 end
72 end
74 function table.shallowcopy(x)
75 return {unpack(x)}
76 end
78 function guess_source(filename)
79 local possible_source = filename:gsub('%.lua$', '%.splua')
80 if file_exists(possible_source) then
81 return possible_source
82 else
83 return filename
84 end
85 end
87 function log_browser.draw(State)
88 assert(#State.lines == #State.line_cache)
89 local mouse_line_index = log_browser.line_index(State, App.mouse_x(), App.mouse_y())
90 local y = State.top
91 for line_index = State.screen_top1.line,#State.lines do
92 App.color(Text_color)
93 local line = State.lines[line_index]
94 if y + State.line_height > App.screen.height then break end
95 local height = State.line_height
96 if should_show(line) then
97 local xleft = render_stack_left_margin(State, line_index, line, y)
98 local xright = render_stack_right_margin(State, line_index, line, y)
99 if line.section_name then
100 App.color(Section_border_color)
101 local section_text = to_text(line.section_name)
102 if line.section_begin then
103 local sectiony = y+Section_border_padding_vertical
104 love.graphics.line(xleft,sectiony, xleft,y+State.line_height)
105 love.graphics.line(xright,sectiony, xright,y+State.line_height)
106 love.graphics.line(xleft,sectiony, xleft+50-2,sectiony)
107 love.graphics.draw(section_text, xleft+50,y)
108 love.graphics.line(xleft+50+App.width(section_text)+2,sectiony, xright,sectiony)
109 else assert(line.section_end)
110 local sectiony = y+State.line_height-Section_border_padding_vertical
111 love.graphics.line(xleft,y, xleft,sectiony)
112 love.graphics.line(xright,y, xright,sectiony)
113 love.graphics.line(xleft,sectiony, xleft+50-2,sectiony)
114 love.graphics.draw(section_text, xleft+50,y)
115 love.graphics.line(xleft+50+App.width(section_text)+2,sectiony, xright,sectiony)
117 else
118 if type(line.data) == 'string' then
119 local old_left, old_right = State.left,State.right
120 State.left,State.right = xleft,xright
121 y = Text.draw(State, line_index, y, --[[startpos]] 1)
122 State.left,State.right = old_left,old_right
123 else
124 height = log_render[line.data.name](line.data, xleft, y, xright-xleft)
127 if App.mouse_x() > Log_browser_state.left and line_index == mouse_line_index then
128 App.color(Cursor_line_background_color)
129 love.graphics.rectangle('fill', xleft,y, xright-xleft, height)
131 y = y + height
136 function render_stack_left_margin(State, line_index, line, y)
137 if line.section_stack == nil then
138 -- assertion message
139 for k,v in pairs(line) do
140 print(k)
143 App.color(Section_border_color)
144 for i=1,#line.section_stack do
145 local x = State.left + (i-1)*Section_border_padding_horizontal
146 love.graphics.line(x,y, x,y+log_browser.height(State, line_index))
147 if y < 30 then
148 love.graphics.print(line.section_stack[i].name, x+State.font_height+5, y+5, --[[vertically]] math.pi/2)
150 if y > App.screen.height-log_browser.height(State, line_index) then
151 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)
154 return log_browser.left_margin(State, line)
157 function render_stack_right_margin(State, line_index, line, y)
158 App.color(Section_border_color)
159 for i=1,#line.section_stack do
160 local x = State.right - (i-1)*Section_border_padding_horizontal
161 love.graphics.line(x,y, x,y+log_browser.height(State, line_index))
162 if y < 30 then
163 love.graphics.print(line.section_stack[i].name, x, y+5, --[[vertically]] math.pi/2)
165 if y > App.screen.height-log_browser.height(State, line_index) then
166 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)
169 return log_browser.right_margin(State, line)
172 function should_show(line)
173 -- Show a line if every single section it's in is expanded.
174 for i=1,#line.section_stack do
175 local section = line.section_stack[i]
176 if not section.expanded then
177 return false
180 return true
183 function log_browser.left_margin(State, line)
184 return State.left + #line.section_stack*Section_border_padding_horizontal
187 function log_browser.right_margin(State, line)
188 return State.right - #line.section_stack*Section_border_padding_horizontal
191 function log_browser.update(State, dt)
194 function log_browser.quit(State)
197 function log_browser.mouse_press(State, x,y, mouse_button)
198 local line_index = log_browser.line_index(State, x,y)
199 if line_index == nil then
200 -- below lower margin
201 return
203 -- leave some space to click without focusing
204 local line = State.lines[line_index]
205 local xleft = log_browser.left_margin(State, line)
206 local xright = log_browser.right_margin(State, line)
207 if x < xleft or x > xright then
208 return
210 -- if it's a section begin/end and the section is collapsed, expand it
211 -- TODO: how to collapse?
212 if line.section_begin or line.section_end then
213 -- HACK: get section reference from next/previous line
214 local new_section
215 if line.section_begin then
216 if line_index < #State.lines then
217 local next_section_stack = State.lines[line_index+1].section_stack
218 if next_section_stack then
219 new_section = next_section_stack[#next_section_stack]
222 elseif line.section_end then
223 if line_index > 1 then
224 local previous_section_stack = State.lines[line_index-1].section_stack
225 if previous_section_stack then
226 new_section = previous_section_stack[#previous_section_stack]
230 if new_section and new_section.expanded == nil then
231 new_section.expanded = true
232 return
235 -- open appropriate file in source side
236 if line.filename ~= Editor_state.filename then
237 source.switch_to_file(line.filename)
239 -- set cursor
240 Editor_state.cursor1 = {line=line.line_number, pos=1, posB=nil}
241 -- make sure it's visible
242 -- TODO: handle extremely long lines
243 Editor_state.screen_top1.line = math.max(0, Editor_state.cursor1.line-5)
244 -- show cursor
245 Focus = 'edit'
246 -- expand B side
247 Editor_state.expanded = true
250 function log_browser.line_index(State, mx,my)
251 -- duplicate some logic from log_browser.draw
252 local y = State.top
253 for line_index = State.screen_top1.line,#State.lines do
254 local line = State.lines[line_index]
255 if should_show(line) then
256 y = y + log_browser.height(State, line_index)
257 if my < y then
258 return line_index
260 if y > App.screen.height then break end
265 function log_browser.mouse_release(State, x,y, mouse_button)
268 function log_browser.text_input(State, t)
271 function log_browser.keychord_press(State, chord, key)
272 -- move
273 if chord == 'up' then
274 while State.screen_top1.line > 1 do
275 State.screen_top1.line = State.screen_top1.line-1
276 if should_show(State.lines[State.screen_top1.line]) then
277 break
280 elseif chord == 'down' then
281 while State.screen_top1.line < #State.lines do
282 State.screen_top1.line = State.screen_top1.line+1
283 if should_show(State.lines[State.screen_top1.line]) then
284 break
287 elseif chord == 'pageup' then
288 local y = 0
289 while State.screen_top1.line > 1 and y < App.screen.height - 100 do
290 State.screen_top1.line = State.screen_top1.line - 1
291 if should_show(State.lines[State.screen_top1.line]) then
292 y = y + log_browser.height(State, State.screen_top1.line)
295 elseif chord == 'pagedown' then
296 local y = 0
297 while State.screen_top1.line < #State.lines and y < App.screen.height - 100 do
298 if should_show(State.lines[State.screen_top1.line]) then
299 y = y + log_browser.height(State, State.screen_top1.line)
301 State.screen_top1.line = State.screen_top1.line + 1
306 function log_browser.height(State, line_index)
307 local line = State.lines[line_index]
308 if line.data == nil then
309 -- section header
310 return State.line_height
311 elseif type(line.data) == 'string' then
312 return State.line_height
313 else
314 if line.height == nil then
315 --? print('nil line height! rendering off screen to calculate')
316 line.height = log_render[line.data.name](line.data, State.left, App.screen.height, State.right-State.left)
318 return line.height
322 function log_browser.key_release(State, key, scancode)