mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 21:52:59 +00:00 
			
		
		
		
	Merge branch 'master' into mc-1.14.x
This commit is contained in:
		| @@ -1,14 +1,120 @@ | |||||||
|  | --[[- Interact with redstone attached to this computer. | ||||||
|  |  | ||||||
|  | The @{redstone} library exposes three "types" of redstone control: | ||||||
|  |  - Binary input/output (@{setOutput}/@{getInput}): These simply check if a | ||||||
|  |    redstone wire has any input or output. A signal strength of 1 and 15 are | ||||||
|  |    treated the same. | ||||||
|  |  - Analogue input/output (@{setAnalogueOutput}/@{getAnalogueInput}): These | ||||||
|  |    work with the actual signal strength of the redstone wired, from 0 to 15. | ||||||
|  |  - Bundled cables (@{setBundledOutput}/@{getBundledInput}): These interact with | ||||||
|  |    "bundled" cables, such as those from Project:Red. These allow you to send | ||||||
|  |    16 separate on/off signals. Each channel corresponds to a colour, with the | ||||||
|  |    first being @{colors.white} and the last @{colors.black}. | ||||||
|  |  | ||||||
|  | Whenever a redstone input changes, a `redstone` event will be fired. This may | ||||||
|  | be used in or | ||||||
|  |  | ||||||
|  | This module may also be referred to as `rs`. For example, one may call | ||||||
|  | `rs.getSides()` instead of @{redstone.getSides}. | ||||||
|  |  | ||||||
|  | @module redstone | ||||||
|  | @usage Toggle the redstone signal above the computer every 0.5 seconds. | ||||||
|  |  | ||||||
|  |     while true do | ||||||
|  |         redstone.setOutput("top", not redstone.getOutput("top")) | ||||||
|  |         sleep(0.5) | ||||||
|  |     end | ||||||
|  | @usage Mimic a redstone comparator in [subtraction mode][comparator]. | ||||||
|  |  | ||||||
|  |     while true do | ||||||
|  |       local rear = rs.getAnalogueInput("back") | ||||||
|  |       local sides = math.max(rs.getAnalogueInput("left"), rs.getAnalogueInput("right")) | ||||||
|  |       rs.setAnalogueOutput("front", math.max(rear - sides, 0)) | ||||||
|  |  | ||||||
|  |       os.pullEvent("redstone") -- Wait for a change to inputs. | ||||||
|  |     end | ||||||
|  |  | ||||||
|  | [comparator]: https://minecraft.gamepedia.com/Redstone_Comparator#Subtract_signal_strength "Redstone Comparator on the Minecraft wiki." | ||||||
|  | ]] | ||||||
|  |  | ||||||
|  | --- Returns a table containing the six sides of the computer. Namely, "top", | ||||||
|  | -- "bottom", "left", "right", "front" and "back". | ||||||
|  | -- | ||||||
|  | -- @treturn { string... } A table of valid sides. | ||||||
| function getSides() end | function getSides() end | ||||||
|  |  | ||||||
|  | --- Turn the redstone signal of a specific side on or off. | ||||||
|  | -- | ||||||
|  | -- @tparam string side The side to set. | ||||||
|  | -- @tparam boolean on Whether the redstone signal should be on or off. When on, | ||||||
|  | -- a signal strength of 15 is emitted. | ||||||
| function setOutput(side, on) end | function setOutput(side, on) end | ||||||
|  |  | ||||||
|  | --- Get the current redstone output of a specific side. | ||||||
|  | -- | ||||||
|  | -- @tparam string side The side to get. | ||||||
|  | -- @treturn boolean Whether the redstone output is on or off. | ||||||
|  | -- @see setOutput | ||||||
| function getOutput(side) end | function getOutput(side) end | ||||||
|  |  | ||||||
|  | --- Get the current redstone input of a specific side. | ||||||
|  | -- | ||||||
|  | -- @tparam string side The side to get. | ||||||
|  | -- @treturn boolean Whether the redstone input is on or off. | ||||||
| function getInput(side) end | function getInput(side) end | ||||||
| function setBundledOutput(side, output) end |  | ||||||
| function getBundledOutput(side) end | --- Set the redstone signal strength for a specific side. | ||||||
| function getBundledInput(side) end | -- | ||||||
| function testBundledInput(side, mask) end | -- @tparam string side The side to set. | ||||||
|  | -- @tparam number value The signal strength, between 0 and 15. | ||||||
|  | -- @throws If `value` is not between 0 and 15. | ||||||
| function setAnalogOutput(side, value) end | function setAnalogOutput(side, value) end | ||||||
| setAnalogueOutput = setAnalogOutput | setAnalogueOutput = setAnalogOutput | ||||||
|  |  | ||||||
|  | --- Get the redstone output signal strength for a specific side. | ||||||
|  | -- | ||||||
|  | -- @tparam string side The side to get. | ||||||
|  | -- @treturn number The output signal strength, between 0 and 15. | ||||||
|  | -- @see setAnalogueOutput | ||||||
| function getAnalogOutput(sid) end | function getAnalogOutput(sid) end | ||||||
| getAnalogueOutput = getAnalogOutput | getAnalogueOutput = getAnalogOutput | ||||||
|  |  | ||||||
|  | --- Get the redstone input signal strength for a specific side. | ||||||
|  | -- | ||||||
|  | -- @tparam string side The side to get. | ||||||
|  | -- @treturn number The input signal strength, between 0 and 15. | ||||||
| function getAnalogInput(side) end | function getAnalogInput(side) end | ||||||
| getAnalogueInput = getAnalogInput | getAnalogueInput = getAnalogInput | ||||||
|  |  | ||||||
|  | --- Set the bundled cable output for a specific side. | ||||||
|  | -- | ||||||
|  | -- @tparam string side The side to set. | ||||||
|  | -- @tparam number The colour bitmask to set. | ||||||
|  | -- @see colors.subtract For removing a colour from the bitmask. | ||||||
|  | -- @see colors.combine For adding a colour to the bitmask. | ||||||
|  | function setBundledOutput(side, output) end | ||||||
|  |  | ||||||
|  | --- Get the bundled cable output for a specific side. | ||||||
|  | -- | ||||||
|  | -- @tparam string side The side to get. | ||||||
|  | -- @treturn number The bundled cable's output. | ||||||
|  | function getBundledOutput(side) end | ||||||
|  |  | ||||||
|  | --- Get the bundled cable input for a specific side. | ||||||
|  | -- | ||||||
|  | -- @tparam string side The side to get. | ||||||
|  | -- @treturn number The bundled cable's input. | ||||||
|  | -- @see testBundledInput To determine if a specific colour is set. | ||||||
|  | function getBundledInput(side) end | ||||||
|  |  | ||||||
|  | --- Determine if a specific combination of colours are on for the given side. | ||||||
|  | -- | ||||||
|  | -- @tparam string side The side to test. | ||||||
|  | -- @tparam number mask The mask to test. | ||||||
|  | -- @see getBundledInput | ||||||
|  | -- @see colors.combine For adding a colour to the bitmask. | ||||||
|  | -- @usage Check if @{colors.white} and @{colors.black} are on for above the | ||||||
|  | -- computer. | ||||||
|  | -- | ||||||
|  | --     print(redstone.testBundledInput("top", colors.combine(colors.white, colors.black))) | ||||||
|  | function testBundledInput(side, mask) end | ||||||
|   | |||||||
| @@ -73,12 +73,10 @@ | |||||||
|   (/doc/stub/fs.lua |   (/doc/stub/fs.lua | ||||||
|    /doc/stub/http.lua |    /doc/stub/http.lua | ||||||
|    /doc/stub/os.lua |    /doc/stub/os.lua | ||||||
|    /doc/stub/redstone.lua |  | ||||||
|    /doc/stub/term.lua |    /doc/stub/term.lua | ||||||
|    /doc/stub/turtle.lua |    /doc/stub/turtle.lua | ||||||
|    /src/main/resources/*/computercraft/lua/rom/apis/io.lua |    /src/main/resources/*/computercraft/lua/rom/apis/io.lua | ||||||
|    /src/main/resources/*/computercraft/lua/rom/apis/window.lua |    /src/main/resources/*/computercraft/lua/rom/apis/window.lua) | ||||||
|    /src/main/resources/*/computercraft/lua/rom/modules/main/cc/shell/completion.lua) |  | ||||||
|  |  | ||||||
|   (linters -doc:undocumented -doc:undocumented-arg)) |   (linters -doc:undocumented -doc:undocumented-arg)) | ||||||
|  |  | ||||||
| @@ -87,7 +85,6 @@ | |||||||
|   (/src/main/resources/*/computercraft/lua/rom/apis/textutils.lua |   (/src/main/resources/*/computercraft/lua/rom/apis/textutils.lua | ||||||
|    /src/main/resources/*/computercraft/lua/rom/modules/main/cc/completion.lua |    /src/main/resources/*/computercraft/lua/rom/modules/main/cc/completion.lua | ||||||
|    /src/main/resources/*/computercraft/lua/rom/modules/main/cc/shell/completion.lua |    /src/main/resources/*/computercraft/lua/rom/modules/main/cc/shell/completion.lua | ||||||
|    /src/main/resources/*/computercraft/lua/rom/programs/advanced/multishell.lua |  | ||||||
|    /src/main/resources/*/computercraft/lua/rom/programs/shell.lua) |    /src/main/resources/*/computercraft/lua/rom/programs/shell.lua) | ||||||
|   (linters -doc:unresolved-reference)) |   (linters -doc:unresolved-reference)) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,11 +16,11 @@ import static dan200.computercraft.api.lua.ArgumentHelper.*; | |||||||
|  |  | ||||||
| public class RedstoneAPI implements ILuaAPI | public class RedstoneAPI implements ILuaAPI | ||||||
| { | { | ||||||
|     private IAPIEnvironment m_environment; |     private final IAPIEnvironment environment; | ||||||
|  |  | ||||||
|     public RedstoneAPI( IAPIEnvironment environment ) |     public RedstoneAPI( IAPIEnvironment environment ) | ||||||
|     { |     { | ||||||
|         m_environment = environment; |         this.environment = environment; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -63,31 +63,31 @@ public class RedstoneAPI implements ILuaAPI | |||||||
|                 // setOutput |                 // setOutput | ||||||
|                 ComputerSide side = parseSide( args ); |                 ComputerSide side = parseSide( args ); | ||||||
|                 boolean output = getBoolean( args, 1 ); |                 boolean output = getBoolean( args, 1 ); | ||||||
|                 m_environment.setOutput( side, output ? 15 : 0 ); |                 environment.setOutput( side, output ? 15 : 0 ); | ||||||
|                 return null; |                 return null; | ||||||
|             } |             } | ||||||
|             case 2: // getOutput |             case 2: // getOutput | ||||||
|                 return new Object[] { m_environment.getOutput( parseSide( args ) ) > 0 }; |                 return new Object[] { environment.getOutput( parseSide( args ) ) > 0 }; | ||||||
|             case 3: // getInput |             case 3: // getInput | ||||||
|                 return new Object[] { m_environment.getInput( parseSide( args ) ) > 0 }; |                 return new Object[] { environment.getInput( parseSide( args ) ) > 0 }; | ||||||
|             case 4: |             case 4: | ||||||
|             { |             { | ||||||
|                 // setBundledOutput |                 // setBundledOutput | ||||||
|                 ComputerSide side = parseSide( args ); |                 ComputerSide side = parseSide( args ); | ||||||
|                 int output = getInt( args, 1 ); |                 int output = getInt( args, 1 ); | ||||||
|                 m_environment.setBundledOutput( side, output ); |                 environment.setBundledOutput( side, output ); | ||||||
|                 return null; |                 return null; | ||||||
|             } |             } | ||||||
|             case 5: // getBundledOutput |             case 5: // getBundledOutput | ||||||
|                 return new Object[] { m_environment.getBundledOutput( parseSide( args ) ) }; |                 return new Object[] { environment.getBundledOutput( parseSide( args ) ) }; | ||||||
|             case 6: // getBundledInput |             case 6: // getBundledInput | ||||||
|                 return new Object[] { m_environment.getBundledInput( parseSide( args ) ) }; |                 return new Object[] { environment.getBundledInput( parseSide( args ) ) }; | ||||||
|             case 7: |             case 7: | ||||||
|             { |             { | ||||||
|                 // testBundledInput |                 // testBundledInput | ||||||
|                 ComputerSide side = parseSide( args ); |                 ComputerSide side = parseSide( args ); | ||||||
|                 int mask = getInt( args, 1 ); |                 int mask = getInt( args, 1 ); | ||||||
|                 int input = m_environment.getBundledInput( side ); |                 int input = environment.getBundledInput( side ); | ||||||
|                 return new Object[] { (input & mask) == mask }; |                 return new Object[] { (input & mask) == mask }; | ||||||
|             } |             } | ||||||
|             case 8: |             case 8: | ||||||
| @@ -100,15 +100,15 @@ public class RedstoneAPI implements ILuaAPI | |||||||
|                 { |                 { | ||||||
|                     throw new LuaException( "Expected number in range 0-15" ); |                     throw new LuaException( "Expected number in range 0-15" ); | ||||||
|                 } |                 } | ||||||
|                 m_environment.setOutput( side, output ); |                 environment.setOutput( side, output ); | ||||||
|                 return null; |                 return null; | ||||||
|             } |             } | ||||||
|             case 10: |             case 10: | ||||||
|             case 11: // getAnalogOutput/getAnalogueOutput |             case 11: // getAnalogOutput/getAnalogueOutput | ||||||
|                 return new Object[] { m_environment.getOutput( parseSide( args ) ) }; |                 return new Object[] { environment.getOutput( parseSide( args ) ) }; | ||||||
|             case 12: |             case 12: | ||||||
|             case 13: // getAnalogInput/getAnalogueInput |             case 13: // getAnalogInput/getAnalogueInput | ||||||
|                 return new Object[] { m_environment.getInput( parseSide( args ) ) }; |                 return new Object[] { environment.getInput( parseSide( args ) ) }; | ||||||
|             default: |             default: | ||||||
|                 return null; |                 return null; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -0,0 +1,121 @@ | |||||||
|  | --- This provides a pure Lua implementation of the builtin @{require} function | ||||||
|  | -- and @{package} library. | ||||||
|  | -- | ||||||
|  | -- Generally you do not need to use this module - it is injected into the | ||||||
|  | -- every program's environment. However, it may be useful when building a | ||||||
|  | -- custom shell or when running programs yourself. | ||||||
|  | -- | ||||||
|  | -- @module cc.require | ||||||
|  | -- @usage Construct the package and require function, and insert them into a | ||||||
|  | -- custom environment. | ||||||
|  | -- | ||||||
|  | --     local env = setmetatable({}, { __index = _ENV }) | ||||||
|  | --     local r = require "cc.require" | ||||||
|  | --     env.require, env.package = r.make(env, "/") | ||||||
|  |  | ||||||
|  | local expect = require and require("cc.expect") or dofile("rom/modules/main/cc/expect.lua") | ||||||
|  | local expect = expect.expect | ||||||
|  |  | ||||||
|  | local function preload(package) | ||||||
|  |     return function(name) | ||||||
|  |         if package.preload[name] then | ||||||
|  |             return package.preload[name] | ||||||
|  |         else | ||||||
|  |             return nil, "no field package.preload['" .. name .. "']" | ||||||
|  |         end | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function from_file(package, env, dir) | ||||||
|  |     return 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(dir, sPath) | ||||||
|  |             end | ||||||
|  |             if fs.exists(sPath) and not fs.isDir(sPath) then | ||||||
|  |                 local fnFile, sError = loadfile(sPath, nil, env) | ||||||
|  |                 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 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function make_require(package) | ||||||
|  |     local sentinel = {} | ||||||
|  |     return function(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 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | --- Build an implementation of Lua's @{package} library, and a @{require} | ||||||
|  | -- function to load modules within it. | ||||||
|  | -- | ||||||
|  | -- @tparam table env The environment to load packages into. | ||||||
|  | -- @tparam string dir The directory that relative packages are loaded from. | ||||||
|  | -- @treturn function The new @{require} function. | ||||||
|  | -- @treturn table The new @{package} library. | ||||||
|  | local function make_package(env, dir) | ||||||
|  |     expect(1, env, "table") | ||||||
|  |     expect(2, dir, "string") | ||||||
|  |  | ||||||
|  |     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 = { preload(package), from_file(package, env, dir) } | ||||||
|  |  | ||||||
|  |     return make_require(package), package | ||||||
|  | end | ||||||
|  |  | ||||||
|  | return { make = make_package } | ||||||
| @@ -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.]], |     description = [[The path to load random messages from. Should be a colon (":") separated string of file paths.]], | ||||||
|     type = "string", |     type = "string", | ||||||
| }) | }) | ||||||
|  |  | ||||||
| settings.define("lua.warn_against_use_of_local", { | settings.define("lua.warn_against_use_of_local", { | ||||||
|     default = true, |     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.]], |     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", |     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 | if term.isColour() then | ||||||
|     settings.define("bios.use_multishell", { |     settings.define("bios.use_multishell", { | ||||||
|         default = true, |         default = true, | ||||||
|   | |||||||
| @@ -7,6 +7,8 @@ | |||||||
| * The Lua REPL warns when declaring locals (lupus590, exerro) | * The Lua REPL warns when declaring locals (lupus590, exerro) | ||||||
| * Add config to allow using command computers in survival. | * Add config to allow using command computers in survival. | ||||||
| * Add fs.isDriveRoot - checks if a path is the root of a drive. | * 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: | And several bug fixes: | ||||||
| * Fix io.lines not accepting arguments. | * 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) | * The Lua REPL warns when declaring locals (lupus590, exerro) | ||||||
| * Add config to allow using command computers in survival. | * Add config to allow using command computers in survival. | ||||||
| * Add fs.isDriveRoot - checks if a path is the root of a drive. | * 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: | And several bug fixes: | ||||||
| * Fix io.lines not accepting arguments. | * Fix io.lines not accepting arguments. | ||||||
|   | |||||||
| @@ -19,8 +19,12 @@ | |||||||
| --     local pretty = require "cc.pretty" | --     local pretty = require "cc.pretty" | ||||||
| --     pretty.write(pretty.group(pretty.text("hello") .. pretty.space_line .. pretty.text("world"))) | --     pretty.write(pretty.group(pretty.text("hello") .. pretty.space_line .. pretty.text("world"))) | ||||||
|  |  | ||||||
| local expect = require "cc.expect".expect | local expect = require "cc.expect" | ||||||
| local type, getmetatable, setmetatable, colours, str_write = type, getmetatable, setmetatable, colours, write | 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. | --- @{table.insert} alternative, but with the length stored inline. | ||||||
| local function append(out, value) | local function append(out, value) | ||||||
| @@ -343,13 +347,38 @@ local function key_compare(a, b) | |||||||
|     return false |     return false | ||||||
| end | 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) |     local obj_type = type(obj) | ||||||
|     if obj_type == "string" then |     if obj_type == "string" then | ||||||
|         local formatted = ("%q"):format(obj):gsub("\\\n", "\\n") |         local formatted = ("%q"):format(obj):gsub("\\\n", "\\n") | ||||||
|         return text(formatted, colours.red) |         return text(formatted, colours.red) | ||||||
|     elseif obj_type == "number" then |     elseif obj_type == "number" then | ||||||
|         return text(tostring(obj), colours.magenta) |         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 |     elseif obj_type ~= "table" or tracking[obj] then | ||||||
|         return text(tostring(obj), colours.lightGrey) |         return text(tostring(obj), colours.lightGrey) | ||||||
|     elseif getmetatable(obj) ~= nil and getmetatable(obj).__tostring then |     elseif getmetatable(obj) ~= nil and getmetatable(obj).__tostring then | ||||||
| @@ -371,15 +400,15 @@ local function pretty_impl(obj, tracking) | |||||||
|             local v = obj[k] |             local v = obj[k] | ||||||
|             local ty = type(k) |             local ty = type(k) | ||||||
|             if ty == "number" and k % 1 == 0 and k >= 1 and k <= length then |             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 |             elseif ty == "string" and not keywords[k] and k:match("^[%a_][%a%d_]*$") then | ||||||
|                 append(doc, text(k .. " = ")) |                 append(doc, text(k .. " = ")) | ||||||
|                 append(doc, pretty_impl(v, tracking)) |                 append(doc, pretty_impl(v, options, tracking)) | ||||||
|             else |             else | ||||||
|                 append(doc, obracket) |                 append(doc, obracket) | ||||||
|                 append(doc, pretty_impl(k, tracking)) |                 append(doc, pretty_impl(k, options, tracking)) | ||||||
|                 append(doc, cbracket) |                 append(doc, cbracket) | ||||||
|                 append(doc, pretty_impl(v, tracking)) |                 append(doc, pretty_impl(v, options, tracking)) | ||||||
|             end |             end | ||||||
|         end |         end | ||||||
|  |  | ||||||
| @@ -393,12 +422,24 @@ end | |||||||
| -- This can then be rendered with @{write} or @{print}. | -- This can then be rendered with @{write} or @{print}. | ||||||
| -- | -- | ||||||
| -- @param obj The object to pretty-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. | -- @treturn Doc The object formatted as a document. | ||||||
| -- @usage Display a table on the screen | -- @usage Display a table on the screen | ||||||
| --     local pretty = require "cc.pretty" | --     local pretty = require "cc.pretty" | ||||||
| --     pretty.print(pretty.pretty({ 1, 2, 3 })) | --     pretty.print(pretty.pretty({ 1, 2, 3 })) | ||||||
| local function pretty(obj) | local function pretty(obj, options) | ||||||
|     return pretty_impl(obj, {}) |     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 | end | ||||||
|  |  | ||||||
| return { | return { | ||||||
|   | |||||||
| @@ -141,12 +141,12 @@ return { | |||||||
|     program = program, |     program = program, | ||||||
|  |  | ||||||
|     -- Re-export various other functions |     -- Re-export various other functions | ||||||
|     help = wrap(help.completeTopic), |     help = wrap(help.completeTopic), --- Wraps @{help.completeTopic} as a @{build} compatible function. | ||||||
|     choice = wrap(completion.choice), |     choice = wrap(completion.choice), --- Wraps @{cc.completion.choice} as a @{build} compatible function. | ||||||
|     peripheral = wrap(completion.peripheral), |     peripheral = wrap(completion.peripheral), --- Wraps @{cc.completion.peripheral} as a @{build} compatible function. | ||||||
|     side = wrap(completion.side), |     side = wrap(completion.side), --- Wraps @{cc.completion.side} as a @{build} compatible function. | ||||||
|     setting = wrap(completion.setting), |     setting = wrap(completion.setting), --- Wraps @{cc.completion.setting} as a @{build} compatible function. | ||||||
|     command = wrap(completion.command), |     command = wrap(completion.command), --- Wraps @{cc.completion.command} as a @{build} compatible function. | ||||||
|  |  | ||||||
|     build = build, |     build = build, | ||||||
| } | } | ||||||
|   | |||||||
| @@ -95,7 +95,10 @@ while bRunning do | |||||||
|             local n = 1 |             local n = 1 | ||||||
|             while n < tResults.n or n <= nForcePrint do |             while n < tResults.n or n <= nForcePrint do | ||||||
|                 local value = tResults[n + 1] |                 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 |                 if ok then | ||||||
|                     pretty.print(serialised) |                     pretty.print(serialised) | ||||||
|                 else |                 else | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ | |||||||
| -- @module[module] shell | -- @module[module] shell | ||||||
|  |  | ||||||
| local expect = dofile("rom/modules/main/cc/expect.lua").expect | 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 multishell = multishell | ||||||
| local parentShell = shell | local parentShell = shell | ||||||
| @@ -28,94 +29,10 @@ local tCompletionInfo = parentShell and parentShell.getCompletionInfo() or {} | |||||||
| local tProgramStack = {} | local tProgramStack = {} | ||||||
|  |  | ||||||
| local shell = {} --- @export | local shell = {} --- @export | ||||||
| local function createShellEnv(sDir) | local function createShellEnv(dir) | ||||||
|     local tEnv = {} |     local env = { shell = shell, multishell = multishell } | ||||||
|     tEnv.shell = shell |     env.require, env.package = make_package(env, dir) | ||||||
|     tEnv.multishell = multishell |     return env | ||||||
|  |  | ||||||
|     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 |  | ||||||
| end | end | ||||||
|  |  | ||||||
| -- Colours | -- Colours | ||||||
|   | |||||||
							
								
								
									
										92
									
								
								src/test/resources/test-rom/spec/apis/redstone_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/test/resources/test-rom/spec/apis/redstone_spec.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | |||||||
|  | local function it_side(func, ...) | ||||||
|  |     local arg = table.pack(...) | ||||||
|  |     it("requires a specific side", function() | ||||||
|  |         expect.error(func, 0):eq("bad argument #1 (string expected, got number)") | ||||||
|  |         expect.error(func, "blah", table.unpack(arg)):eq("Invalid side.") | ||||||
|  |  | ||||||
|  |         func("top", table.unpack(arg)) | ||||||
|  |         func("Top", table.unpack(arg)) | ||||||
|  |         func("toP", table.unpack(arg)) | ||||||
|  |     end) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | describe("The redstone library", function() | ||||||
|  |     describe("redstone.setOutput", function() | ||||||
|  |         it_side(redstone.setOutput, false) | ||||||
|  |  | ||||||
|  |         it("sets the output strength correctly", function() | ||||||
|  |             redstone.setOutput("top", false) | ||||||
|  |             expect(redstone.getAnalogueOutput("top")):eq(0) | ||||||
|  |  | ||||||
|  |             redstone.setOutput("top", true) | ||||||
|  |             expect(redstone.getAnalogueOutput("top")):eq(15) | ||||||
|  |         end) | ||||||
|  |     end) | ||||||
|  |  | ||||||
|  |     describe("redstone.getOutput", function() | ||||||
|  |         it_side(redstone.getOutput) | ||||||
|  |  | ||||||
|  |         it("gets the output strength correctly", function() | ||||||
|  |             redstone.setAnalogueOutput("top", 0) | ||||||
|  |             expect(redstone.getOutput("top")):eq(false) | ||||||
|  |  | ||||||
|  |             redstone.setAnalogueOutput("top", 1) | ||||||
|  |             expect(redstone.getOutput("top")):eq(true) | ||||||
|  |  | ||||||
|  |             redstone.setAnalogueOutput("top", 15) | ||||||
|  |             expect(redstone.getOutput("top")):eq(true) | ||||||
|  |         end) | ||||||
|  |     end) | ||||||
|  |  | ||||||
|  |     describe("redstone.getInput", function() | ||||||
|  |         it_side(redstone.getInput) | ||||||
|  |     end) | ||||||
|  |  | ||||||
|  |     describe("redstone.setAnalogueOutput", function() | ||||||
|  |         it_side(redstone.setAnalogueOutput, 0) | ||||||
|  |  | ||||||
|  |         it("checks the strength parameter", function() | ||||||
|  |             expect.error(redstone.setAnalogueOutput, "top", true):eq("bad argument #2 (number expected, got boolean)") | ||||||
|  |             expect.error(redstone.setAnalogueOutput, "top", 0 / 0):eq("bad argument #2 (number expected, got nan)") | ||||||
|  |             expect.error(redstone.setAnalogueOutput, "top", math.huge):eq("bad argument #2 (number expected, got inf)") | ||||||
|  |             expect.error(redstone.setAnalogueOutput, "top", -1):eq("Expected number in range 0-15") | ||||||
|  |             expect.error(redstone.setAnalogueOutput, "top", 16):eq("Expected number in range 0-15") | ||||||
|  |         end) | ||||||
|  |     end) | ||||||
|  |  | ||||||
|  |     describe("redstone.getAnalogueOutput", function() | ||||||
|  |         it_side(redstone.getAnalogueOutput) | ||||||
|  |     end) | ||||||
|  |  | ||||||
|  |     describe("redstone.getAnalogueInput", function() | ||||||
|  |         it_side(redstone.getAnalogueInput) | ||||||
|  |     end) | ||||||
|  |  | ||||||
|  |     describe("redstone.setBundledOutput", function() | ||||||
|  |         it_side(redstone.setBundledOutput, 0) | ||||||
|  |  | ||||||
|  |         it("checks the mask parameter", function() | ||||||
|  |             expect.error(redstone.setBundledOutput, "top", true):eq("bad argument #2 (number expected, got boolean)") | ||||||
|  |             expect.error(redstone.setBundledOutput, "top", 0 / 0):eq("bad argument #2 (number expected, got nan)") | ||||||
|  |             expect.error(redstone.setBundledOutput, "top", math.huge):eq("bad argument #2 (number expected, got inf)") | ||||||
|  |         end) | ||||||
|  |     end) | ||||||
|  |  | ||||||
|  |     describe("redstone.getBundledOutput", function() | ||||||
|  |         it_side(redstone.getBundledOutput) | ||||||
|  |     end) | ||||||
|  |  | ||||||
|  |     describe("redstone.getBundledInput", function() | ||||||
|  |         it_side(redstone.getBundledInput) | ||||||
|  |     end) | ||||||
|  |  | ||||||
|  |     describe("redstone.testBundledInput", function() | ||||||
|  |         it_side(redstone.testBundledInput, 0) | ||||||
|  |  | ||||||
|  |         it("checks the mask parameter", function() | ||||||
|  |             expect.error(redstone.testBundledInput, "top", true):eq("bad argument #2 (number expected, got boolean)") | ||||||
|  |             expect.error(redstone.testBundledInput, "top", 0 / 0):eq("bad argument #2 (number expected, got nan)") | ||||||
|  |             expect.error(redstone.testBundledInput, "top", math.huge):eq("bad argument #2 (number expected, got inf)") | ||||||
|  |         end) | ||||||
|  |     end) | ||||||
|  | end) | ||||||
| @@ -165,7 +165,7 @@ describe("cc.pretty", function() | |||||||
|     describe("pretty", function() |     describe("pretty", function() | ||||||
|         -- We make use of "render" here, as it's considerably easier than checking against the actual structure. |         -- 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. |         -- 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() |         describe("tables", function() | ||||||
|             it("displays empty tables", function() |             it("displays empty tables", function() | ||||||
| @@ -201,8 +201,21 @@ describe("cc.pretty", function() | |||||||
|             expect(pretty("hello\nworld")):eq('"hello\\nworld"') |             expect(pretty("hello\nworld")):eq('"hello\\nworld"') | ||||||
|         end) |         end) | ||||||
|  |  | ||||||
|         it("shows functions", function() |         describe("functions", function() | ||||||
|             expect(pretty(pretty)):eq(tostring(pretty)) |             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) |     end) | ||||||
| end) | end) | ||||||
|   | |||||||
| @@ -0,0 +1,79 @@ | |||||||
|  | describe("cc.require", function() | ||||||
|  |     local r = require "cc.require" | ||||||
|  |     local function mk() | ||||||
|  |         local env = setmetatable({}, { __index = _ENV }) | ||||||
|  |         env.require, env.package = r.make({}, "/test-files/modules") | ||||||
|  |         return env.require, env.package | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     local function setup(path, contents) | ||||||
|  |         fs.delete("/test-files/modules") | ||||||
|  |         io.open(path, "w"):write(contents):close() | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     describe("require", function() | ||||||
|  |         it("errors on recursive modules", function() | ||||||
|  |             local require, package = mk() | ||||||
|  |             package.preload.pkg = function() require "pkg" end | ||||||
|  |             expect.error(require, "pkg"):eq("loop or previous error loading module 'pkg'") | ||||||
|  |         end) | ||||||
|  |  | ||||||
|  |         it("supplies the current module name", function() | ||||||
|  |             local require, package = mk() | ||||||
|  |             package.preload.pkg = table.pack | ||||||
|  |             expect(require("pkg")):same { n = 1, "pkg" } | ||||||
|  |         end) | ||||||
|  |  | ||||||
|  |         it("returns true instead of nil", function() | ||||||
|  |             local require, package = mk() | ||||||
|  |             package.preload.pkg = function() return nil end | ||||||
|  |             expect(require("pkg")):eq(true) | ||||||
|  |         end) | ||||||
|  |  | ||||||
|  |         it("returns a constant value", function() | ||||||
|  |             local require, package = mk() | ||||||
|  |             package.preload.pkg = function() return {} end | ||||||
|  |             expect(require("pkg")):eq(require("pkg")) | ||||||
|  |         end) | ||||||
|  |  | ||||||
|  |         it("returns an error on not-found modules", function() | ||||||
|  |             local require, package = mk() | ||||||
|  |             package.path = "/?;/?.lua" | ||||||
|  |             expect.error(require, "pkg"):eq( | ||||||
|  |                 "module 'pkg' not found:\n" .. | ||||||
|  |                 "  no field package.preload['pkg']\n" .. | ||||||
|  |                 "  no file '/pkg'\n" .. | ||||||
|  |                 "  no file '/pkg.lua'") | ||||||
|  |         end) | ||||||
|  |     end) | ||||||
|  |  | ||||||
|  |     describe("the file loader", function() | ||||||
|  |         local function get(path) | ||||||
|  |             local require, package = mk() | ||||||
|  |             if path then package.path = path end | ||||||
|  |             return require | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         it("works on absolute paths", function() | ||||||
|  |             local require = get("/test-files/?.lua") | ||||||
|  |             setup("test-files/some_module.lua", "return 123") | ||||||
|  |             expect(require("some_module")):eq(123) | ||||||
|  |         end) | ||||||
|  |  | ||||||
|  |         it("works on relative paths", function() | ||||||
|  |             local require = get("?.lua") | ||||||
|  |             setup("test-files/modules/some_module.lua", "return 123") | ||||||
|  |             expect(require("some_module")):eq(123) | ||||||
|  |         end) | ||||||
|  |  | ||||||
|  |         it("fails on syntax errors", function() | ||||||
|  |             local require = get("?.lua") | ||||||
|  |             setup("test-files/modules/some_module.lua", "1") | ||||||
|  |             expect.error(require, "some_module"):str_match( | ||||||
|  |                 "^module 'some_module' not found:\n" .. | ||||||
|  |                 "  no field package.preload%['some_module'%]\n" .. | ||||||
|  |                 "  [^:]*some_module.lua:1: unexpected symbol near '1'$" | ||||||
|  |             ) | ||||||
|  |         end) | ||||||
|  |     end) | ||||||
|  | end) | ||||||
		Reference in New Issue
	
	Block a user
	 SquidDev
					SquidDev