From 24f6b908066c286df02df0f1da8ca0593e67141c Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Wed, 21 Mar 2018 15:03:01 -0700 Subject: [PATCH] Rearranging a bit. --- ldt.moon | 526 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 265 insertions(+), 261 deletions(-) diff --git a/ldt.moon b/ldt.moon index ba32c3e..88b5fa9 100644 --- a/ldt.moon +++ b/ldt.moon @@ -1,7 +1,7 @@ C = require "curses" re = require 're' repr = require 'repr' -local run_debugger, guard, stdscr +local ldb, stdscr AUTO = {} log = io.open("output.log", "w") @@ -13,12 +13,12 @@ callstack_range = -> if not info min = i-1 break - if info.func == run_debugger + if info.func == ldb.run_debugger min = i+2 break for i=min,999 info = debug.getinfo(i, 'f') - if not info or info.func == guard + if not info or info.func == ldb.guard max = i-3 break return min, max @@ -199,276 +199,280 @@ line_tables = setmetatable({}, {__index:(filename)=> return line_table }) -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! - - color_index = 0 - existing = {} - make_color = (fg=-1, bg=-1)-> - key = "#{fg},#{bg}" - unless existing[key] - color_index += 1 - C.init_pair(color_index, fg, bg) - existing[key] = C.color_pair(color_index) - return existing[key] - color_lang = re.compile[[ - x <- {| - {:attrs: {| {attr} (" " {attr})* |} :} - / ( - ({:bg: "on " {color} :} / ({:fg: color :} (" on " {:bg: color :})?)) - {:attrs: {| (" " {attr})* |} :}) - |} - attr <- "blink" / "bold" / "dim" / "invis" / "normal" / "protect" / "reverse" / "standout" / "underline" - color <- "black" / "blue" / "cyan" / "green" / "magenta" / "red" / "white" / "yellow" / "default" - ]] - C.COLOR_DEFAULT = -1 - export color - color = (s="default")-> - t = assert(color_lang\match(s), "Invalid color: #{s}") - if t.fg then t.fg = C["COLOR_"..t.fg\upper!] - if t.bg then t.bg = C["COLOR_"..t.bg\upper!] - c = make_color(t.fg, t.bg) - for a in *t.attrs - c |= C["A_"..a\upper!] - return c - - do -- Fullscreen flash - stdscr\wbkgd(color"yellow on red bold") - stdscr\clear! - stdscr\refresh! - lines = wrap_text("ERROR!\n \n "..err_msg.."\n \npress any key...", math.floor(SCREEN_W/2)) - max_line = 0 - for line in *lines do max_line = math.max(max_line, #line) - for i, line in ipairs(lines) - if i == 1 or i == #lines - stdscr\mvaddstr(math.floor(SCREEN_H/2 - #lines/2)+i, math.floor((SCREEN_W-#line)/2), line) - else - stdscr\mvaddstr(math.floor(SCREEN_H/2 - #lines/2)+i, math.floor((SCREEN_W-max_line)/2), line) - stdscr\refresh! - C.doupdate! - stdscr\getch! - - stdscr\wbkgd(color!) - stdscr\clear! - stdscr\refresh! - - pads = {} - - do -- Err pad - err_msg_lines = wrap_text(err_msg, SCREEN_W - 4) - for i,line in ipairs(err_msg_lines) - err_msg_lines[i] = (" ")\rep(2)..line - pads.err = Pad("Error Message", 0,0,AUTO,SCREEN_W, err_msg_lines, (i)=> color("red bold")) - pads.err._frame\attrset(color("red")) - pads.err\refresh! - - stack_locations = {} - do -- Stack pad - stack_names = {} - max_filename, max_fn_name = 0, 0 - stack_min, stack_max = callstack_range! - for i=stack_min,stack_max - info = debug.getinfo(i) - if not info then break - fn_name = info.name or "" - table.insert(stack_names, fn_name) - line = if info.short_src - line_table = line_tables[info.short_src] - if line_table - char = line_table[info.currentline] - line_num = 1 - file = file_cache[info.short_src] - for _ in file\sub(1,char)\gmatch("\n") do line_num += 1 - "#{info.short_src}:#{line_num}" - else - info.short_src..":"..info.currentline - else - "???" - table.insert(stack_locations, line) - max_filename = math.max(max_filename, #line) - max_fn_name = math.max(max_fn_name, #fn_name) - callstack = {} - max_fn_name, max_filename = 0, 0 - for i=1,#stack_names do - fn_name = stack_names[i] - callstack[i] = {fn_name, stack_locations[i]} - max_fn_name = math.max(max_fn_name, #fn_name) - max_filename = math.max(max_filename, #stack_locations[i]) - - stack_h = math.max(#callstack+2, math.floor(2/3*SCREEN_H)) - stack_w = max_fn_name + 1 + max_filename - pads.stack = Pad "(C)allstack",pads.err.height,SCREEN_W-stack_w,stack_h,stack_w, - stack_names, ((i)=> (i == @selected) and color("black on green") or color("green bold")), - stack_locations, ((i)=> (i == @selected) and color("black on cyan") or color("cyan bold")) - - show_src = (filename, line_no)-> - if pads.src - pads.src\erase! - file = file_cache[filename] - if file - src_lines = {} - for line in (file..'\n')\gmatch("([^\n]*)\n") - table.insert src_lines, line - pads.src = NumberedPad "(S)ource Code", pads.err.height,0, - pads.stack.height,pads.stack.x, src_lines, (i)=> - if i == @selected then return color("black on white") - elseif i == line_no then return color("yellow on red bold") - return color("white") - pads.src\select(line_no) - else - lines = {} - for i=1,math.floor(pads.stack.height/2)-1 do table.insert(lines, "") - s = "" - s = (" ")\rep(math.floor((pads.stack.x-2-#s)/2))..s - table.insert(lines, s) - pads.src = Pad "(S)ource Code", pads.err.height,0,pads.stack.height,pads.stack.x,lines, ->color("red") - - show_vars = (stack_index)-> - if pads.vars - pads.vars\erase! - if pads.values - pads.values\erase! - callstack_min, _ = callstack_range! - var_names, 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') - --values\add_line(("function: %s @ %s:%s")\format(info.name or '???', info.short_src, info.linedefined)) - table.insert(values, repr(info)) - else - table.insert(values, repr(value)) - - var_y = pads.stack.y + pads.stack.height - var_x = 0 - --height = math.min(2+#var_names, SCREEN_H-pads.err.height-pads.stack.height) - height = SCREEN_H-(pads.err.height+pads.stack.height) - pads.vars = Pad "(V)ars", var_y,var_x,height,AUTO,var_names, ((i)=> i == @selected and color('reverse') or color()) - - pads.vars.on_select = (var_index)=> - value_x = pads.vars.x+pads.vars.width - value_w = SCREEN_W-(value_x) - -- Show single value: - if var_index - pads.values = Pad "(D)ata",var_y,value_x,pads.vars.height,value_w,wrap_text(values[var_index], value_w-2), (i)=>color() - else - pads.values = Pad "(D)ata",var_y,value_x,pads.vars.height,value_w,{}, (i)=>color() - collectgarbage() - collectgarbage() - - pads.vars\select(1) - - pads.stack.on_select = (stack_index)=> - filename,line_no = pads.stack.columns[2][stack_index]\match("([^:]*):(%d*).*") - --filename, line_no = pads.stack.lines[stack_index]\match("[^|]*| ([^:]*):(%d*).*") - line_no = tonumber(line_no) - show_src(filename, line_no) - show_vars(stack_index) - - pads.stack\select(1) - - selected_pad = nil - select_pad = (pad)-> - if selected_pad != pad - if selected_pad - selected_pad\set_active(false) - selected_pad\refresh! - selected_pad = pad - selected_pad\set_active(true) - selected_pad\refresh! - - select_pad(pads.src) - - while true - for _,p in pairs(pads) - if p.dirty - p\refresh! - C.doupdate! - c = stdscr\getch! - switch c - when C.KEY_DOWN, C.KEY_SF, ("j")\byte! - selected_pad\scroll(1,0) - when ('J')\byte! - selected_pad\scroll(10,0) - - when C.KEY_UP, C.KEY_SR, ("k")\byte! - selected_pad\scroll(-1,0) - when ('K')\byte! - selected_pad\scroll(-10,0) - - when C.KEY_RIGHT, ("l")\byte! - selected_pad\scroll(0,1) - when ("L")\byte! - selected_pad\scroll(0,10) - - when C.KEY_LEFT, ("h")\byte! - selected_pad\scroll(0,-1) - when ("H")\byte! - selected_pad\scroll(0,-10) - - when ('c')\byte! - select_pad(pads.stack) -- (C)allstack - - when ('s')\byte! - select_pad(pads.src) -- (S)ource Code - - when ('v')\byte! - select_pad(pads.vars) -- (V)ars - - when ('d')\byte! - select_pad(pads.values) -- (D)ata - - when ('o')\byte! - file = stack_locations[pads.stack.selected] - filename,line_no = file\match("([^:]*):(.*)") - line_no = tostring(pads.src.selected) - -- Launch system editor and then redraw everything - C.endwin! - os.execute((os.getenv("EDITOR") or "nano").." +"..line_no.." "..filename) - stdscr = C.initscr! - C.cbreak! - C.echo(false) - C.nl(false) - C.curs_set(0) - C.start_color! - C.use_default_colors! - stdscr\clear! - stdscr\refresh! - for _,pad in pairs(pads) do pad\refresh! - - when ('q')\byte!, ("Q")\byte! - pads = {} - C.endwin! - return - - C.endwin! +-- Cleanup curses and print the error to stdout like regular err_hand = (err)-> C.endwin! print "Error in debugger." print(debug.traceback(err, 2)) os.exit(2) -return { +ldb = { + 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! + + color_index = 0 + existing = {} + make_color = (fg=-1, bg=-1)-> + key = "#{fg},#{bg}" + unless existing[key] + color_index += 1 + C.init_pair(color_index, fg, bg) + existing[key] = C.color_pair(color_index) + return existing[key] + color_lang = re.compile[[ + x <- {| + {:attrs: {| {attr} (" " {attr})* |} :} + / ( + ({:bg: "on " {color} :} / ({:fg: color :} (" on " {:bg: color :})?)) + {:attrs: {| (" " {attr})* |} :}) + |} + attr <- "blink" / "bold" / "dim" / "invis" / "normal" / "protect" / "reverse" / "standout" / "underline" + color <- "black" / "blue" / "cyan" / "green" / "magenta" / "red" / "white" / "yellow" / "default" + ]] + C.COLOR_DEFAULT = -1 + export color + color = (s="default")-> + t = assert(color_lang\match(s), "Invalid color: #{s}") + if t.fg then t.fg = C["COLOR_"..t.fg\upper!] + if t.bg then t.bg = C["COLOR_"..t.bg\upper!] + c = make_color(t.fg, t.bg) + for a in *t.attrs + c |= C["A_"..a\upper!] + return c + + do -- Fullscreen flash + stdscr\wbkgd(color"yellow on red bold") + stdscr\clear! + stdscr\refresh! + lines = wrap_text("ERROR!\n \n "..err_msg.."\n \npress any key...", math.floor(SCREEN_W/2)) + max_line = 0 + for line in *lines do max_line = math.max(max_line, #line) + for i, line in ipairs(lines) + if i == 1 or i == #lines + stdscr\mvaddstr(math.floor(SCREEN_H/2 - #lines/2)+i, math.floor((SCREEN_W-#line)/2), line) + else + stdscr\mvaddstr(math.floor(SCREEN_H/2 - #lines/2)+i, math.floor((SCREEN_W-max_line)/2), line) + stdscr\refresh! + C.doupdate! + stdscr\getch! + + stdscr\wbkgd(color!) + stdscr\clear! + stdscr\refresh! + + pads = {} + + do -- Err pad + err_msg_lines = wrap_text(err_msg, SCREEN_W - 4) + for i,line in ipairs(err_msg_lines) + err_msg_lines[i] = (" ")\rep(2)..line + pads.err = Pad("Error Message", 0,0,AUTO,SCREEN_W, err_msg_lines, (i)=> color("red bold")) + pads.err._frame\attrset(color("red")) + pads.err\refresh! + + stack_locations = {} + do -- Stack pad + stack_names = {} + max_filename, max_fn_name = 0, 0 + stack_min, stack_max = callstack_range! + for i=stack_min,stack_max + info = debug.getinfo(i) + if not info then break + fn_name = info.name or "" + table.insert(stack_names, fn_name) + line = if info.short_src + line_table = line_tables[info.short_src] + if line_table + char = line_table[info.currentline] + line_num = 1 + file = file_cache[info.short_src] + for _ in file\sub(1,char)\gmatch("\n") do line_num += 1 + "#{info.short_src}:#{line_num}" + else + info.short_src..":"..info.currentline + else + "???" + table.insert(stack_locations, line) + max_filename = math.max(max_filename, #line) + max_fn_name = math.max(max_fn_name, #fn_name) + callstack = {} + max_fn_name, max_filename = 0, 0 + for i=1,#stack_names do + fn_name = stack_names[i] + callstack[i] = {fn_name, stack_locations[i]} + max_fn_name = math.max(max_fn_name, #fn_name) + max_filename = math.max(max_filename, #stack_locations[i]) + + stack_h = math.max(#callstack+2, math.floor(2/3*SCREEN_H)) + stack_w = max_fn_name + 1 + max_filename + pads.stack = Pad "(C)allstack",pads.err.height,SCREEN_W-stack_w,stack_h,stack_w, + stack_names, ((i)=> (i == @selected) and color("black on green") or color("green bold")), + stack_locations, ((i)=> (i == @selected) and color("black on cyan") or color("cyan bold")) + + show_src = (filename, line_no)-> + if pads.src + pads.src\erase! + file = file_cache[filename] + if file + src_lines = {} + for line in (file..'\n')\gmatch("([^\n]*)\n") + table.insert src_lines, line + pads.src = NumberedPad "(S)ource Code", pads.err.height,0, + pads.stack.height,pads.stack.x, src_lines, (i)=> + if i == line_no and i == @selected then return color("yellow on red bold") + elseif i == @selected then return color("black on white") + elseif i == line_no then return color("red on black bold") + return color("white") + pads.src\select(line_no) + else + lines = {} + for i=1,math.floor(pads.stack.height/2)-1 do table.insert(lines, "") + s = "" + s = (" ")\rep(math.floor((pads.stack.x-2-#s)/2))..s + table.insert(lines, s) + pads.src = Pad "(S)ource Code", pads.err.height,0,pads.stack.height,pads.stack.x,lines, ->color("red") + + show_vars = (stack_index)-> + if pads.vars + pads.vars\erase! + if pads.values + pads.values\erase! + callstack_min, _ = callstack_range! + var_names, 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') + --values\add_line(("function: %s @ %s:%s")\format(info.name or '???', info.short_src, info.linedefined)) + table.insert(values, repr(info)) + else + table.insert(values, repr(value)) + + var_y = pads.stack.y + pads.stack.height + var_x = 0 + --height = math.min(2+#var_names, SCREEN_H-pads.err.height-pads.stack.height) + height = SCREEN_H-(pads.err.height+pads.stack.height) + pads.vars = Pad "(V)ars", var_y,var_x,height,AUTO,var_names, ((i)=> i == @selected and color('reverse') or color()) + + pads.vars.on_select = (var_index)=> + value_x = pads.vars.x+pads.vars.width + value_w = SCREEN_W-(value_x) + -- Show single value: + if var_index + pads.values = Pad "(D)ata",var_y,value_x,pads.vars.height,value_w,wrap_text(values[var_index], value_w-2), (i)=>color() + else + pads.values = Pad "(D)ata",var_y,value_x,pads.vars.height,value_w,{}, (i)=>color() + collectgarbage() + collectgarbage() + + pads.vars\select(1) + + pads.stack.on_select = (stack_index)=> + filename,line_no = pads.stack.columns[2][stack_index]\match("([^:]*):(%d*).*") + --filename, line_no = pads.stack.lines[stack_index]\match("[^|]*| ([^:]*):(%d*).*") + line_no = tonumber(line_no) + show_src(filename, line_no) + show_vars(stack_index) + + pads.stack\select(1) + + selected_pad = nil + select_pad = (pad)-> + if selected_pad != pad + if selected_pad + selected_pad\set_active(false) + selected_pad\refresh! + selected_pad = pad + selected_pad\set_active(true) + selected_pad\refresh! + + select_pad(pads.src) + + while true + for _,p in pairs(pads) + if p.dirty + p\refresh! + C.doupdate! + c = stdscr\getch! + switch c + when C.KEY_DOWN, C.KEY_SF, ("j")\byte! + selected_pad\scroll(1,0) + when ('J')\byte! + selected_pad\scroll(10,0) + + when C.KEY_UP, C.KEY_SR, ("k")\byte! + selected_pad\scroll(-1,0) + when ('K')\byte! + selected_pad\scroll(-10,0) + + when C.KEY_RIGHT, ("l")\byte! + selected_pad\scroll(0,1) + when ("L")\byte! + selected_pad\scroll(0,10) + + when C.KEY_LEFT, ("h")\byte! + selected_pad\scroll(0,-1) + when ("H")\byte! + selected_pad\scroll(0,-10) + + when ('c')\byte! + select_pad(pads.stack) -- (C)allstack + + when ('s')\byte! + select_pad(pads.src) -- (S)ource Code + + when ('v')\byte! + select_pad(pads.vars) -- (V)ars + + when ('d')\byte! + select_pad(pads.values) -- (D)ata + + when ('o')\byte! + file = stack_locations[pads.stack.selected] + filename,line_no = file\match("([^:]*):(.*)") + line_no = tostring(pads.src.selected) + -- Launch system editor and then redraw everything + C.endwin! + os.execute((os.getenv("EDITOR") or "nano").." +"..line_no.." "..filename) + stdscr = C.initscr! + C.cbreak! + C.echo(false) + C.nl(false) + C.curs_set(0) + C.start_color! + C.use_default_colors! + stdscr\clear! + stdscr\refresh! + for _,pad in pairs(pads) do pad\refresh! + + when ('q')\byte!, ("Q")\byte! + pads = {} + C.endwin! + return + + C.endwin! + guard: (fn, ...)-> - return xpcall(fn, ((err_msg)-> xpcall(run_debugger, err_hand, err_msg)), ...) + return xpcall(fn, ((err_msg)-> xpcall(ldb.run_debugger, err_hand, err_msg)), ...) breakpoint: -> - return xpcall(run_debugger, err_hand, "Breakpoint triggered!") + return xpcall(ldb.run_debugger, err_hand, "Breakpoint triggered!") hijack_error: -> export error error = (err_msg)-> - return xpcall(run_debugger, err_hand, err_msg) + return xpcall(ldb.run_debugger, err_hand, err_msg) } +return ldb