mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-27 20:07:39 +00:00
Merge branch 'master' into mc-1.14.x
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
* The Lua REPL warns when declaring locals (lupus590, exerro)
|
||||
* Add config to allow using command computers in survival.
|
||||
* Add fs.isDriveRoot - checks if a path is the root of a drive.
|
||||
* `cc.pretty` can now display a function's arguments and where it was defined. The Lua REPL will show arguments by default.
|
||||
* Move the shell's `require`/`package` implementation to a separate `cc.require` module.
|
||||
|
||||
And several bug fixes:
|
||||
* Fix io.lines not accepting arguments.
|
||||
|
||||
@@ -7,6 +7,8 @@ New features in CC: Tweaked 1.88.0
|
||||
* The Lua REPL warns when declaring locals (lupus590, exerro)
|
||||
* Add config to allow using command computers in survival.
|
||||
* Add fs.isDriveRoot - checks if a path is the root of a drive.
|
||||
* `cc.pretty` can now display a function's arguments and where it was defined. The Lua REPL will show arguments by default.
|
||||
* Move the shell's `require`/`package` implementation to a separate `cc.require` module.
|
||||
|
||||
And several bug fixes:
|
||||
* Fix io.lines not accepting arguments.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -141,12 +141,12 @@ return {
|
||||
program = program,
|
||||
|
||||
-- Re-export various other functions
|
||||
help = wrap(help.completeTopic),
|
||||
choice = wrap(completion.choice),
|
||||
peripheral = wrap(completion.peripheral),
|
||||
side = wrap(completion.side),
|
||||
setting = wrap(completion.setting),
|
||||
command = wrap(completion.command),
|
||||
help = wrap(help.completeTopic), --- Wraps @{help.completeTopic} as a @{build} compatible function.
|
||||
choice = wrap(completion.choice), --- Wraps @{cc.completion.choice} as a @{build} compatible function.
|
||||
peripheral = wrap(completion.peripheral), --- Wraps @{cc.completion.peripheral} as a @{build} compatible function.
|
||||
side = wrap(completion.side), --- Wraps @{cc.completion.side} as a @{build} compatible function.
|
||||
setting = wrap(completion.setting), --- Wraps @{cc.completion.setting} as a @{build} compatible function.
|
||||
command = wrap(completion.command), --- Wraps @{cc.completion.command} as a @{build} compatible function.
|
||||
|
||||
build = build,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
-- @module[module] shell
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
local make_package = dofile("rom/modules/main/cc/require.lua").make
|
||||
|
||||
local multishell = multishell
|
||||
local parentShell = shell
|
||||
@@ -28,94 +29,10 @@ local tCompletionInfo = parentShell and parentShell.getCompletionInfo() or {}
|
||||
local tProgramStack = {}
|
||||
|
||||
local shell = {} --- @export
|
||||
local function createShellEnv(sDir)
|
||||
local tEnv = {}
|
||||
tEnv.shell = shell
|
||||
tEnv.multishell = multishell
|
||||
|
||||
local package = {}
|
||||
package.loaded = {
|
||||
_G = _G,
|
||||
bit32 = bit32,
|
||||
coroutine = coroutine,
|
||||
math = math,
|
||||
package = package,
|
||||
string = string,
|
||||
table = table,
|
||||
}
|
||||
package.path = "?;?.lua;?/init.lua;/rom/modules/main/?;/rom/modules/main/?.lua;/rom/modules/main/?/init.lua"
|
||||
if turtle then
|
||||
package.path = package.path .. ";/rom/modules/turtle/?;/rom/modules/turtle/?.lua;/rom/modules/turtle/?/init.lua"
|
||||
elseif commands then
|
||||
package.path = package.path .. ";/rom/modules/command/?;/rom/modules/command/?.lua;/rom/modules/command/?/init.lua"
|
||||
end
|
||||
package.config = "/\n;\n?\n!\n-"
|
||||
package.preload = {}
|
||||
package.loaders = {
|
||||
function(name)
|
||||
if package.preload[name] then
|
||||
return package.preload[name]
|
||||
else
|
||||
return nil, "no field package.preload['" .. name .. "']"
|
||||
end
|
||||
end,
|
||||
function(name)
|
||||
local fname = string.gsub(name, "%.", "/")
|
||||
local sError = ""
|
||||
for pattern in string.gmatch(package.path, "[^;]+") do
|
||||
local sPath = string.gsub(pattern, "%?", fname)
|
||||
if sPath:sub(1, 1) ~= "/" then
|
||||
sPath = fs.combine(sDir, sPath)
|
||||
end
|
||||
if fs.exists(sPath) and not fs.isDir(sPath) then
|
||||
local fnFile, sError = loadfile(sPath, nil, tEnv)
|
||||
if fnFile then
|
||||
return fnFile, sPath
|
||||
else
|
||||
return nil, sError
|
||||
end
|
||||
else
|
||||
if #sError > 0 then
|
||||
sError = sError .. "\n "
|
||||
end
|
||||
sError = sError .. "no file '" .. sPath .. "'"
|
||||
end
|
||||
end
|
||||
return nil, sError
|
||||
end,
|
||||
}
|
||||
|
||||
local sentinel = {}
|
||||
local function require(name)
|
||||
expect(1, name, "string")
|
||||
if package.loaded[name] == sentinel then
|
||||
error("loop or previous error loading module '" .. name .. "'", 0)
|
||||
end
|
||||
if package.loaded[name] then
|
||||
return package.loaded[name]
|
||||
end
|
||||
|
||||
local sError = "module '" .. name .. "' not found:"
|
||||
for _, searcher in ipairs(package.loaders) do
|
||||
local loader = table.pack(searcher(name))
|
||||
if loader[1] then
|
||||
package.loaded[name] = sentinel
|
||||
local result = loader[1](name, table.unpack(loader, 2, loader.n))
|
||||
if result == nil then result = true end
|
||||
|
||||
package.loaded[name] = result
|
||||
return result
|
||||
else
|
||||
sError = sError .. "\n " .. loader[2]
|
||||
end
|
||||
end
|
||||
error(sError, 2)
|
||||
end
|
||||
|
||||
tEnv.package = package
|
||||
tEnv.require = require
|
||||
|
||||
return tEnv
|
||||
local function createShellEnv(dir)
|
||||
local env = { shell = shell, multishell = multishell }
|
||||
env.require, env.package = make_package(env, dir)
|
||||
return env
|
||||
end
|
||||
|
||||
-- Colours
|
||||
|
||||
Reference in New Issue
Block a user