diff --git a/third_party/lua/README-luadebug.md b/third_party/lua/README-luadebug.md index 6704238375f17e4e825379cf575b17e64802a109..b7f9d823b3f81fec1381a44626531a5b286f9ac9 100644 --- a/third_party/lua/README-luadebug.md +++ b/third_party/lua/README-luadebug.md @@ -86,10 +86,10 @@ Using these you can customize the debugger to work in your environment. For inst There are also some goodies you can use to make debugging easier. * `dbg.writeln(format, ...)` - Basically the same as `dbg.write(string.format(format.."\n", ...))` -* `dbg.pretty_depth = int` - Set how deep `dbg.pretty()` formats tables. +* `dbg.cfg.pretty_depth = int` - Set how deep `dbg.pretty()` formats tables. * `dbg.pretty(obj)` - Will return a pretty print string of an object. * `dbg.pp(obj)` - Basically the same as `dbg.writeln(dbg.pretty(obj))` -* `dbg.auto_where = int_or_false` - Set the where command to run automatically when the active line changes. The value is the number of context lines. +* `dbg.cfg.auto_where = int_or_false` - Set the where command to run automatically when the active line changes. The value is the number of context lines. * `dbg.error(error, [level])` - Drop in replacement for `error()` that breaks in the debugger. * `dbg.assert(error, [message])` - Drop in replacement for `assert()` that breaks in the debugger. * `dbg.call(f, ...)` - Drop in replacement for `pcall()` that breaks in the debugger. diff --git a/third_party/lua/luadebug.lua b/third_party/lua/luadebug.lua index 5e1c3dfbf08ccf447992cebdbabed3eff4bedaca..f352ddcba4ca94a855d9ce2d998e0e5909ccc686 100644 --- a/third_party/lua/luadebug.lua +++ b/third_party/lua/luadebug.lua @@ -26,6 +26,7 @@ local dbg +local DEBUGGER = 'luadebug.lua' -- Use ANSI color codes in the prompt by default. local COLOR_GRAY = "" local COLOR_RED = "" @@ -36,7 +37,9 @@ local GREEN_CARET = " => " local auto_listing = true local function pretty(obj, max_depth) - if max_depth == nil then max_depth = dbg.pretty_depth end + if max_depth == nil then + max_depth = dbg.cfg.pretty_depth + end -- Returns true if a table has a __tostring metamethod. local function coerceable(tbl) @@ -99,15 +102,37 @@ local function dbg_writeln(str, ...) end end -local function format_loc(file, line) return COLOR_BLUE .. file .. COLOR_RESET .. ":" .. COLOR_YELLOW .. - line .. COLOR_RESET end +-- colored text output wrappers +local function color_blue(text) + return COLOR_BLUE .. text .. COLOR_RESET +end + +local function color_yellow(text) + return COLOR_YELLOW .. text .. COLOR_RESET +end + +local function color_red(text) + return COLOR_RED .. text .. COLOR_RESET +end + +local function color_grey(text) + return COLOR_GRAY .. text .. COLOR_RESET +end + +local function q(text) + return "'" .. text .. "'" +end + +local function format_loc(file, line) + return color_blue(file) .. ":" .. color_yellow(line) +end local function format_stack_frame_info(info) local filename = info.source:match("@(.*)") local source = filename and dbg.shorten_path(filename) or info.short_src local namewhat = (info.namewhat == "" and "chunk at" or info.namewhat) - local name = (info.name and "'" .. COLOR_BLUE .. info.name .. COLOR_RESET .. "'" or - format_loc(source, info.linedefined)) + local name = info.name and q(color_blue(info.name)) or + format_loc(source, info.linedefined) return format_loc(source, info.currentline) .. " in " .. namewhat .. " " .. name end @@ -195,8 +220,8 @@ local function mutate_bindings(_, name, value) repeat local var = debug.getlocal(level, i) if name == var then - dbg_writeln(COLOR_YELLOW .. - "luadebug.lua" .. GREEN_CARET .. "Set local variable " .. COLOR_BLUE .. name .. COLOR_RESET) + dbg_writeln(color_yellow(DEBUGGER) .. GREEN_CARET .. + "Set local variable " .. color_blue(name)) return debug.setlocal(level, i, value) end i = i + 1 @@ -209,8 +234,8 @@ local function mutate_bindings(_, name, value) repeat local var = debug.getupvalue(func, i) if name == var then - dbg_writeln(COLOR_YELLOW .. "luadebug.lua" .. GREEN_CARET .. - "Set upvalue " .. COLOR_BLUE .. name .. COLOR_RESET) + dbg_writeln(color_yellow(DEBUGGER) .. + "Set upvalue " .. color_blue(name)) return debug.setupvalue(func, i, value) end i = i + 1 @@ -218,14 +243,14 @@ local function mutate_bindings(_, name, value) end -- Set a global. - dbg_writeln(COLOR_YELLOW .. "luadebug.lua" .. GREEN_CARET .. "Set global variable " .. COLOR_BLUE .. - name .. COLOR_RESET) + dbg_writeln(color_yellow(DEBUGGER) .. + "Set global variable " .. color_blue(name)) _G[name] = value end -- Compile an expression with the given variable bindings. local function compile_chunk(block, env) - local source = "luadebug.lua REPL" + local source = DEBUGGER .. " REPL" local chunk = loadstring(block, source) if chunk then setfenv(chunk, env) @@ -275,10 +300,13 @@ local function where(info, context_lines) for i = info.currentline - context_lines, info.currentline + context_lines do local tab_or_caret = (i == info.currentline and GREEN_CARET or " ") local line = source[i] - if line then dbg_writeln(COLOR_GRAY .. "% 4d" .. tab_or_caret .. "%s", i, line) end + if line then + dbg_writeln(color_grey("% 4d") .. tab_or_caret .. "%s", i, line) + end end else - dbg_writeln(COLOR_RED .. "Error: Source not available for " .. COLOR_BLUE .. info.short_src); + dbg_writeln(color_red("Error: Source not available for ") .. + color_blue(q(info.source))) end return false @@ -314,7 +342,7 @@ local function cmd_print(expr) -- The first result is the pcall error. if not results[1] then - dbg_writeln(COLOR_RED .. "Error:" .. COLOR_RESET .. " " .. results[2]) + dbg_writeln(color_red("Error:") .. " " .. results[2]) else local output = "" for i = 2, results.n do @@ -322,7 +350,7 @@ local function cmd_print(expr) end if output == "" then output = "<no result>" end - dbg_writeln(COLOR_BLUE .. expr .. GREEN_CARET .. output) + dbg_writeln(color_blue(expr) .. GREEN_CARET .. output) end return false @@ -341,7 +369,7 @@ local function cmd_eval(code) -- Call the chunk and collect the results. local success, err = pcall(chunk, unpack(rawget(env, "...") or {})) if not success then - dbg_writeln(COLOR_RED .. "Error:" .. COLOR_RESET .. " " .. tostring(err)) + dbg_writeln(color_red("Error:") .. " " .. tostring(err)) end return false @@ -359,7 +387,9 @@ local function cmd_down() if info then stack_inspect_offset = offset dbg_writeln("Inspecting frame: " .. format_stack_frame_info(info)) - if tonumber(dbg.auto_where) then where(info, dbg.auto_where) end + if tonumber(dbg.cfg.auto_where) then + where(info, dbg.cfg.auto_where) + end else dbg_writeln("Already at the bottom of the stack.") end @@ -380,7 +410,9 @@ local function cmd_up() if info then stack_inspect_offset = offset dbg_writeln("Inspecting frame: " .. format_stack_frame_info(info)) - if tonumber(dbg.auto_where) then where(info, dbg.auto_where) end + if tonumber(dbg.cfg.auto_where) then + where(info, dbg.cfg.auto_where) + end else dbg_writeln("Already at the top of the stack.") end @@ -408,7 +440,8 @@ local function cmd_trace() local is_current_frame = (i + stack_top == stack_inspect_offset) local tab_or_caret = (is_current_frame and GREEN_CARET or " ") - dbg_writeln(COLOR_GRAY .. "% 4d" .. COLOR_RESET .. tab_or_caret .. "%s", i, format_stack_frame_info(info)) + dbg_writeln(color_grey("% 4d") .. tab_or_caret .. "%s", + i, format_stack_frame_info(info)) i = i + 1 end @@ -428,7 +461,7 @@ local function cmd_locals() -- Skip the debugger object itself, "(*internal)" values, and Lua 5.2's _ENV object. if not rawequal(v, dbg) and k ~= "_ENV" and not k:match("%(.*%)") then - dbg_writeln(" " .. COLOR_BLUE .. k .. GREEN_CARET .. pretty(v)) + dbg_writeln(" " .. color_blue(k) .. GREEN_CARET .. pretty(v)) end end @@ -509,12 +542,12 @@ local function run_command(line) last_cmd = line -- unpack({...}) prevents tail call elimination so the stack frame indices are predictable. return unpack({ command(command_arg) }) - elseif dbg.auto_eval then + elseif dbg.cfg.auto_eval then return unpack({ cmd_eval(line) }) else - dbg_writeln(COLOR_RED .. - "Error:" .. COLOR_RESET .. " command '%s' not recognized.\nType 'h' and press return for a command list.", - line) + dbg_writeln(color_red("Error:") .. + " command '%s' not recognized.\n" .. + "Type 'h' and press return for a command list.", line) return false end end @@ -526,20 +559,25 @@ repl = function(reason) end local info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL - 3) - reason = reason and (COLOR_YELLOW .. "break via " .. COLOR_RED .. reason .. GREEN_CARET) or "" + reason = reason and (color_yellow("break via ") .. color_red(reason) .. + GREEN_CARET) or "" dbg_writeln(reason .. format_stack_frame_info(info)) - if tonumber(dbg.auto_where) then where(info, dbg.auto_where) end + if tonumber(dbg.cfg.auto_where) then + where(info, dbg.cfg.auto_where) + end repeat if auto_listing then pcall(cmd_listing(3)) end - local success, done, hook = pcall(run_command, dbg.read(COLOR_RED .. "luadebug.lua> " .. COLOR_RESET)) + local success, done, hook = pcall(run_command, + dbg.read(color_red(DEBUGGER .. "> "))) if success then - debug.sethook(hook and hook(0), "crl") + debug.sethook(hook and hook(0), "l") else - local message = COLOR_RED .. "INTERNAL DEBUGGER.LUA ERROR. ABORTING\n:" .. COLOR_RESET .. " " .. done + local message = color_red("INTERNAL " .. DEBUGGER .. " ERROR. " .. + "ABORTING\n:") .. " " .. done dbg_writeln(message) error(message) end @@ -547,11 +585,30 @@ repl = function(reason) end -- Make the debugger object callable like a function. -dbg = setmetatable({}, { +dbg = setmetatable({ + read = dbg_read, + write = dbg_write, + writeln = dbg_writeln, + + shorten_path = function(path) return path end, + exit = function(err) os.exit(err) end, + + cfg = { + auto_where = false, + auto_eval = false, + pretty_depth = 3, + }, + pretty = pretty, + pp = function(value, depth) + dbg_writeln(pretty(value, depth)) + end, + }, { __call = function(_, condition, top_offset, source) - if condition then return end + if condition then + return + end - top_offset = (top_offset or 0) + top_offset = top_offset or 0 stack_inspect_offset = top_offset stack_top = top_offset @@ -560,27 +617,12 @@ dbg = setmetatable({}, { end, }) --- Expose the debugger's IO functions. -dbg.read = dbg_read -dbg.write = dbg_write -dbg.shorten_path = function(path) return path end -dbg.exit = function(err) os.exit(err) end - -dbg.writeln = dbg_writeln - -dbg.pretty_depth = 3 -dbg.pretty = pretty -dbg.pp = function(value, depth) dbg_writeln(pretty(value, depth)) end - -dbg.auto_where = false -dbg.auto_eval = false - local lua_error, lua_assert = error, assert -- Works like error(), but invokes the debugger. function dbg.error(err, level) level = level or 1 - dbg_writeln(COLOR_RED .. "ERROR: " .. COLOR_RESET .. pretty(err)) + dbg_writeln(color_red("ERROR: ") .. pretty(err)) dbg(false, level, "dbg.error()") lua_error(err, level) @@ -589,7 +631,7 @@ end -- Works like assert(), but invokes the debugger on a failure. function dbg.assert(condition, message) if not condition then - dbg_writeln(COLOR_RED .. "ERROR:" .. COLOR_RESET .. message) + dbg_writeln(color_red("ERROR:") .. message) dbg(false, 1, "dbg.assert()") end @@ -599,7 +641,7 @@ end -- Works like pcall(), but invokes the debugger on an error. function dbg.call(f, ...) return xpcall(f, function(err) - dbg_writeln(COLOR_RED .. "ERROR: " .. COLOR_RESET .. pretty(err)) + dbg_writeln(color_red("ERROR: ") .. pretty(err)) dbg(false, 1, "dbg.call()") return err @@ -609,12 +651,12 @@ end -- Error message handler that can be used with lua_pcall(). function dbg.msgh(...) if debug.getinfo(2) then - dbg_writeln(COLOR_RED .. "ERROR: " .. COLOR_RESET .. pretty(...)) + dbg_writeln(color_red("ERROR: ") .. pretty(...)) dbg(false, 1, "dbg.msgh()") else - dbg_writeln(COLOR_RED .. - "luadebug.lua: " .. - COLOR_RESET .. "Error did not occur in Lua code. Execution will continue after dbg_pcall().") + dbg_writeln(color_red(DEBUGGER .. ": ") .. + "Error did not occur in Lua code. " .. + "Execution will continue after dbg_pcall().") end return ... @@ -637,7 +679,6 @@ if color_maybe_supported and not os.getenv("DBG_NOCOLOR") then end -- Detect Lua/LuaJIT version. -dbg_writeln(COLOR_YELLOW .. "luadebug.lua: " .. COLOR_RESET .. "Loaded for " .. - jit.version) +dbg_writeln(color_yellow(DEBUGGER .. ": ") .. "Loaded for " .. jit.version) return dbg diff --git a/third_party/lua/luadebug_tutorial.lua b/third_party/lua/luadebug_tutorial.lua index ccf021307e013f8270400c5b0cba0115b2a0ecab..ea1fc7b24566ccf049faabd738654883b0c81e5e 100644 --- a/third_party/lua/luadebug_tutorial.lua +++ b/third_party/lua/luadebug_tutorial.lua @@ -29,7 +29,7 @@ print [[ Type 'w' to show 5 lines of surrounding code directly in the debugger. (w = Where) Type 'w 3' to show 3 lines, etc. - Alternatively, set dbg.auto_where to a number + Alternatively, set dbg.cfg.auto_where to a number to run it automatically every time the program advances. Once you've tried the where command, type 's' to step to