Almost working.
This commit is contained in:
parent
f466e0f471
commit
2a1cdf7c89
278
cursed.moon
278
cursed.moon
@ -1,69 +1,71 @@
|
||||
C = require "curses"
|
||||
repr = require 'repr'
|
||||
local REGULAR, INVERTED, HIGHLIGHTED, RED
|
||||
local run_debugger, cursed
|
||||
local REGULAR, INVERTED, HIGHLIGHTED, RED, SCREEN_H, SCREEN_W
|
||||
local run_debugger, cursed, stdscr, main_loop
|
||||
AUTO = -1
|
||||
log = io.open("output.log", "w")
|
||||
|
||||
-- Return the callstack index of the code that actually caused an error and the max index
|
||||
callstack_range = ->
|
||||
min, max = 0, -1
|
||||
for i=1,999 do
|
||||
if debug.getinfo(i,'f').func == run_debugger
|
||||
info = debug.getinfo(i, 'f')
|
||||
if not info
|
||||
error("Could not find info at level #{i}")
|
||||
continue unless info
|
||||
if info.func == main_loop
|
||||
min = i+2
|
||||
break
|
||||
for i=min,999
|
||||
if debug.getinfo(i,'f').func == cursed then
|
||||
info = debug.getinfo(i, 'f')
|
||||
continue unless info
|
||||
if info.func == cursed then
|
||||
max = i-3
|
||||
break
|
||||
return min, max
|
||||
|
||||
alternating_colors = setmetatable({}, {__index:(i)=> if i % 2 == 0 then INVERTED else REGULAR})
|
||||
class Pad
|
||||
new: (@y,@x,@height,@width)=>
|
||||
new: (@y,@x,@height,@width,@lines,@attrs=alternating_colors)=>
|
||||
--log\write("New Pad:\n #{table.concat @lines, "\n "}\n")
|
||||
@offset = 0
|
||||
@selected = nil
|
||||
@chstrs = {}
|
||||
@lines = {}
|
||||
|
||||
@_height = #@lines + 2
|
||||
@_width = 2
|
||||
for x in *@lines do @_width = math.max(@_width, #x+2)
|
||||
|
||||
if @height == AUTO
|
||||
@resize_height = true
|
||||
@height = 2
|
||||
@height = @_height
|
||||
if @width == AUTO
|
||||
@resize_width = true
|
||||
@width = 2
|
||||
@_width, @_height = 2, 2
|
||||
@width = @_width
|
||||
|
||||
--log\write("#lines = #{#@lines}, height = #{@_height}, width = #{@_width}\n")
|
||||
@_pad = C.newpad(@_height, @_width)
|
||||
@_pad\scrollok(true)
|
||||
|
||||
move_to: (y, x)=>
|
||||
--@erase!
|
||||
@y = y
|
||||
@x = x
|
||||
|
||||
@chstrs = {}
|
||||
for i, line in ipairs(@lines)
|
||||
attr = @attrs[i]
|
||||
chstr = C.new_chstr(@width-2)
|
||||
@chstrs[i] = chstr
|
||||
chstr\set_str(0, line, attr)
|
||||
chstr\set_str(#line+0, ' ', attr, chstr\len!-#line)
|
||||
@_pad\mvaddchstr(i-1+1,0+1,chstr)
|
||||
@refresh!
|
||||
if @on_move then @on_move!
|
||||
|
||||
set_size: (h, w)=>
|
||||
@height = h
|
||||
@width = w
|
||||
@refresh!
|
||||
if @on_resize then @on_resize!
|
||||
|
||||
set_internal_size: (h, w)=>
|
||||
@_height = h
|
||||
@_width = w
|
||||
@refresh!
|
||||
if @on_resize then @on_resize!
|
||||
|
||||
select: (i)=>
|
||||
return if i == @selected
|
||||
if i == @selected or #@lines == 0 then return
|
||||
if i != nil
|
||||
i = math.max(1, math.min(#@lines, i))
|
||||
if @selected
|
||||
j = @selected
|
||||
attr = j % 2 == 0 and INVERTED or REGULAR
|
||||
@chstrs[j]\set_str(0, @lines[j], attr)
|
||||
@chstrs[j]\set_str(#@lines[j], ' ', attr, @chstrs[j]\len!-#@lines[j])
|
||||
@chstrs[j]\set_str(0, @lines[j], @attrs[j])
|
||||
@chstrs[j]\set_str(#@lines[j], ' ', @attrs[j], @chstrs[j]\len!-#@lines[j])
|
||||
@_pad\mvaddchstr(j-1+1,0+1,@chstrs[j])
|
||||
|
||||
if i
|
||||
assert(@chstrs[i], "DIDN'T FIND CHSTR: #{i}/#{#@chstrs} (#{#@lines})")
|
||||
@chstrs[i]\set_str(0, @lines[i], HIGHLIGHTED)
|
||||
@chstrs[i]\set_str(#@lines[i], ' ', HIGHLIGHTED, @chstrs[i]\len!-#@lines[i])
|
||||
@_pad\mvaddchstr(i-1+1,0+1,@chstrs[i])
|
||||
@ -83,34 +85,7 @@ class Pad
|
||||
C.ACS_HLINE, C.ACS_HLINE,
|
||||
C.ACS_ULCORNER, C.ACS_URCORNER,
|
||||
C.ACS_LLCORNER, C.ACS_LRCORNER)
|
||||
@_pad\pnoutrefresh(@offset,0,@y,@x,@y+@height,@x+@width)
|
||||
|
||||
set_lines: (lines, attrs)=>
|
||||
attrs = attrs or setmetatable({}, {__index:(i)=> if i % 2 == 0 then INVERTED else REGULAR})
|
||||
@lines = {}
|
||||
@attrs = {}
|
||||
@chstrs = {}
|
||||
max_width = 0
|
||||
for i, line in ipairs(lines)
|
||||
max_width = math.max(max_width, #line)
|
||||
@_width = max_width + 2
|
||||
if @resize_width
|
||||
@width = @_width
|
||||
@_height = #lines + 2
|
||||
if @resize_height
|
||||
@height = @_height
|
||||
@_pad\resize(@_height, @_width)
|
||||
|
||||
for i, line in ipairs(lines)
|
||||
@lines[i] = line
|
||||
attr = attrs[i]
|
||||
@attrs[i] = attr
|
||||
chstr = C.new_chstr(@width-2)
|
||||
@chstrs[i] = chstr
|
||||
chstr\set_str(0, line, attr)
|
||||
chstr\set_str(#line+0, ' ', attr, chstr\len!-#line)
|
||||
@_pad\mvaddchstr(i-1+1,0+1,chstr)
|
||||
@refresh!
|
||||
@_pad\pnoutrefresh(@offset,0,@y,@x,@y+@height+1,@x+@width)
|
||||
|
||||
erase: =>
|
||||
@_pad\erase!
|
||||
@ -133,27 +108,11 @@ class Pad
|
||||
@select(@selected and (@selected + delta) or 1)
|
||||
|
||||
|
||||
run_debugger = (err_msg)->
|
||||
initial_index = 1
|
||||
stdscr = C.initscr!
|
||||
main_loop = (err_msg, stack_index=1, var_index)->
|
||||
SCREEN_H, SCREEN_W = stdscr\getmaxyx!
|
||||
|
||||
C.cbreak!
|
||||
C.echo(false)
|
||||
C.nl(false)
|
||||
C.curs_set(0)
|
||||
C.start_color!
|
||||
C.use_default_colors!
|
||||
|
||||
stdscr\clear!
|
||||
stdscr\refresh!
|
||||
stdscr\keypad(true)
|
||||
|
||||
export REGULAR, INVERTED, HIGHLIGHTED, RED
|
||||
_, REGULAR = C.init_pair(1, -1, -1), C.color_pair(1)
|
||||
_, INVERTED = C.init_pair(2, -1, C.COLOR_BLACK), C.color_pair(2)
|
||||
_, HIGHLIGHTED = C.init_pair(3, C.COLOR_BLACK, C.COLOR_YELLOW), C.color_pair(3)
|
||||
_, RED = C.init_pair(4, C.COLOR_RED, -1), C.color_pair(4) | C.A_BOLD
|
||||
|
||||
stack_names = {}
|
||||
stack_locations = {}
|
||||
@ -162,7 +121,7 @@ run_debugger = (err_msg)->
|
||||
for i=stack_min,stack_max
|
||||
info = debug.getinfo(i)
|
||||
if not info then break
|
||||
table.insert(stack_names, info.name or "???")
|
||||
table.insert(stack_names, info.name or "<unnamed function>")
|
||||
filename = info.short_src..":"..info.currentline
|
||||
table.insert(stack_locations, filename)
|
||||
max_filename = math.max(max_filename, #filename)
|
||||
@ -170,8 +129,6 @@ run_debugger = (err_msg)->
|
||||
for i=1,#stack_names do
|
||||
callstack[i] = stack_locations[i]..(" ")\rep(max_filename-#stack_locations[i]).." | "..stack_names[i].." "
|
||||
|
||||
err_pad = Pad(0,0,AUTO,SCREEN_W)
|
||||
err_pad._pad\attrset(RED)
|
||||
err_msg_lines = {}
|
||||
for line in err_msg\gmatch("[^\n]*")
|
||||
buff = ""
|
||||
@ -182,80 +139,107 @@ run_debugger = (err_msg)->
|
||||
else
|
||||
buff = buff .. word
|
||||
table.insert(err_msg_lines, " "..buff)
|
||||
err_pad\set_lines(err_msg_lines, setmetatable({}, __index:->RED))
|
||||
err_pad = Pad(0,0,AUTO,SCREEN_W, err_msg_lines,setmetatable({}, __index:->RED))
|
||||
err_pad._pad\attrset(RED)
|
||||
|
||||
stack_pad = Pad(err_pad.height,0,AUTO,50)
|
||||
stack_pad\set_lines(callstack)
|
||||
var_names = Pad(err_pad.height,stack_pad.x+stack_pad.width,10,AUTO)
|
||||
var_values = Pad(err_pad.height,var_names.x+var_names.width,10,AUTO)
|
||||
stack_pad = Pad(err_pad.height,0,AUTO,AUTO, callstack)
|
||||
stack_pad\select(stack_index)
|
||||
|
||||
stack_pad.on_resize = =>
|
||||
var_names\erase!
|
||||
var_names.x = self.x+self.width
|
||||
var_names\refresh!
|
||||
var_names\on_resize(var_names.height, var_names.width)
|
||||
|
||||
var_names.on_resize = =>
|
||||
var_values\erase!
|
||||
var_values.x = self.x+self.width
|
||||
var_values.width = SCREEN_W - (self.x+self.width)
|
||||
var_values\refresh!
|
||||
|
||||
stack_pad.on_select = (i)=>
|
||||
var_names\clear!
|
||||
var_values\clear!
|
||||
callstack_min, _ = callstack_range!
|
||||
_var_names, _var_values = {}, {}
|
||||
for loc=1,999
|
||||
name, value = debug.getlocal(callstack_min+i-1, loc)
|
||||
if value == nil then break
|
||||
table.insert(_var_names, tostring(name))
|
||||
if type(value) == 'function'
|
||||
info = debug.getinfo(value, 'nS')
|
||||
--var_values\add_line(("function: %s @ %s:%s")\format(info.name or '???', info.short_src, info.linedefined))
|
||||
table.insert(_var_values, repr(info))
|
||||
else
|
||||
table.insert(_var_values, repr(value))
|
||||
var_names\set_lines(_var_names)
|
||||
var_values\set_lines(_var_values)
|
||||
callstack_min, _ = callstack_range!
|
||||
_var_names, _var_values = {}, {}
|
||||
for loc=1,999
|
||||
name, value = debug.getlocal(callstack_min+stack_index-1, loc)
|
||||
if value == nil then break
|
||||
table.insert(_var_names, tostring(name))
|
||||
if type(value) == 'function'
|
||||
info = debug.getinfo(value, 'nS')
|
||||
--var_values\add_line(("function: %s @ %s:%s")\format(info.name or '???', info.short_src, info.linedefined))
|
||||
table.insert(_var_values, repr(info))
|
||||
else
|
||||
table.insert(_var_values, repr(value))
|
||||
|
||||
stack_pad\select(initial_index)
|
||||
var_names = Pad(err_pad.height,stack_pad.x+stack_pad.width,10,AUTO,_var_names)
|
||||
var_values = Pad(err_pad.height,var_names.x+var_names.width,10,AUTO,_var_values)
|
||||
if var_index and #_var_names > 0
|
||||
var_names\select(var_index)
|
||||
var_values\select(var_index)
|
||||
|
||||
while true
|
||||
C.doupdate!
|
||||
c = stdscr\getch!
|
||||
old_index = index
|
||||
if ({[C.KEY_DOWN]:1, [C.KEY_SF]:1, [("j")\byte!]:1})[c]
|
||||
stack_pad\scroll(1)
|
||||
elseif ({[C.KEY_UP]:1, [C.KEY_SR]:1, [("k")\byte!]:1})[c]
|
||||
stack_pad\scroll(-1)
|
||||
elseif c == ('n')\byte!
|
||||
var_names.offset = var_names.offset + 1
|
||||
var_names\refresh!
|
||||
--var_names\scroll(1)
|
||||
elseif c == ('m')\byte!
|
||||
var_names.offset = var_names.offset - 1
|
||||
var_names\refresh!
|
||||
--var_names\scroll(-1)
|
||||
elseif c == ('J')\byte!
|
||||
stack_pad\scroll(10)
|
||||
elseif c == ('K')\byte!
|
||||
stack_pad\scroll(-10)
|
||||
elseif c == ('o')\byte!
|
||||
file = stack_locations[stack_pad.selected]
|
||||
filename,line_no = file\match("([^:]*):(.*)")
|
||||
-- Launch system editor and then redraw everything
|
||||
C.endwin!
|
||||
os.execute((os.getenv("EDITOR") or "nano").." +"..line_no.." "..filename)
|
||||
initial_index = stack_pad.selected
|
||||
-- TODO: improve this
|
||||
return run_debugger(err_msg)
|
||||
switch c
|
||||
when C.KEY_DOWN, C.KEY_SF, ("j")\byte!
|
||||
if var_index
|
||||
var_names\scroll(1)
|
||||
return main_loop(err_msg,stack_pad.selected,var_names.selected)
|
||||
else
|
||||
stack_pad\scroll(1)
|
||||
return main_loop(err_msg,stack_pad.selected,nil)
|
||||
|
||||
if c == ('q')\byte! or c == ("Q")\byte!
|
||||
break
|
||||
when C.KEY_UP, C.KEY_SR, ("k")\byte!
|
||||
if var_index
|
||||
var_names\scroll(-1)
|
||||
return main_loop(err_msg,stack_pad.selected,var_names.selected)
|
||||
else
|
||||
stack_pad\scroll(-1)
|
||||
return main_loop(err_msg,stack_pad.selected,nil)
|
||||
|
||||
when ('J')\byte!
|
||||
if var_index
|
||||
var_names\scroll(10)
|
||||
return main_loop(err_msg,stack_pad.selected,var_names.selected)
|
||||
else
|
||||
stack_pad\scroll(10)
|
||||
return main_loop(err_msg,stack_pad.selected,nil)
|
||||
|
||||
when ('K')\byte!
|
||||
if var_index
|
||||
var_names\scroll(-10)
|
||||
return main_loop(err_msg,stack_pad.selected,var_names.selected)
|
||||
else
|
||||
stack_pad\scroll(-10)
|
||||
return main_loop(err_msg,stack_pad.selected,nil)
|
||||
|
||||
when C.KEY_RIGHT, ("l")\byte!
|
||||
if var_index == nil
|
||||
return main_loop(err_msg,stack_pad.selected,1)
|
||||
|
||||
when C.KEY_LEFT, ("h")\byte!
|
||||
if var_index != nil
|
||||
return main_loop(err_msg,stack_pad.selected,nil)
|
||||
|
||||
when ('o')\byte!
|
||||
file = stack_locations[stack_pad.selected]
|
||||
filename,line_no = file\match("([^:]*):(.*)")
|
||||
-- Launch system editor and then redraw everything
|
||||
C.endwin!
|
||||
os.execute((os.getenv("EDITOR") or "nano").." +"..line_no.." "..filename)
|
||||
initial_index = stack_pad.selected
|
||||
return main_loop(err_msg,stack_pad.selected,var_index)
|
||||
|
||||
when ('q')\byte!, ("Q")\byte!
|
||||
break
|
||||
|
||||
run_debugger = (err_msg)->
|
||||
export stdscr, SCREEN_H, SCREEN_W
|
||||
stdscr = C.initscr!
|
||||
SCREEN_H, SCREEN_W = stdscr\getmaxyx!
|
||||
|
||||
C.cbreak!
|
||||
C.echo(false)
|
||||
C.nl(false)
|
||||
C.curs_set(0)
|
||||
C.start_color!
|
||||
C.use_default_colors!
|
||||
|
||||
export REGULAR, INVERTED, HIGHLIGHTED, RED
|
||||
_, REGULAR = C.init_pair(1, -1, -1), C.color_pair(1)
|
||||
_, INVERTED = C.init_pair(2, -1, C.COLOR_BLACK), C.color_pair(2)
|
||||
_, HIGHLIGHTED = C.init_pair(3, C.COLOR_BLACK, C.COLOR_YELLOW), C.color_pair(3)
|
||||
_, RED = C.init_pair(4, C.COLOR_RED, -1), C.color_pair(4) | C.A_BOLD
|
||||
|
||||
return main_loop(err_msg)
|
||||
|
||||
C.endwin!
|
||||
return
|
||||
|
||||
cursed = (fn, ...)->
|
||||
-- To display Lua errors, we must close curses to return to
|
||||
|
Loading…
Reference in New Issue
Block a user