mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 05:33:00 +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 | ||||
|  | ||||
| --- 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 | ||||
|  | ||||
| --- 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 | ||||
|  | ||||
| --- 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 setBundledOutput(side, output) end | ||||
| function getBundledOutput(side) end | ||||
| function getBundledInput(side) end | ||||
| function testBundledInput(side, mask) end | ||||
|  | ||||
| --- Set the redstone signal strength for a specific side. | ||||
| -- | ||||
| -- @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 | ||||
| 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 | ||||
| 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 | ||||
| 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/http.lua | ||||
|    /doc/stub/os.lua | ||||
|    /doc/stub/redstone.lua | ||||
|    /doc/stub/term.lua | ||||
|    /doc/stub/turtle.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/modules/main/cc/shell/completion.lua) | ||||
|    /src/main/resources/*/computercraft/lua/rom/apis/window.lua) | ||||
|  | ||||
|   (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/modules/main/cc/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) | ||||
|   (linters -doc:unresolved-reference)) | ||||
|  | ||||
|   | ||||
| @@ -16,11 +16,11 @@ import static dan200.computercraft.api.lua.ArgumentHelper.*; | ||||
|  | ||||
| public class RedstoneAPI implements ILuaAPI | ||||
| { | ||||
|     private IAPIEnvironment m_environment; | ||||
|     private final IAPIEnvironment environment; | ||||
|  | ||||
|     public RedstoneAPI( IAPIEnvironment environment ) | ||||
|     { | ||||
|         m_environment = environment; | ||||
|         this.environment = environment; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -63,31 +63,31 @@ public class RedstoneAPI implements ILuaAPI | ||||
|                 // setOutput | ||||
|                 ComputerSide side = parseSide( args ); | ||||
|                 boolean output = getBoolean( args, 1 ); | ||||
|                 m_environment.setOutput( side, output ? 15 : 0 ); | ||||
|                 environment.setOutput( side, output ? 15 : 0 ); | ||||
|                 return null; | ||||
|             } | ||||
|             case 2: // getOutput | ||||
|                 return new Object[] { m_environment.getOutput( parseSide( args ) ) > 0 }; | ||||
|                 return new Object[] { environment.getOutput( parseSide( args ) ) > 0 }; | ||||
|             case 3: // getInput | ||||
|                 return new Object[] { m_environment.getInput( parseSide( args ) ) > 0 }; | ||||
|                 return new Object[] { environment.getInput( parseSide( args ) ) > 0 }; | ||||
|             case 4: | ||||
|             { | ||||
|                 // setBundledOutput | ||||
|                 ComputerSide side = parseSide( args ); | ||||
|                 int output = getInt( args, 1 ); | ||||
|                 m_environment.setBundledOutput( side, output ); | ||||
|                 environment.setBundledOutput( side, output ); | ||||
|                 return null; | ||||
|             } | ||||
|             case 5: // getBundledOutput | ||||
|                 return new Object[] { m_environment.getBundledOutput( parseSide( args ) ) }; | ||||
|                 return new Object[] { environment.getBundledOutput( parseSide( args ) ) }; | ||||
|             case 6: // getBundledInput | ||||
|                 return new Object[] { m_environment.getBundledInput( parseSide( args ) ) }; | ||||
|                 return new Object[] { environment.getBundledInput( parseSide( args ) ) }; | ||||
|             case 7: | ||||
|             { | ||||
|                 // testBundledInput | ||||
|                 ComputerSide side = parseSide( args ); | ||||
|                 int mask = getInt( args, 1 ); | ||||
|                 int input = m_environment.getBundledInput( side ); | ||||
|                 int input = environment.getBundledInput( side ); | ||||
|                 return new Object[] { (input & mask) == mask }; | ||||
|             } | ||||
|             case 8: | ||||
| @@ -100,15 +100,15 @@ public class RedstoneAPI implements ILuaAPI | ||||
|                 { | ||||
|                     throw new LuaException( "Expected number in range 0-15" ); | ||||
|                 } | ||||
|                 m_environment.setOutput( side, output ); | ||||
|                 environment.setOutput( side, output ); | ||||
|                 return null; | ||||
|             } | ||||
|             case 10: | ||||
|             case 11: // getAnalogOutput/getAnalogueOutput | ||||
|                 return new Object[] { m_environment.getOutput( parseSide( args ) ) }; | ||||
|                 return new Object[] { environment.getOutput( parseSide( args ) ) }; | ||||
|             case 12: | ||||
|             case 13: // getAnalogInput/getAnalogueInput | ||||
|                 return new Object[] { m_environment.getInput( parseSide( args ) ) }; | ||||
|                 return new Object[] { environment.getInput( parseSide( args ) ) }; | ||||
|             default: | ||||
|                 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.]], | ||||
|     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 | ||||
|   | ||||
							
								
								
									
										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() | ||||
|         -- 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) | ||||
|   | ||||
| @@ -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