Display function arguments and positions in the REPL

- cc.pretty.pretty now accepts two additional options:
   - function_args: Show function arguments
   - function_source: Show where functions are defined.
 - Expose the two options as lua.* settings (defaulting function_args to
   true, and function_source to false).
   These are then used in the Lua REPL.

Closes #361
This commit is contained in:
SquidDev 2020-05-14 19:11:57 +01:00
parent 086fccd997
commit 96e7b60285
4 changed files with 82 additions and 13 deletions

View File

@ -938,11 +938,23 @@ settings.define("motd.path", {
description = [[The path to load random messages from. Should be a colon (":") separated string of file paths.]],
type = "string",
})
settings.define("lua.warn_against_use_of_local", {
default = true,
description = [[Print a message when input in the Lua REPL starts with the word 'local'. Local variables defined in the Lua REPL are be inaccessable on the next input.]],
type = "boolean",
})
settings.define("lua.function_args", {
default = true,
description = "Show function arguments when printing functions.",
type = "boolean",
})
settings.define("lua.function_source", {
default = false,
description = "Show where a function was defined when printing functions.",
type = "boolean",
})
if term.isColour() then
settings.define("bios.use_multishell", {
default = true,

View File

@ -19,8 +19,12 @@
-- local pretty = require "cc.pretty"
-- pretty.write(pretty.group(pretty.text("hello") .. pretty.space_line .. pretty.text("world")))
local expect = require "cc.expect".expect
local type, getmetatable, setmetatable, colours, str_write = type, getmetatable, setmetatable, colours, write
local expect = require "cc.expect"
local expect, field = expect.expect, expect.field
local type, getmetatable, setmetatable, colours, str_write, tostring = type, getmetatable, setmetatable, colours, write, tostring
local debug_info = type(debug) == "table" and type(debug.getinfo) == "function" and debug.getinfo
local debug_local = type(debug) == "table" and type(debug.getlocal) == "function" and debug.getlocal
--- @{table.insert} alternative, but with the length stored inline.
local function append(out, value)
@ -343,13 +347,38 @@ local function key_compare(a, b)
return false
end
local function pretty_impl(obj, tracking)
local function show_function(fn, options)
local info = debug_info and debug_info(fn, "Su")
-- Include function source position if available
local name
if options.function_source and info and info.short_src and info.linedefined and info.linedefined >= 1 then
name = "function<" .. info.short_src .. ":" .. info.linedefined .. ">"
else
name = tostring(fn)
end
-- Include arguments if a Lua function and if available. Lua will report "C"
-- functions as variadic.
if options.function_args and info and info.what == "Lua" and info.nparams and debug_local then
local args = {}
for i = 1, info.nparams do args[i] = debug_local(fn, i) or "?" end
if info.isvararg then args[#args + 1] = "..." end
name = name .. "(" .. table.concat(args, ", ") .. ")"
end
return name
end
local function pretty_impl(obj, options, tracking)
local obj_type = type(obj)
if obj_type == "string" then
local formatted = ("%q"):format(obj):gsub("\\\n", "\\n")
return text(formatted, colours.red)
elseif obj_type == "number" then
return text(tostring(obj), colours.magenta)
elseif obj_type == "function" then
return text(show_function(obj, options), colours.lightGrey)
elseif obj_type ~= "table" or tracking[obj] then
return text(tostring(obj), colours.lightGrey)
elseif getmetatable(obj) ~= nil and getmetatable(obj).__tostring then
@ -371,15 +400,15 @@ local function pretty_impl(obj, tracking)
local v = obj[k]
local ty = type(k)
if ty == "number" and k % 1 == 0 and k >= 1 and k <= length then
append(doc, pretty_impl(v, tracking))
append(doc, pretty_impl(v, options, tracking))
elseif ty == "string" and not keywords[k] and k:match("^[%a_][%a%d_]*$") then
append(doc, text(k .. " = "))
append(doc, pretty_impl(v, tracking))
append(doc, pretty_impl(v, options, tracking))
else
append(doc, obracket)
append(doc, pretty_impl(k, tracking))
append(doc, pretty_impl(k, options, tracking))
append(doc, cbracket)
append(doc, pretty_impl(v, tracking))
append(doc, pretty_impl(v, options, tracking))
end
end
@ -393,12 +422,24 @@ end
-- This can then be rendered with @{write} or @{print}.
--
-- @param obj The object to pretty-print.
-- @tparam[opt] { function_args = boolean, function_source = boolean } options
-- Controls how various properties are displayed.
-- - `function_args`: Show the arguments to a function if known (`false` by default).
-- - `function_source: Show where the function was defined, instead of
-- `function: xxxxxxxx` (`false` by default).
-- @treturn Doc The object formatted as a document.
-- @usage Display a table on the screen
-- local pretty = require "cc.pretty"
-- pretty.print(pretty.pretty({ 1, 2, 3 }))
local function pretty(obj)
return pretty_impl(obj, {})
local function pretty(obj, options)
expect(2, options, "table", "nil")
options = options or {}
local actual_options = {
function_source = field(options, "function_source", "boolean", "nil") or false,
function_args = field(options, "function_args", "boolean", "nil") or false,
}
return pretty_impl(obj, actual_options, {})
end
return {

View File

@ -95,7 +95,10 @@ while bRunning do
local n = 1
while n < tResults.n or n <= nForcePrint do
local value = tResults[n + 1]
local ok, serialised = pcall(pretty.pretty, value)
local ok, serialised = pcall(pretty.pretty, value, {
function_args = settings.get("lua.function_args"),
function_source = settings.get("lua.function_source"),
})
if ok then
pretty.print(serialised)
else

View File

@ -165,7 +165,7 @@ describe("cc.pretty", function()
describe("pretty", function()
-- We make use of "render" here, as it's considerably easier than checking against the actual structure.
-- However, it does also mean our tests are less unit-like.
local function pretty(x, width) return pp.render(pp.pretty(x), width) end
local function pretty(x, width, options) return pp.render(pp.pretty(x, options), width) end
describe("tables", function()
it("displays empty tables", function()
@ -201,8 +201,21 @@ describe("cc.pretty", function()
expect(pretty("hello\nworld")):eq('"hello\\nworld"')
end)
it("shows functions", function()
expect(pretty(pretty)):eq(tostring(pretty))
describe("functions", function()
it("shows functions", function()
expect(pretty(pretty)):eq(tostring(pretty))
end)
it("shows function arguments", function()
local f = function(a, ...) end
expect(pretty(f, nil, { function_args = true })):eq(tostring(f) .. "(a, ...)")
end)
it("shows the function source", function()
local f = function(a, ...) end
expect(pretty(f, nil, { function_source = true }))
:str_match("^function<.*pretty_spec%.lua:%d+>$")
end)
end)
end)
end)