diff --git a/.editorconfig b/.editorconfig index f11468850..d57b269a4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,5 +14,9 @@ trim_trailing_whitespace = false [*.sexp] indent_size = 2 +[*.yml] +indent_size = 2 + + [*.properties] insert_final_newline = false diff --git a/.github/workflows/make-doc.sh b/.github/workflows/make-doc.sh new file mode 100755 index 000000000..e447c3046 --- /dev/null +++ b/.github/workflows/make-doc.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -eu + +DEST="${GITHUB_REF#refs/*/}" +echo "Uploading docs to https://tweaked.cc/$DEST" + +# Setup ssh key +mkdir -p "$HOME/.ssh/" +echo "$SSH_KEY" > "$HOME/.ssh/key" +chmod 600 "$HOME/.ssh/key" + +# And upload +rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \ + "$GITHUB_WORKSPACE/doc/" \ + "$SSH_USER@$SSH_HOST:/var/www/tweaked.cc/$DEST" diff --git a/.github/workflows/make-doc.yml b/.github/workflows/make-doc.yml new file mode 100644 index 000000000..1d5e4605d --- /dev/null +++ b/.github/workflows/make-doc.yml @@ -0,0 +1,29 @@ +name: Build documentation + +on: + push: + branches: [ master ] + tags: + +jobs: + make_doc: + name: Build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + + - name: Build documentation + run: | + test -d bin || mkdir bin + test -f bin/illuaminate || wget -q -Obin/illuaminate https://squiddev.cc/illuaminate/linux-x86-64/illuaminate + chmod +x bin/illuaminate + bin/illuaminate doc-gen + + - name: Upload documentation + run: .github/workflows/make-doc.sh 2> /dev/null + env: + SSH_KEY: ${{ secrets.SSH_KEY }} + SSH_USER: ${{ secrets.SSH_USER }} + SSH_HOST: ${{ secrets.SSH_HOST }} + SSH_PORT: ${{ secrets.SSH_PORT }} diff --git a/.gitignore b/.gitignore index 80c50f9b8..cc7ddcea3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /logs /build /out +/doc/**/*.html # Runtime directories /run diff --git a/README.md b/README.md index 692e7bb48..fd3003786 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ![CC: Tweaked](logo.png) +# ![CC: Tweaked](doc/logo.png) [![Current build status](https://github.com/SquidDev-CC/CC-Tweaked/workflows/Build/badge.svg)](https://github.com/SquidDev-CC/CC-Tweaked/actions "Current build status") [![Download CC: Tweaked on CurseForge](http://cf.way2muchnoise.eu/title/cc-tweaked.svg)](https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked on CurseForge") CC: Tweaked is a fork of [ComputerCraft](https://github.com/dan200/ComputerCraft), adding programmable computers, diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 000000000..efc2afafa --- /dev/null +++ b/doc/index.md @@ -0,0 +1,13 @@ +# CC: Tweaked + +This is a small website to test documentation generation from Lua source code. + +This is still very much in the proof-of-concept stage. We've rolled own own documentation +generation tool, and there's a couple of missing features. Furthermore, Java-based APIs +(such as Lua builtins, or @{os}) are not documented. + +For more information, please check out [the GitHub issue][gh_issue] and [the +documented source][gh_branch]. + +[gh_issue]: https://github.com/SquidDev-CC/CC-Tweaked/issues/133 +[gh_branch]: https://github.com/SquidDev-CC/CC-Tweaked/tree/feature/doc-gen diff --git a/logo.png b/doc/logo.png similarity index 100% rename from logo.png rename to doc/logo.png diff --git a/doc/stub/commands.lua b/doc/stub/commands.lua new file mode 100644 index 000000000..89b0f604b --- /dev/null +++ b/doc/stub/commands.lua @@ -0,0 +1,6 @@ +function exec(command) end +function execAsync(commad) end +function list() end +function getBlockPosition() end +function getBlockInfos(min_x, min_y, min_z, max_x, max_y, max_z) end +function getBlockInfo(x, y, z) end diff --git a/doc/stub/fs.lua b/doc/stub/fs.lua new file mode 100644 index 000000000..73d578e01 --- /dev/null +++ b/doc/stub/fs.lua @@ -0,0 +1,42 @@ +--- The FS API allows you to manipulate files and the filesystem. +-- +-- @module fs + +function list(path) end +function combine(base, child) end +function getName(path) end +function getSize(path) end +function exists(path) end +function isDir(path) end +function isReadOnly(path) end +function makeDir(path) end +function move(from, to) end +function copy(from, to) end +function delete(path) end +function open(path, mode) end +function getDrive(path) end +function getFreeSpace(path) end +function find(pattern) end +function getDir(path) end + +--- A file handle which can be read from. +-- +-- @type ReadHandle +-- @see fs.open +local ReadHandle = {} +function ReadHandle.read(count) end +function ReadHandle.readAll() end +function ReadHandle.readLine(with_trailing) end +function ReadHandle.seek(whence, offset) end +function ReadHandle.close() end + +--- A file handle which can be written to. +-- +-- @type WriteHandle +-- @see fs.open +local WriteHandle = {} +function WriteHandle.write(text) end +function WriteHandle.writeLine(text) end +function WriteHandle.flush(text) end +function WriteHandle.seek(whence, offset) end +function WriteHandle.close() end diff --git a/doc/stub/http.lua b/doc/stub/http.lua new file mode 100644 index 000000000..8e7df608f --- /dev/null +++ b/doc/stub/http.lua @@ -0,0 +1,213 @@ +--- The http library allows communicating with web servers, sending and +-- receiving data from them. +-- +-- #### `http_check` event +-- +-- @module http + +--- Asynchronously make a HTTP request to the given url. +-- +-- This returns immediately, a [`http_success`](#http-success-event) or +-- [`http_failure`](#http-failure-event) will be queued once the request has +-- completed. +-- +-- @tparam string url The url to request +-- @tparam[opt] string body An optional string containing the body of the +-- request. If specified, a `POST` request will be made instead. +-- @tparam[opt] { [string] = string } headers Additional headers to send as part +-- of this request. +-- @tparam[opt] boolean binary Whether to make a binary HTTP request. If true, +-- the body will not be UTF-8 encoded, and the received response will not be +-- decoded. +-- +-- @tparam[2] { +-- url = string, body? = string, headers? = { [string] = string }, +-- binary? = boolean, method? = string, redirect? = boolean, +-- } request Options for the request. +-- +-- This table form is an expanded version of the previous syntax. All arguments +-- from above are passed in as fields instead (for instance, +-- `http.request("https://example.com")` becomes `http.request { url = +-- "https://example.com" }`). +-- +-- This table also accepts several additional options: +-- +-- - `method`: Which HTTP method to use, for instance `"PATCH"` or `"DELETE"`. +-- - `redirect`: Whether to follow HTTP redirects. Defaults to true. +-- +-- @see http.get For a synchronous way to make GET requests. +-- @see http.post For a synchronous way to make POST requests. +function request(...) end + +--- Make a HTTP GET request to the given url. +-- +-- @tparam string url The url to request +-- @tparam[opt] { [string] = string } headers Additional headers to send as part +-- of this request. +-- @tparam[opt] boolean binary Whether to make a binary HTTP request. If true, +-- the body will not be UTF-8 encoded, and the received response will not be +-- decoded. +-- +-- @tparam[2] { +-- url = string, headers? = { [string] = string }, +-- binary? = boolean, method? = string, redirect? = boolean, +-- } request Options for the request. See @{http.request} for details on how +-- these options behave. +-- +-- @treturn Response The resulting http response, which can be read from. +-- @treturn[2] nil When the http request failed, such as in the event of a 404 +-- error or connection timeout. +-- @treturn string A message detailing why the request failed. +-- @treturn Response|nil The failing http response, if available. +-- +-- @usage Make a request to [example.computercraft.cc](https://example.computercraft.cc), +-- and print the returned page. +-- ```lua +-- local request = http.get("https://example.computercraft.cc") +-- print(request.readAll()) +-- -- => HTTP is working! +-- request.close() +-- ``` +function get(...) end + +--- Make a HTTP POST request to the given url. +-- +-- @tparam string url The url to request +-- @tparam string body The body of the POST request. +-- @tparam[opt] { [string] = string } headers Additional headers to send as part +-- of this request. +-- @tparam[opt] boolean binary Whether to make a binary HTTP request. If true, +-- the body will not be UTF-8 encoded, and the received response will not be +-- decoded. +-- +-- @tparam[2] { +-- url = string, body? = string, headers? = { [string] = string }, +-- binary? = boolean, method? = string, redirect? = boolean, +-- } request Options for the request. See @{http.request} for details on how +-- these options behave. +-- +-- @treturn Response The resulting http response, which can be read from. +-- @treturn[2] nil When the http request failed, such as in the event of a 404 +-- error or connection timeout. +-- @treturn string A message detailing why the request failed. +-- @treturn Response|nil The failing http response, if available. +function post(...) end + +--- A http response. This acts very much like a @{fs.ReadHandle|file}, though +-- provides some http specific methods. +-- +-- #### `http_success` event +-- #### `http_failure` event +-- +-- @type Response +-- @see http.request On how to make a http request. +local Response = {} + +--- Returns the response code and response message returned by the server +-- +-- @treturn number The response code (i.e. 200) +-- @treturn string The response message (i.e. "OK") +function Response.getResponseCode() end + +--- Get a table containing the response's headers, in a format similar to that +-- required by @{http.request}. If multiple headers are sent with the same +-- name, they will be combined with a comma. +-- +-- @treturn { [string]=string } The response's headers. +-- Make a request to [example.computercraft.cc](https://example.computercraft.cc), +-- and print the returned headers. +-- ```lua +-- local request = http.get("https://example.computercraft.cc") +-- print(textutils.serialize(request.getResponseHeaders())) +-- -- => { +-- -- [ "Content-Type" ] = "text/plain; charset=utf8", +-- -- [ "content-length" ] = 17, +-- -- ... +-- -- } +-- request.close() +-- ``` +function Response.getResponseHeaders() end + +function Response.read(count) end +function Response.readAll() end +function Response.readLine(with_trailing) end +function Response.seek(whence, offset) end +function Response.close() end + +--- Asynchronously determine whether a URL can be requested. +-- +-- If this returns `true`, one should also listen for [`http_check` +-- events](#http-check-event) which will container further information about +-- whether the URL is allowed or not. +-- +-- @tparam string url The URL to check. +-- @treturn true When this url is not invalid. This does not imply that it is +-- allowed - see the comment above. +-- @treturn[2] false When this url is invalid. +-- @treturn string A reason why this URL is not valid (for instance, if it is +-- malformed, or blocked). +-- +-- @see http.checkURL For a synchronous version. +function checkURLAsync(url) end + +--- Determine whether a URL can be requested. +-- +-- If this returns `true`, one should also listen for [`http_check` +-- events](#http-check-event) which will container further information about +-- whether the URL is allowed or not. +-- +-- @tparam string url The URL to check. +-- @treturn true When this url is valid and can be requested via @{http.request}. +-- @treturn[2] false When this url is invalid. +-- @treturn string A reason why this URL is not valid (for instance, if it is +-- malformed, or blocked). +-- +-- @see http.checkURLAsync For an asynchronous version. +-- +-- @usage +-- ```lua +-- print(http.checkURL("https://example.computercraft.cc/")) +-- -- => true +-- print(http.checkURL("http://localhost/")) +-- -- => false Domain not permitted +-- print(http.checkURL("not a url")) +-- -- => false URL malformed +-- ``` +function checkURL(url) end + +--- Open a websocket. +-- +-- @tparam string url The websocket url to connect to. This should have the +-- `ws://` or `wss://` protocol. +-- @tparam[opt] { [string] = string } headers Additional headers to send as part +-- of the initial websocket connection. +-- +-- @treturn Websocket The websocket connection. +-- @treturn[2] false If the websocket connection failed. +-- @treturn string An error message describing why the connection failed. +function websocket(url, headers) end + +--- Asynchronously open a websocket. +-- +-- This returns immediately, a [`websocket_success`](#websocket-success-event) +-- or [`websocket_failure`](#websocket-failure-event) will be queued once the +-- request has completed. +-- +-- @tparam string url The websocket url to connect to. This should have the +-- `ws://` or `wss://` protocol. +-- @tparam[opt] { [string] = string } headers Additional headers to send as part +-- of the initial websocket connection. +function websocketAsync(url, headers) end + + + +--- A websocket, which can be used to send an receive messages with a web +-- server. +-- +-- @type Websocket +-- @see http.websocket On how to open a websocket. +local Websocket = {} + +function Websocket.send(message, binary) end +function Websocket.receive() end +function Websocket.close() end diff --git a/doc/stub/os.lua b/doc/stub/os.lua new file mode 100644 index 000000000..163d2c2b0 --- /dev/null +++ b/doc/stub/os.lua @@ -0,0 +1,17 @@ +function queueEvent(event, ...) end +function startTimer(delay) end +function setAlarm(time) end +function shutdown() end +function reboot() end +function getComputerID() end +computerID = getComputerID +function setComputerLabel(label) end +function getComputerLabel() end +computerLabel = getComputerLabel +function clock() end +function time(timezone) end +function day(timezone) end +function cancelTimer(id) end +function cancelAlarm(id) end +function epoch(timezone) end +function date(format, time) end diff --git a/doc/stub/redstone.lua b/doc/stub/redstone.lua new file mode 100644 index 000000000..cf10a45ca --- /dev/null +++ b/doc/stub/redstone.lua @@ -0,0 +1,14 @@ +function getSides() end +function setOutput(side, on) end +function getOutput(side) end +function getInput(side) end +function setBundledOutput(side, output) end +function getBundledOutput(side) end +function getBundledInput(side) end +function testBundledInput(side, mask) end +function setAnalogOutput(side, value) end +setAnalogueOutput = setAnalogOutput +function getAnalogOutput(sid) end +getAnalogueOutput = getAnalogOutput +function getAnalogInput(side) end +getAnalogueInput = getAnaloguInput diff --git a/doc/stub/term.lua b/doc/stub/term.lua new file mode 100644 index 000000000..2111949b6 --- /dev/null +++ b/doc/stub/term.lua @@ -0,0 +1,52 @@ +function write(text) end +function scroll(lines) end +function setCursorPos(x, y) end +function setCursorBlink(blink) end +function getCursorPos() end +function getSize() end +function clear() end +function clearLine() end +function setTextColour(colour) end +setTextColor = setTextColour +function setBackgroundColour(colour) end +setBackgroundColor = setBackgroundColour +function isColour() end +isColor = isColour +function getTextColour() end +getTextColor = getTextColor +function getBackgroundColour() end +getBackgroundColour = getBackgroundColour +function blit(text, text_colours, background_colours) end +function setPaletteColour(colour, ...) end +setPaletteColour = setPaletteColour +function getPaletteColour(colour, ...) end +getPaletteColour = getPaletteColour +function nativePaletteColour(colour) end +nativePaletteColour = nativePaletteColour + +--- @type Redirect +local Redirect = {} + +Redirect.write = write +Redirect.scroll = scroll +Redirect.setCursorPos = setCursorPos +Redirect.setCursorBlink = setCursorBlink +Redirect.getCursorPos = getCursorPos +Redirect.getSize = getSize +Redirect.clear = clear +Redirect.clearLine = clearLine +Redirect.setTextColour = setTextColour +Redirect.setTextColor = setTextColor +Redirect.setBackgroundColour = setBackgroundColour +Redirect.setBackgroundColor = setBackgroundColor +Redirect.isColour = isColour +Redirect.isColor = isColor +Redirect.getTextColour = getTextColour +Redirect.getTextColor = getTextColor +Redirect.getBackgroundColour = getBackgroundColour +Redirect.getBackgroundColor = getBackgroundColor +Redirect.blit = blit +Redirect.setPaletteColour = setPaletteColour +Redirect.setPaletteColor = setPaletteColor +Redirect.getPaletteColour = getPaletteColour +Redirect.getPaletteColor = getPaletteColor diff --git a/doc/stub/turtle.lua b/doc/stub/turtle.lua new file mode 100644 index 000000000..0baa4c6fa --- /dev/null +++ b/doc/stub/turtle.lua @@ -0,0 +1,42 @@ +function forward() end +function back() end +function up() end +function down() end +function turnLeft() end +function turnRight() end +function dig(side) end +function digUp(side) end +function digDown(side) end +function place() end +function placeUp() end +function placeDown() end +function drop(count) end +function select(slot) end +function getItemCount(slot) end +function getItemSpace(slot) end +function detect() end +function detectUp() end +function detectDown() end +function compare() end +function compareUp() end +function compareDown() end +function attack(side) end +function attackUp(side) end +function attackDown(side) end +function dropUp(count) end +function dropDown(count) end +function suck(count) end +function suckUp(count) end +function suckDown(count) end +function getFuelLevel() end +function refuel(count) end +function compareTo(slot) end +function transferTo(slot, count) end +function getSelectedSlot() end +function getFuelLimit() end +function equipLeft() end +function equipRight() end +function inspect() end +function inspectUp() end +function inspectDown() end +function getItemDetail(slot) end diff --git a/doc/styles.css b/doc/styles.css new file mode 100644 index 000000000..ef14a7d94 --- /dev/null +++ b/doc/styles.css @@ -0,0 +1,186 @@ +/* Basic reset on elements */ +h1, h2, h3, h4, p, table, div, body { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* Make the page a little more airy */ +body { + margin: 20px auto; + max-width: 1200px; + padding: 0 10px; + line-height: 1.6; + color: #222; + background: #fff; +} + +/* Try to use system default fonts. */ +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", + "Droid Sans", "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; +} + +code, pre, .parameter, .type, .definition-name, .reference { + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; +} + +/* Some definitions of basic tags */ +code { + color: #c7254e; + background-color: #f9f2f4; +} + +p { + margin: 0.9em 0; +} + +h1 { + font-size: 1.5em; + font-weight: lighter; + border-bottom: solid 1px #aaa; +} + +h2, h3, h4 { margin: 1.4em 0 0.3em;} +h2 { font-size: 1.25em; } +h3 { font-size: 1.15em; font-weight: bold; } +h4 { font-size: 1.06em; } + +a, a:visited, a:active { font-weight: bold; color: #004080; text-decoration: none; } +a:hover { text-decoration: underline; } + +blockquote { margin-left: 3em; } + +/* Stop sublists from having initial vertical space */ +ul ul { margin-top: 0px; } +ol ul { margin-top: 0px; } +ol ol { margin-top: 0px; } +ul ol { margin-top: 0px; } + +/* Make the target distinct; helps when we're navigating to a function */ +a:target + * { background-color: #FFFF99; } + +/* Allow linking to any subsection */ +a[name]::before { content: "#"; } + +/* Layout */ +#main { + display: flex; + flex-wrap: nowrap; + justify-content: space-between; + min-height: calc(100vh - 100px); +} + +#main > nav { + flex-basis: 30%; + min-width: 150px; + max-width: 250px; + background-color: #f0f0f0; +} + +nav h1, nav ul { padding: 0em 10px; } + +nav h2 { + background-color:#e7e7e7; + font-size: 1.1em; + color:#000000; + padding: 5px 10px; +} + +nav ul { + list-style-type: none; + margin: 0; +} + +#content { + flex-shrink: 1; + flex-basis: 80%; + padding: 0px 10px; +} + +footer { + text-align: right; + font-size: 0.8em; +} + +/* The definition lists at the top of each page */ +table.definition-list { + border-collapse: collapse; + width: 100%; +} + +table.definition-list td, table.definition-list th { + border: 1px solid #cccccc; + padding: 5px; +} + +table.definition-list th { + background-color: #f0f0f0; + min-width: 200px; + white-space: nowrap; + text-align: right; +} + +table.definition-list td { width: 100%; } + +dl.definition dt { + border-top: 1px solid #ccc; + padding-top: 1em; + display: flex; +} + +dl.definition dt .definition-name { + padding: 0 0.1em; + margin: 0 0.1em; + flex-grow: 1; +} + + +dl.definition dd { + padding-bottom: 1em; + margin: 10px 0 0 20px; +} + +dl.definition h3 { + font-size: .95em; +} + +/* Links to source-code */ +.source-link { font-size: 0.8em; } +.source-link::before { content: '[' } +.source-link::after { content: ']' } +a.source-link, a.source-link:visited, a.source-link:active { color: #505050; } + +/* Method definitions */ +span.parameter:after { content:":"; padding-left: 0.3em; } +.optional { text-decoration: underline dotted; } + +/** Fancy colour display. */ +.colour-ref { + display: inline-block; + width: 0.8em; + height: 0.8em; + margin: 0.1em 0.1em 0.3em 0.1em; /* Terrrible hack to force vertical alignment. */ + border: solid 1px black; + box-sizing: border-box; + vertical-align: middle; +} + +/* styles for prettification of source */ +.highlight .comment { color: #558817; } +.highlight .constant { color: #a8660d; } +.highlight .escape { color: #844631; } +.highlight .keyword { color: #aa5050; font-weight: bold; } +.highlight .library { color: #0e7c6b; } +.highlight .marker { color: #512b1e; background: #fedc56; font-weight: bold; } +.highlight .string { color: #8080ff; } +.highlight .literal-kw { color: #8080ff; } +.highlight .number { color: #f8660d; } +.highlight .operator { color: #2239a8; font-weight: bold; } +.highlight .preprocessor, pre .prepro { color: #a33243; } +.highlight .global { color: #800080; } +.highlight .user-keyword { color: #800080; } +.highlight .prompt { color: #558817; } +.highlight .url { color: #272fc2; text-decoration: underline; } diff --git a/illuaminate.sexp b/illuaminate.sexp index cda3d0a85..0a8fe626e 100644 --- a/illuaminate.sexp +++ b/illuaminate.sexp @@ -1,10 +1,28 @@ ; -*- mode: Lisp;-*- (sources + /doc/stub/ /src/main/resources/assets/computercraft/lua/bios.lua /src/main/resources/assets/computercraft/lua/rom/ /src/test/resources/test-rom) + +(doc + (title "CC: Tweaked") + (index doc/index.md) + (source-link https://github.com/SquidDev-CC/CC-Tweaked/blob/${commit}/${path}#L${line}) + + (library-path + /doc/stub/ + + /src/main/resources/assets/computercraft/lua/rom/apis + /src/main/resources/assets/computercraft/lua/rom/apis/command + /src/main/resources/assets/computercraft/lua/rom/apis/turtle + + /src/main/resources/assets/computercraft/lua/rom/modules/main + /src/main/resources/assets/computercraft/lua/rom/modules/command + /src/main/resources/assets/computercraft/lua/rom/modules/turtle)) + (at / (linters ;; It'd be nice to avoid this, but right now there's a lot of instances of @@ -16,9 +34,9 @@ -var:unused-arg ;; Suppress a couple of documentation comments warnings for now. We'll - ;; hopefully be able to remove them in the coming weeks. - -doc:detached-comment -doc:undocumented -doc:undocumented-arg - -doc:unresolved-reference)) + ;; hopefully be able to remove them in the future. + -doc:undocumented -doc:undocumented-arg -doc:unresolved-reference + -var:unresolved-member)) ;; We disable the unused global linter in bios.lua and the APIs. In the future ;; hopefully we'll get illuaminate to handle this. @@ -26,5 +44,9 @@ (/src/main/resources/assets/computercraft/lua/bios.lua /src/main/resources/assets/computercraft/lua/rom/apis/) (linters -var:unused-global) - (lint - (allow-toplevel-global true))) + (lint (allow-toplevel-global true))) + +;; Silence some variable warnings in documentation stubs. +(at /doc/stub + (linters -var:unused-global) + (lint (allow-toplevel-global true))) diff --git a/src/main/resources/assets/computercraft/lua/bios.lua b/src/main/resources/assets/computercraft/lua/bios.lua index a7f7a9d21..377255845 100644 --- a/src/main/resources/assets/computercraft/lua/bios.lua +++ b/src/main/resources/assets/computercraft/lua/bios.lua @@ -21,7 +21,7 @@ if _VERSION == "Lua 5.1" then local nativeloadstring = loadstring local nativesetfenv = setfenv - --- Historically load/loadstring would handle the chunk name as if it has + -- Historically load/loadstring would handle the chunk name as if it has -- been prefixed with "=". We emulate that behaviour here. local function prefix(chunkname) if type(chunkname) ~= "string" then return chunkname end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/colors.lua b/src/main/resources/assets/computercraft/lua/rom/apis/colors.lua index b2e665268..8754b1929 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/colors.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/colors.lua @@ -1,23 +1,91 @@ +--- The Colors API allows you to manipulate sets of colors. +-- +-- This is useful in conjunction with Bundled Cables from the RedPower mod, +-- RedNet Cables from the MineFactory Reloaded mod, and colors on Advanced +-- Computers and Advanced Monitors. +-- +-- For the non-American English version just replace @{colors} with @{colours} +-- and it will use the other API, colours which is exactly the same, except in +-- British English (e.g. @{colors.gray} is spelt @{colours.grey}). +-- +-- @see colours +-- @module colors + local expect = dofile("rom/modules/main/cc/expect.lua").expect --- Colors -white = 1 -orange = 2 -magenta = 4 -lightBlue = 8 -yellow = 16 -lime = 32 -pink = 64 -gray = 128 -lightGray = 256 -cyan = 512 -purple = 1024 -blue = 2048 -brown = 4096 -green = 8192 -red = 16384 -black = 32768 +--- White: Written as `0` in paint files and @{term.blit}, has a default +-- terminal colour of #F0F0F0. +white = 0x1 +--- Orange: Written as `1` in paint files and @{term.blit}, has a +-- default terminal colour of #F2B233. +orange = 0x2 + +--- Magenta: Written as `2` in paint files and @{term.blit}, has a +-- default terminal colour of #E57FD8. +magenta = 0x4 + +--- Light blue: Written as `3` in paint files and @{term.blit}, has a +-- default terminal colour of #99B2F2. +lightBlue = 0x8 + +--- Yellow: Written as `4` in paint files and @{term.blit}, has a +-- default terminal colour of #DEDE6C. +yellow = 0x10 + +--- Lime: Written as `5` in paint files and @{term.blit}, has a default +-- terminal colour of #7FCC19. +lime = 0x20 + +--- Pink. Written as `6` in paint files and @{term.blit}, has a default +-- terminal colour of #F2B2CC. +pink = 0x40 + +--- Gray: Written as `7` in paint files and @{term.blit}, has a default +-- terminal colour of #4C4C4C. +gray = 0x80 + +--- Light gray: Written as `8` in paint files and @{term.blit}, has a +-- default terminal colour of #999999. +lightGray = 0x100 + +--- Cyan: Written as `9` in paint files and @{term.blit}, has a default +-- terminal colour of #4C99B2. +cyan = 0x200 + +--- Purple: Written as `a` in paint files and @{term.blit}, has a +-- default terminal colour of #B266E5. +purple = 0x400 + +--- Blue: Written as `b` in paint files and @{term.blit}, has a default +-- terminal colour of #3366CC. +blue = 0x800 + +--- Brown: Written as `c` in paint files and @{term.blit}, has a default +-- terminal colour of #7F664C. +brown = 0x1000 + +--- Green: Written as `d` in paint files and @{term.blit}, has a default +-- terminal colour of #57A64E. +green = 0x2000 + +--- Red: Written as `e` in paint files and @{term.blit}, has a default +-- terminal colour of #CC4C4C. +red = 0x4000 + +--- Black: Written as `f` in paint files and @{term.blit}, has a default +-- terminal colour of #191919. +black = 0x8000 + +--- Combines a set of colors (or sets of colors) into a larger set. +-- +-- @tparam number ... The colors to combine. +-- @treturn number The union of the color sets given in `...` +-- @usage +-- ```lua +-- colors.combine(colors.white, colors.magenta, colours.lightBlue) +-- -- => 13 +-- ``` function combine( ... ) local r = 0 for i = 1, select('#', ...) do @@ -28,6 +96,20 @@ function combine( ... ) return r end +--- Removes one or more colors (or sets of colors) from an initial set. +-- +-- Each parameter beyond the first may be a single color or may be a set of +-- colors (in the latter case, all colors in the set are removed from the +-- original set). +-- +-- @tparam number colors The color from which to subtract. +-- @tparam number ... The colors to subtract. +-- @treturn number The resulting color. +-- @usage +-- ```lua +-- colours.subtract(colours.lime, colours.orange, colours.white) +-- -- => 32 +-- ``` function subtract( colors, ... ) expect(1, colors, "number") local r = colors @@ -39,12 +121,33 @@ function subtract( colors, ... ) return r end +--- Tests whether `color` is contained within `colors`. +-- +-- @tparam number colors A color, or color set +-- @tparam number color A color or set of colors that `colors` should contain. +-- @treturn boolean If `colors` contains all colors within `color`. +-- @usage +-- ```lua +-- colors.test(colors.combine(colors.white, colors.magenta, colours.lightBlue), colors.lightBlue) +-- -- => true +-- ``` function test( colors, color ) expect(1, colors, "number") expect(2, color, "number") return bit32.band(colors, color) == color end +--- Combine a three-colour RGB value into one hexadecimal representation. +-- +-- @tparam number r The red channel, should be between 0 and 1. +-- @tparam number g The red channel, should be between 0 and 1. +-- @tparam number b The blue channel, should be between 0 and 1. +-- @treturn number The combined hexadecimal colour. +-- @usage +-- ```lua +-- colors.rgb(0.7, 0.2, 0.6) +-- -- => 0xb23399 +-- ``` function packRGB( r, g, b ) expect(1, r, "number") expect(2, g, "number") @@ -55,6 +158,18 @@ function packRGB( r, g, b ) bit32.band( b * 255, 0xFF ) end +--- Separate a hexadecimal RGB colour into its three constituent channels. +-- +-- @tparam number rgb The combined hexadecimal colour. +-- @treturn number The red channel, will be between 0 and 1. +-- @treturn number The red channel, will be between 0 and 1. +-- @treturn number The blue channel, will be between 0 and 1. +-- @usage +-- ```lua +-- colors.rgb(0xb23399) +-- -- => 0.7, 0.2, 0.6 +-- ``` +-- @see colors.packRGB function unpackRGB( rgb ) expect(1, rgb, "number") return @@ -63,6 +178,30 @@ function unpackRGB( rgb ) bit32.band( rgb, 0xFF ) / 255 end +--- Either calls @{colors.packRGB} or @{colors.unpackRGB}, depending on how many +-- arguments it receives. +-- +-- **Note:** This function is deprecated, and it is recommended you use the +-- specific pack/unpack function directly. +-- +-- @tparam[1] number r The red channel, as an argument to @{colors.packRGB}. +-- @tparam[1] number g The green channel, as an argument to @{colors.packRGB}. +-- @tparam[1] number b The blue channel, as an argument to @{colors.packRGB}. +-- @tparam[2] number rgb The combined hexadecimal color, as an argument to @{colors.unpackRGB}. +-- @treturn[1] number The combined hexadecimal colour, as returned by @{colors.packRGB}. +-- @treturn[2] number The red channel, as returned by @{colors.unpackRGB} +-- @treturn[2] number The green channel, as returned by @{colors.unpackRGB} +-- @treturn[2] number The blue channel, as returned by @{colors.unpackRGB} +-- @usage +-- ```lua +-- colors.rgb(0xb23399) +-- -- => 0.7, 0.2, 0.6 +-- ``` +-- @usage +-- ```lua +-- colors.rgb(0.7, 0.2, 0.6) +-- -- => 0xb23399 +-- ``` function rgb8( r, g, b ) if g == nil and b == nil then return unpackRGB( r ) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/colours.lua b/src/main/resources/assets/computercraft/lua/rom/apis/colours.lua index fa17c6cb3..74f048df7 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/colours.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/colours.lua @@ -1,11 +1,23 @@ --- Colours (for lovers of british spelling) +--- Colours for lovers of British spelling. +-- +-- @see colors +-- @module colours + local colours = _ENV for k, v in pairs(colors) do colours[k] = v end +--- Grey. Written as `7` in paint files and @{term.blit}, has a default +-- terminal colour of #4C4C4C. +-- +-- @see colors.gray colours.grey = colors.gray -colours.gray = nil +colours.gray = nil --- @local +--- Light grey. Written as `8` in paint files and @{term.blit}, has a +-- default terminal colour of #999999. +-- +-- @see colors.lightGray colours.lightGrey = colors.lightGray -colours.lightGray = nil +colours.lightGray = nil --- @local diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/command/commands.lua b/src/main/resources/assets/computercraft/lua/rom/apis/command/commands.lua index caf86f5cf..d0c02b304 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/command/commands.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/command/commands.lua @@ -1,7 +1,26 @@ +--- The commands API allows your system to directly execute [Minecraft +-- commands][mc] and gather data from the results. +-- +-- While one may use @{commands.exec} directly to execute a command, the +-- commands API also provides helper methods to execute every command. For +-- instance, `commands.say("Hi!")` is equivalent to `commands.exec("say Hi!")`. +-- +-- @{commands.async} provides a similar interface to execute asynchronous +-- commands. `commands.async.say("Hi!")` is equivalent to +-- `commands.execAsync("Hi!")`. +-- +-- [mc]: https://minecraft.gamepedia.com/Commands +-- +-- @module commands if not commands then - error( "Cannot load command API on normal computer", 2 ) + error( "Cannot load command API on normal computer", 2 ) end + +--- The builtin commands API, without any generated command helper functions +-- +-- This may be useful if a built-in function (such as @{commands.list}) has been +-- overwritten by a command. native = commands.native or commands local function collapseArgs( bJSONIsNBT, ... ) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/disk.lua b/src/main/resources/assets/computercraft/lua/rom/apis/disk.lua index 7a0dbad39..502989008 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/disk.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/disk.lua @@ -1,3 +1,15 @@ +--- The Disk API allows you to interact with disk drives. +-- +-- These functions can operate on locally attached or remote disk drives. To use +-- a locally attached drive, specify “side” as one of the six sides +-- (e.g. `left`); to use a remote disk drive, specify its name as printed when +-- enabling its modem (e.g. `drive_0`). +-- +-- **Note:** All computers (except command computers), turtles and pocket +-- computers can be placed within a disk drive to access it's internal storage +-- like a disk. +-- +-- @module disk local function isDrive( name ) if type( name ) ~= "string" then @@ -6,6 +18,11 @@ local function isDrive( name ) return peripheral.getType( name ) == "drive" end +--- Checks whether any item at all is in the disk drive +-- +-- @tparam string name The name of the disk drive. +-- @treturn boolean If something is in the disk drive. +-- @usage disk.isPresent(false) function isPresent( name ) if isDrive( name ) then return peripheral.call( name, "isDiskPresent" ) @@ -13,6 +30,16 @@ function isPresent( name ) return false end +--- Get the label of the floppy disk, record, or other media within the given +-- disk drive. +-- +-- If there is a computer or turtle within the drive, this will set the label as +-- read by `os.getComputerLabel`. +-- +-- @tparam string name The name of the disk drive. +-- @treturn string|nil The name of the current media, or `nil` if the drive is +-- not present or empty. +-- @see disk.setLabel function getLabel( name ) if isDrive( name ) then return peripheral.call( name, "getDiskLabel" ) @@ -20,12 +47,23 @@ function getLabel( name ) return nil end +--- Set the label of the floppy disk or other media +-- +-- @tparam string name The name of the disk drive. +-- @tparam string|nil label The new label of the disk function setLabel( name, label ) if isDrive( name ) then peripheral.call( name, "setDiskLabel", label ) end end +--- Check whether the current disk provides a mount. +-- +-- This will return true for disks and computers, but not records. +-- +-- @tparam string name The name of the disk drive. +-- @treturn boolean If the disk is present and provides a mount. +-- @see disk.getMountPath function hasData( name ) if isDrive( name ) then return peripheral.call( name, "hasData" ) @@ -33,6 +71,13 @@ function hasData( name ) return false end +--- Find the directory name on the local computer where the contents of the +-- current floppy disk (or other mount) can be found. +-- +-- @tparam string name The name of the disk drive. +-- @treturn string|nil The mount's directory, or `nil` if the drive does not +-- contain a floppy or computer. +-- @see disk.hasData function getMountPath( name ) if isDrive( name ) then return peripheral.call( name, "getMountPath" ) @@ -40,6 +85,15 @@ function getMountPath( name ) return nil end +--- Whether the current disk is a [music disk][disk] as opposed to a floppy disk +-- or other item. +-- +-- If this returns true, you will can @{disk.playAudio|play} the record. +-- +-- [disk]: https://minecraft.gamepedia.com/Music_Disc +-- +-- @tparam string name The name of the disk drive. +-- @treturn boolean If the disk is present and has audio saved on it. function hasAudio( name ) if isDrive( name ) then return peripheral.call( name, "hasAudio" ) @@ -47,6 +101,13 @@ function hasAudio( name ) return false end +--- Get the title of the audio track from the music record in the drive. +-- +-- This generally returns the same as @{disk.getLabel} for records. +-- +-- @tparam string name The name of the disk drive. +-- @treturn string|false|nil The track title, `false` if there is not a music +-- record in the drive or `nil` if no drive is present. function getAudioTitle( name ) if isDrive( name ) then return peripheral.call( name, "getAudioTitle" ) @@ -54,12 +115,25 @@ function getAudioTitle( name ) return nil end +--- Starts playing the music record in the drive. +-- +-- If any record is already playing on any disk drive, it stops before the +-- target drive starts playing. The record stops when it reaches the end of the +-- track, when it is removed from the drive, when @{disk.stopAudio} is called, or +-- when another record is started. +-- +-- @tparam string name The name of the disk drive. +-- @usage disk.playAudio("bottom") function playAudio( name ) if isDrive( name ) then peripheral.call( name, "playAudio" ) end end +--- Stops the music record in the drive from playing, if it was started with +-- @{disk.playAudio}. +-- +-- @tparam string name The name o the disk drive. function stopAudio( name ) if not name then for _, sName in ipairs( peripheral.getNames() ) do @@ -72,16 +146,26 @@ function stopAudio( name ) end end +--- Ejects any item currently in the drive, spilling it into the world as a loose item. +-- +-- @tparam string name The name of the disk drive. +-- @usage disk.eject("bottom") function eject( name ) if isDrive( name ) then peripheral.call( name, "ejectDisk" ) end end +--- Returns a number which uniquely identifies the disk in the drive. +-- +-- Note, unlike @{disk.getLabel}, this does not return anything for other media, +-- such as computers or turtles. +-- +-- @tparam string name The name of the disk drive. +-- @treturn string|nil The disk ID, or `nil` if the drive does not contain a floppy disk. function getID( name ) if isDrive( name ) then return peripheral.call( name, "getDiskID" ) end return nil end - diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/gps.lua b/src/main/resources/assets/computercraft/lua/rom/apis/gps.lua index 79b192494..7a4d03af0 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/gps.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/gps.lua @@ -1,5 +1,30 @@ +--- The GPS API provides a method for turtles and computers to retrieve their +-- own locations. +-- +-- It broadcasts a PING message over @{rednet} and wait for responses. In order +-- for this system to work, there must be at least 4 computers used as gps hosts +-- which will respond and allow trilateration. Three of these hosts should be in +-- a plane, and the fourth should be either above or below the other three. The +-- three in a plane should not be in a line with each other. You can set up +-- hosts using the gps program. +-- +-- **Note**: When entering in the coordinates for the host you need to put in +-- the `x`, `y`, and `z` coordinates of the computer, not the modem, as all +-- rednet distances are measured from the block the computer is in. +-- +-- Also note that you may choose which axes x, y, or z refers to - so long as +-- your systems have the same definition as any GPS servers that're in range, it +-- works just the same. For example, you might build a GPS cluster according to +-- [this tutorial][1], using z to account for height, or you might use y to +-- account for height in the way that Minecraft's debug screen displays. +-- +-- [1]: http://www.computercraft.info/forums2/index.php?/topic/3088-how-to-guide-gps-global-position-system/ +-- +-- @module gps + local expect = dofile("rom/modules/main/cc/expect.lua").expect +--- The channel which GPS requests and responses are broadcast on. CHANNEL_GPS = 65534 local function trilaterate( A, B, C ) @@ -56,6 +81,15 @@ local function narrow( p1, p2, fix ) end end +--- Tries to retrieve the computer or turtles own location. +-- +-- @tparam[opt] number timeout The maximum time taken to establish our +-- position. Defaults to 2 seconds if not specified. +-- @tparam[opt] boolean debug Print debugging messages +-- @treturn[1] number This computer's `x` position. +-- @treturn[1] number This computer's `y` position. +-- @treturn[1] number This computer's `z` position. +-- @treturn[2] nil If the position could not be established. function locate( _nTimeout, _bDebug ) expect(1, _nTimeout, "number", "nil") expect(2, _bDebug, "boolean", "nil") diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/help.lua b/src/main/resources/assets/computercraft/lua/rom/apis/help.lua index ded20f2f3..1b3088c4b 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/help.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/help.lua @@ -1,16 +1,38 @@ +--- Provides an API to read help files. +-- +-- @module help + local expect = dofile("rom/modules/main/cc/expect.lua").expect local sPath = "/rom/help" +--- Returns a colon-separated list of directories where help files are searched +-- for. All directories are absolute. +-- +-- @treturn string The current help search path, separated by colons. +-- @see help.setPath function path() return sPath end +--- Sets the colon-seperated list of directories where help files are searched +-- for to `newPath` +-- +-- @tparam string newPath The new path to use. +-- @usage help.setPath( "/disk/help/" ) +-- @usage help.setPath( help.path() .. ":/myfolder/help/" ) +-- @see help.path function setPath( _sPath ) expect(1, _sPath, "string") sPath = _sPath end +--- Returns the location of the help file for the given topic. +-- +-- @tparam string topic The topic to find +-- @treturn string|nil The path to the given topic's help file, or `nil` if it +-- cannot be found. +-- @usage print(help.lookup("disk")) function lookup( _sTopic ) expect(1, _sTopic, "string") -- Look on the path variable @@ -27,6 +49,9 @@ function lookup( _sTopic ) return nil end +--- Returns a list of topics that can be looked up and/or displayed. +-- +-- @treturn table A list of topics in alphabetical order. function topics() -- Add index local tItems = { @@ -59,6 +84,11 @@ function topics() return tItemList end +--- Returns a list of topic endings that match the prefix. Can be used with +-- `read` to allow input of a help topic. +-- +-- @tparam string prefix The prefix to match +-- @treturn table A list of matching topics. function completeTopic( sText ) expect(1, sText, "string") local tTopics = topics() diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/io.lua b/src/main/resources/assets/computercraft/lua/rom/apis/io.lua index 3cf28c80b..8079e022f 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/io.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/io.lua @@ -1,6 +1,10 @@ --- Definition for the IO API +--- Emulates Lua's standard [io library][io]. +-- +-- [io]: https://www.lua.org/manual/5.1/manual.html#5.7 +-- +-- @module io -local expect, typeOf = dofile("rom/modules/main/cc/expect.lua").expect, _G.type +local expect, type_of = dofile("rom/modules/main/cc/expect.lua").expect, _G.type --- If we return nil then close the file, as we've reached the end. -- We use this weird wrapper function as we wish to preserve the varargs @@ -9,6 +13,9 @@ local function checkResult(handle, ...) return ... end +--- A file handle which can be read or written to. +-- +-- @type Handle local handleMetatable handleMetatable = { __name = "FILE*", @@ -20,10 +27,17 @@ handleMetatable = { return "file (" .. hash .. ")" end end, + __index = { + --- Close this file handle, freeing any resources it uses. + -- + -- @treturn[1] true If this handle was successfully closed. + -- @treturn[2] nil If this file handle could not be closed. + -- @treturn[2] string The reason it could not be closed. + -- @throws If this handle was already closed. close = function(self) - if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2) + if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) end if self._closed then error("attempt to use a closed file", 2) end @@ -36,18 +50,24 @@ handleMetatable = { return nil, "attempt to close standard stream" end end, + + --- Flush any buffered output, forcing it to be written to the file + -- + -- @throws If the handle has been closed flush = function(self) - if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2) + if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) end if self._closed then error("attempt to use a closed file", 2) end local handle = self._handle if handle.flush then handle.flush() end + return true end, + lines = function(self, ...) - if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2) + if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) end if self._closed then error("attempt to use a closed file", 2) end @@ -57,9 +77,10 @@ handleMetatable = { local args = table.pack(...) return function() return checkResult(self, self:read(table.unpack(args, 1, args.n))) end end, + read = function(self, ...) - if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2) + if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) end if self._closed then error("attempt to use a closed file", 2) end @@ -71,9 +92,9 @@ handleMetatable = { for i = 1, n do local arg = select(i, ...) local res - if typeOf(arg) == "number" then + if type_of(arg) == "number" then if handle.read then res = handle.read(arg) end - elseif typeOf(arg) == "string" then + elseif type_of(arg) == "string" then local format = arg:gsub("^%*", ""):sub(1, 1) if format == "l" then @@ -88,7 +109,7 @@ handleMetatable = { error("bad argument #" .. i .. " (invalid format)", 2) end else - error("bad argument #" .. i .. " (expected string, got " .. typeOf(arg) .. ")", 2) + error("bad argument #" .. i .. " (expected string, got " .. type_of(arg) .. ")", 2) end output[i] = res @@ -99,9 +120,10 @@ handleMetatable = { if n == 0 and handle.readLine then return handle.readLine() end return table.unpack(output, 1, n) end, + seek = function(self, whence, offset) - if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2) + if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) end if self._closed then error("attempt to use a closed file", 2) end @@ -111,10 +133,18 @@ handleMetatable = { -- It's a tail call, so error positions are preserved return handle.seek(whence, offset) end, + setvbuf = function(self, mode, size) end, + + --- Write one or more values to the file + -- + -- @tparam string|number ... The values to write. + -- @treturn[1] Handle The current file, allowing chained calls. + -- @treturn[2] nil If the file could not be written to. + -- @treturn[2] string The error message which occurred while writing. write = function(self, ...) - if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2) + if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) end if self._closed then error("attempt to use a closed file", 2) end @@ -156,41 +186,88 @@ local defaultError = setmetatable({ local currentInput = defaultInput local currentOutput = defaultOutput +--- A file handle representing the "standard input". Reading from this +-- file will prompt the user for input. stdin = defaultInput + +--- A file handle representing the "standard output". Writing to this +-- file will display the written text to the screen. stdout = defaultOutput + +--- A file handle representing the "standard error" stream. +-- +-- One may use this to display error messages, writing to it will display +-- them on the terminal. stderr = defaultError -function close(_file) - if _file == nil then return currentOutput:close() end +--- Closes the provided file handle. +-- +-- @tparam[opt] Handle file The file handle to close, defaults to the +-- current output file. +-- +-- @see Handle:close +-- @see io.output +function close(file) + if file == nil then return currentOutput:close() end - if typeOf(_file) ~= "table" or getmetatable(_file) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(_file) .. ")", 2) + if type_of(file) ~= "table" or getmetatable(file) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(file) .. ")", 2) end - return _file:close() + return file:close() end +--- Flushes the current output file. +-- +-- @see Handle:flush +-- @see io.output function flush() return currentOutput:flush() end -function input(_arg) - if typeOf(_arg) == "string" then - local res, err = open(_arg, "rb") +--- Get or set the current input file. +-- +-- @tparam[opt] Handle|string file The new input file, either as a file path or pre-existing handle. +-- @treturn Handle The current input file. +-- @throws If the provided filename cannot be opened for reading. +function input(file) + if type_of(file) == "string" then + local res, err = open(file, "rb") if not res then error(err, 2) end currentInput = res - elseif typeOf(_arg) == "table" and getmetatable(_arg) == handleMetatable then - currentInput = _arg - elseif _arg ~= nil then - error("bad argument #1 (FILE expected, got " .. typeOf(_arg) .. ")", 2) + elseif type_of(file) == "table" and getmetatable(file) == handleMetatable then + currentInput = file + elseif file ~= nil then + error("bad fileument #1 (FILE expected, got " .. type_of(file) .. ")", 2) end return currentInput end -function lines(_sFileName) - expect(1, _sFileName, "string", "nil") - if _sFileName then - local ok, err = open(_sFileName, "rb") +--- Opens the given file name in read mode and returns an iterator that, +-- each time it is called, returns a new line from the file. +-- +-- This can be used in a for loop to iterate over all lines of a file: +-- +-- ```lua +-- for line in io.lines(filename) do print(line) end +-- ``` +-- +-- Once the end of the file has been reached, @{nil} will be +-- returned. The file is automatically closed. +-- +-- If no file name is given, the @{io.input|current input} will be used +-- instead. In this case, the handle is not used. +-- +-- @tparam[opt] string filename The name of the file to extract lines from +-- @treturn function():string|nil The line iterator. +-- @throws If the file cannot be opened for reading +-- +-- @see Handle:lines +-- @see io.input +function lines(filename) + expect(1, filename, "string", "nil") + if filename then + local ok, err = open(filename, "rb") if not ok then error(err, 2) end -- We set this magic flag to mark this file as being opened by io.lines and so should be @@ -202,38 +279,72 @@ function lines(_sFileName) end end -function open(_sPath, _sMode) - expect(1, _sPath, "string") - expect(2, _sMode, "string", "nil") +--- Open a file with the given mode, either returning a new file handle +-- or @{nil}, plus an error message. +-- +-- The `mode` string can be any of the following: +-- - **"r"**: Read mode +-- - **"w"**: Write mode +-- - **"w"**: Append mode +-- +-- The mode may also have a `b` at the end, which opens the file in "binary +-- mode". This allows you to read binary files, as well as seek within a file. +-- +-- @tparam string filename The name of the file to open. +-- @tparam[opt] string mode The mode to open the file with. This defaults to `rb`. +-- @treturn[1] Handle The opened file. +-- @treturn[2] nil In case of an error. +-- @treturn[2] string The reason the file could not be opened. +function open(filename, mode) + expect(1, filename, "string") + expect(2, mode, "string", "nil") - local sMode = _sMode and _sMode:gsub("%+", "") or "rb" - local file, err = fs.open(_sPath, sMode) + local sMode = mode and mode:gsub("%+", "") or "rb" + local file, err = fs.open(filename, sMode) if not file then return nil, err end return setmetatable({ _handle = file }, handleMetatable) end -function output(_arg) - if typeOf(_arg) == "string" then - local res, err = open(_arg, "w") +--- Get or set the current output file. +-- +-- @tparam[opt] Handle|string file The new output file, either as a file path or pre-existing handle. +-- @treturn Handle The current output file. +-- @throws If the provided filename cannot be opened for writing. +function output(file) + if type_of(file) == "string" then + local res, err = open(file, "w") if not res then error(err, 2) end currentOutput = res - elseif typeOf(_arg) == "table" and getmetatable(_arg) == handleMetatable then - currentOutput = _arg - elseif _arg ~= nil then - error("bad argument #1 (FILE expected, got " .. typeOf(_arg) .. ")", 2) + elseif type_of(file) == "table" and getmetatable(file) == handleMetatable then + currentOutput = file + elseif file ~= nil then + error("bad argument #1 (FILE expected, got " .. type_of(file) .. ")", 2) end return currentOutput end +--- Read from the currently opened input file. +-- +-- This is equivalent to `io.input():read(...)`. See @{Handle:read|the +-- documentation} there for full details. +-- +-- @tparam string ... The formats to read, defaulting to a whole line. +-- @treturn (string|nil)... The data read, or @{nil} if nothing can be read. function read(...) return currentInput:read(...) end -function type(handle) - if typeOf(handle) == "table" and getmetatable(handle) == handleMetatable then - if handle._closed then +--- Checks whether `handle` is a given file handle, and determine if it is open +-- or not. +-- +-- @param obj The value to check +-- @treturn string|nil `"file"` if this is an open file, `"closed file"` if it +-- is a closed file handle, or `nil` if not a file handle. +function type(obj) + if type_of(obj) == "table" and getmetatable(obj) == handleMetatable then + if obj._closed then return "closed file" else return "file" @@ -242,6 +353,12 @@ function type(handle) return nil end +--- Write to the currently opened output file. +-- +-- This is equivalent to `io.output():write(...)`. See @{Handle:write|the +-- documentation} there for full details. +-- +-- @tparam string ... The strings to write function write(...) return currentOutput:write(...) end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/keys.lua b/src/main/resources/assets/computercraft/lua/rom/apis/keys.lua index c3ab42605..d303a8087 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/keys.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/keys.lua @@ -1,5 +1,12 @@ --- Minecraft key code bindings --- See http://www.minecraftwiki.net/wiki/Key_codes for more info +--- The Keys API provides a table of numerical codes corresponding to keyboard +-- keys, suitable for decoding key events. +-- +-- The Minecraft wiki [has a list of key +-- codes](http://www.minecraftwiki.net/wiki/Key_codes). It is recommended that +-- you use the constants provided by this file, rather than the underlying +-- numerical values. +-- +-- @module keys local expect = dofile("rom/modules/main/cc/expect.lua").expect @@ -53,12 +60,18 @@ local keys = _ENV for nKey, sKey in pairs( tKeys ) do keys[sKey] = nKey end -keys["return"] = keys.enter ---backwards compatibility to earlier, typo prone, versions -keys.scollLock = keys.scrollLock -keys.cimcumflex = keys.circumflex -function getName( _nKey ) - expect(1, _nKey, "number") - return tKeys[ _nKey ] +keys["return"] = keys.enter --- @local +--backwards compatibility to earlier, typo prone, versions +keys.scollLock = keys.scrollLock --- @local +keys.cimcumflex = keys.circumflex --- @local + +--- Translates a numerical key code to a human-readable name. The human-readable +-- name is one of the constants in the keys API. +-- +-- @tparam number code The key code to look up. +-- @treturn string|nil The name of the key, or `nil` if not a valid key code. +function getName( code ) + expect(1, code, "number") + return tKeys[ code ] end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/paintutils.lua b/src/main/resources/assets/computercraft/lua/rom/apis/paintutils.lua index d725367d0..4a5630034 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/paintutils.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/paintutils.lua @@ -1,3 +1,8 @@ +--- An API for advanced systems which can draw pixels and lines, load and draw +-- image files. You can use the `colors` API for easier color manipulation. +-- +-- @module paintutils + local expect = dofile("rom/modules/main/cc/expect.lua").expect local function drawPixelInternal( xPos, yPos ) @@ -18,51 +23,88 @@ local function parseLine( tImageArg, sLine ) table.insert( tImageArg, tLine ) end -function parseImage( sRawData ) - expect(1, sRawData, "string") +--- Parses an image from a multi-line string +-- +-- @tparam string image The string containing the raw-image data. +-- @treturn table The parsed image data, suitable for use with +-- @{paintutils.drawImage}. +function parseImage( image ) + expect(1, image, "string") local tImage = {} - for sLine in ( sRawData .. "\n" ):gmatch( "(.-)\n" ) do -- read each line like original file handling did + for sLine in ( image .. "\n" ):gmatch( "(.-)\n" ) do parseLine( tImage, sLine ) end return tImage end -function loadImage( sPath ) - expect(1, sPath, "string") +--- Loads an image from a file. +-- +-- You can create a file suitable for being loaded using the `paint` program. +-- +-- @tparam string path The file to load. +-- +-- @treturn table|nil The parsed image data, suitable for use with +-- @{paintutils.drawImage}, or `nil` if the file does not exist. +function loadImage( path ) + expect(1, path, "string") - if fs.exists( sPath ) then - local file = io.open( sPath, "r" ) + if fs.exists( path ) then + local file = io.open( path, "r" ) local sContent = file:read("*a") file:close() - return parseImage( sContent ) -- delegate image parse to parseImage + return parseImage( sContent ) end return nil end -function drawPixel( xPos, yPos, nColour ) +--- Draws a single pixel to the current term at the specified position. +-- +-- Be warned, this may change the position of the cursor and the current +-- background colour. You should not expect either to be preserved. +-- +-- @tparam number xPos The x position to draw at, where 1 is the far left. +-- @tparam number yPos The y position to draw at, where 1 is the very top. +-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be +-- the current background colour if not specified. +function drawPixel( xPos, yPos, colour ) expect(1, xPos, "number") expect(2, yPos, "number") - expect(3, nColour, "number", "nil") - if nColour then - term.setBackgroundColor( nColour ) + expect(3, colour, "number", "nil") + + if type( xPos ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( xPos ) .. ")", 2 ) end + if type( yPos ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( yPos ) .. ")", 2 ) end + if colour ~= nil and type( colour ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( colour ) .. ")", 2 ) end + if colour then + term.setBackgroundColor( colour ) end return drawPixelInternal( xPos, yPos ) end -function drawLine( startX, startY, endX, endY, nColour ) +--- Draws a straight line from the start to end position. +-- +-- Be warned, this may change the position of the cursor and the current +-- background colour. You should not expect either to be preserved. +-- +-- @tparam number startX The starting x position of the line. +-- @tparam number startY The starting y position of the line. +-- @tparam number endX The end x position of the line. +-- @tparam number endY The end y position of the line. +-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be +-- the current background colour if not specified. +function drawLine( startX, startY, endX, endY, colour ) expect(1, startX, "number") expect(2, startY, "number") expect(3, endX, "number") expect(4, endY, "number") - expect(5, nColour, "number", "nil") + expect(5, colour, "number", "nil") startX = math.floor(startX) startY = math.floor(startY) endX = math.floor(endX) endY = math.floor(endY) - if nColour then - term.setBackgroundColor( nColour ) + if colour then + term.setBackgroundColor( colour ) end if startX == endX and startY == endY then drawPixelInternal( startX, startY ) @@ -110,6 +152,18 @@ function drawLine( startX, startY, endX, endY, nColour ) end end +--- Draws the outline of a box on the current term from the specified start +-- position to the specified end position. +-- +-- Be warned, this may change the position of the cursor and the current +-- background colour. You should not expect either to be preserved. +-- +-- @tparam number startX The starting x position of the line. +-- @tparam number startY The starting y position of the line. +-- @tparam number endX The end x position of the line. +-- @tparam number endY The end y position of the line. +-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be +-- the current background colour if not specified. function drawBox( startX, startY, endX, endY, nColour ) expect(1, startX, "number") expect(2, startY, "number") @@ -154,7 +208,18 @@ function drawBox( startX, startY, endX, endY, nColour ) end end end - +--- Draws a filled box on the current term from the specified start position to +-- the specified end position. +-- +-- Be warned, this may change the position of the cursor and the current +-- background colour. You should not expect either to be preserved. +-- +-- @tparam number startX The starting x position of the line. +-- @tparam number startY The starting y position of the line. +-- @tparam number endX The end x position of the line. +-- @tparam number endY The end y position of the line. +-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be +-- the current background colour if not specified. function drawFilledBox( startX, startY, endX, endY, nColour ) expect(1, startX, "number") expect(2, startY, "number") @@ -194,12 +259,17 @@ function drawFilledBox( startX, startY, endX, endY, nColour ) end end -function drawImage( tImage, xPos, yPos ) - expect(1, tImage, "table") +--- Draw an image loaded by @{paintutils.parseImage} or @{paintutils.loadImage}. +-- +-- @tparam table image The parsed image data. +-- @tparam number xPos The x position to start drawing at. +-- @tparam number xPos The y position to start drawing at. +function drawImage( image, xPos, yPos ) + expect(1, image, "table") expect(2, xPos, "number") expect(3, yPos, "number") - for y = 1, #tImage do - local tLine = tImage[y] + for y = 1, #image do + local tLine = image[y] for x = 1, #tLine do if tLine[x] > 0 then term.setBackgroundColor( tLine[x] ) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/parallel.lua b/src/main/resources/assets/computercraft/lua/rom/apis/parallel.lua index 43b832c8b..ea4f8d43a 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/parallel.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/parallel.lua @@ -1,3 +1,18 @@ +--- Provides a simple implementation of multitasking. +-- +-- Functions are not actually executed simultaniously, but rather this API will +-- automatically switch between them whenever they yield (eg whenever they call +-- @{coroutine.yield}, or functions that call that - eg `os.pullEvent` - or +-- functions that call that, etc - basically, anything that causes the function +-- to "pause"). +-- +-- Each function executed in "parallel" gets its own copy of the event queue, +-- and so "event consuming" functions (again, mostly anything that causes the +-- script to pause - eg `sleep`, `rednet.receive`, most of the `turtle` API, +-- etc) can safely be used in one without affecting the event queue accessed by +-- the other. +-- +-- @module parallel local function create( ... ) local tFns = table.pack(...) @@ -55,12 +70,22 @@ local function runUntilLimit( _routines, _limit ) end end +--- Switches between execution of the functions, until any of them +-- finishes. If any of the functions errors, the message is propagated upwards +-- from the @{parallel.waitForAny} call. +-- +-- @tparam function ... The functions this task will run function waitForAny( ... ) local routines = create( ... ) return runUntilLimit( routines, #routines - 1 ) end +--- Switches between execution of the functions, until all of them are +-- finished. If any of the functions errors, the message is propagated upwards +-- from the @{parallel.waitForAll} call. +-- +-- @tparam function ... The functions this task will run function waitForAll( ... ) local routines = create( ... ) - runUntilLimit( routines, 0 ) + return runUntilLimit( routines, 0 ) end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua b/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua index a398d696f..c3c71ff65 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua @@ -1,110 +1,182 @@ +--- The Peripheral API is for interacting with peripherals connected to the +-- computer, such as the Disk Drive, the Advanced Monitor and Monitor. +-- +-- Each peripheral block has a name, either referring to the side the peripheral +-- can be found on, or a name on an adjacent wired network. +-- +-- If the peripheral is next to the computer, its side is either `front`, +-- `back`, `left`, `right`, `top` or `bottom`. If the peripheral is attached by +-- a cable, its side will follow the format `type_id`, for example `printer_0`. +-- +-- Peripheral functions are called *methods*, a term borrowed from Java. +-- +-- @module peripheral + local expect = dofile("rom/modules/main/cc/expect.lua").expect local native = peripheral +local sides = rs.getSides() +--- Provides a list of all peripherals available. +-- +-- If a device is located directly next to the system, then its name will be +-- listed as the side it is attached to. If a device is attached via a Wired +-- Modem, then it'll be reported according to its name on the wired network. +-- +-- @treturn table A list of the names of all attached peripherals. function getNames() - local tResults = {} - for _, sSide in ipairs( rs.getSides() ) do - if native.isPresent( sSide ) then - table.insert( tResults, sSide ) - if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then - local tRemote = native.call( sSide, "getNamesRemote" ) - for _, sName in ipairs( tRemote ) do - table.insert( tResults, sName ) + local results = {} + for n = 1, #sides do + local side = sides[n] + if native.isPresent(side) then + table.insert(results, side) + if native.getType(side) == "modem" and not native.call(side, "isWireless") then + local remote = native.call(side, "getNamesRemote") + for _, name in ipairs(remote) do + table.insert(results, name) end end end end - return tResults + return results end -function isPresent( _sSide ) - expect(1, _sSide, "string") - if native.isPresent( _sSide ) then +--- Determines if a peripheral is present with the given name. +-- +-- @tparam string name The side or network name that you want to check. +-- @treturn boolean If a peripheral is present with the given name. +-- @usage peripheral.isPresent("top") +-- @usage peripheral.isPresent("monitor_0") +function isPresent(name) + expect(1, name, "string") + if native.isPresent(name) then return true end - for _, sSide in ipairs( rs.getSides() ) do - if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then - if native.call( sSide, "isPresentRemote", _sSide ) then - return true - end + + for n = 1, #sides do + local name = sides[n] + if native.getType(name) == "modem" and not native.call(name, "isWireless") and + native.call(name, "isPresentRemote", name) + then + return true end end return false end -function getType( _sSide ) - expect(1, _sSide, "string") - if native.isPresent( _sSide ) then - return native.getType( _sSide ) +--- Get the type of the peripheral with the given name. +-- +-- @tparam string name The name of the peripheral to find. +-- @treturn string|nil The peripheral's type, or `nil` if it is not present. +function getType(name) + expect(1, name, "string") + if native.isPresent(name) then + return native.getType(name) end - for _, sSide in ipairs( rs.getSides() ) do - if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then - if native.call( sSide, "isPresentRemote", _sSide ) then - return native.call( sSide, "getTypeRemote", _sSide ) - end + for n = 1, #sides do + local side = sides[n] + if native.getType(side) == "modem" and not native.call(side, "isWireless") and + native.call(side, "isPresentRemote", name) + then + return native.call(side, "getTypeRemote", name) end end return nil end -function getMethods( _sSide ) - expect(1, _sSide, "string") - if native.isPresent( _sSide ) then - return native.getMethods( _sSide ) +--- Get all available methods for the peripheral with the given name. +-- +-- @tparam string name The name of the peripheral to find. +-- @treturn table|nil A list of methods provided by this peripheral, or `nil` if +-- it is not present. +function getMethods(name) + expect(1, name, "string") + if native.isPresent(name) then + return native.getMethods(name) end - for _, sSide in ipairs( rs.getSides() ) do - if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then - if native.call( sSide, "isPresentRemote", _sSide ) then - return native.call( sSide, "getMethodsRemote", _sSide ) - end + for n = 1, #sides do + local side = sides[n] + if native.getType(side) == "modem" and not native.call(side, "isWireless") and + native.call(side, "isPresentRemote", name) + then + return native.call(side, "getMethodsRemote", name) end end return nil end -function call( _sSide, _sMethod, ... ) - expect(1, _sSide, "string") - expect(2, _sMethod, "string") - if native.isPresent( _sSide ) then - return native.call( _sSide, _sMethod, ... ) +--- Call a method on a peripheral with a given name +-- +-- @tparam string name The name of the peripheral to invoke the method on. +-- @tparam string method The name of the method +-- @param ... Additional arguments to pass to the method +-- @return The return values of the peripheral method. +-- +-- @usage peripheral.call("top", "open", 1) +function call(name, method, ...) + expect(1, name, "string") + expect(2, method, "string") + if native.isPresent(name) then + return native.call(name, method, ...) end - for _, sSide in ipairs( rs.getSides() ) do - if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then - if native.call( sSide, "isPresentRemote", _sSide ) then - return native.call( sSide, "callRemote", _sSide, _sMethod, ... ) - end + + for n = 1, #sides do + local side = sides[n] + if native.getType(side) == "modem" and not native.call(side, "isWireless") and + native.call(side, "isPresentRemote", name) + then + return native.call(side, "callRemote", name, method, ...) end end return nil end -function wrap( _sSide ) - expect(1, _sSide, "string") - if peripheral.isPresent( _sSide ) then - local tMethods = peripheral.getMethods( _sSide ) - local tResult = {} - for _, sMethod in ipairs( tMethods ) do - tResult[sMethod] = function( ... ) - return peripheral.call( _sSide, sMethod, ... ) - end - end - return tResult +--- Get a table containing functions pointing to the peripheral's methods, which +-- can then be called as if using @{peripheral.call}. +-- +-- @tparam string name The name of the peripheral to wrap. +-- @treturn table|nil The table containing the peripheral's methods, or `nil` if +-- there is no peripheral present with the given name. +-- @usage peripheral.wrap("top").open(1) +function wrap(name) + expect(1, name, "string") + + local methods = peripheral.getMethods(name) + if not methods then + return nil end - return nil + + local result = {} + for _, method in ipairs(methods) do + result[method] = function(...) + return peripheral.call(name, method, ...) + end + end + return result end -function find( sType, fnFilter ) - expect(1, sType, "string") - expect(2, fnFilter, "function", "nil") - local tResults = {} - for _, sName in ipairs( peripheral.getNames() ) do - if peripheral.getType( sName ) == sType then - local wrapped = peripheral.wrap( sName ) - if fnFilter == nil or fnFilter( sName, wrapped ) then - table.insert( tResults, wrapped ) +--- Find all peripherals of a specific type, and return the +-- @{peripheral.wrap|wrapped} peripherals. +-- +-- @tparam string ty The type of peripheral to look for. +-- @tparam[opt] function(name:string, wrapped:table):boolean filter A +-- filter function, which takes the peripheral's name and wrapped table +-- and returns if it should be included in the result. +-- @treturn table... 0 or more wrapped peripherals matching the given filters. +-- @usage local monitors = { peripheral.find("monitor") } +-- @usage peripheral.find("modem", rednet.open) +function find(ty, filter) + expect(1, ty, "string") + expect(2, filter, "function", "nil") + + local results = {} + for _, name in ipairs(peripheral.getNames()) do + if peripheral.getType(name) == ty then + local wrapped = peripheral.wrap(name) + if filter == nil or filter(name, wrapped) then + table.insert(results, wrapped) end end end - return table.unpack( tResults ) + return table.unpack(results) end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/rednet.lua b/src/main/resources/assets/computercraft/lua/rom/apis/rednet.lua index d6bf5d668..9152aa647 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/rednet.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/rednet.lua @@ -1,51 +1,92 @@ +--- The Rednet API allows systems to communicate between each other without +-- using redstone. It serves as a wrapper for the modem API, offering ease of +-- functionality (particularly in regards to repeating signals) with some +-- expense of fine control. +-- +-- In order to send and receive data, a modem (either wired, wireless, or ender) +-- is required. The data reaches any possible destinations immediately after +-- sending it, but is range limited. +-- +-- Rednet also allows you to use a "protocol" - simple string names indicating +-- what messages are about. Receiving systems may filter messages according to +-- their protocols, thereby automatically ignoring incoming messages which don't +-- specify an identical string. It's also possible to @{rednet.lookup|lookup} +-- which systems in the area use certain protocols, hence making it easier to +-- determine where given messages should be sent in the first place. +-- +-- @module rednet + local expect = dofile("rom/modules/main/cc/expect.lua").expect +--- The channel used by the Rednet API to @{broadcast} messages. CHANNEL_BROADCAST = 65535 + +--- The channel used by the Rednet API to repeat messages. CHANNEL_REPEAT = 65533 local tReceivedMessages = {} local tReceivedMessageTimeouts = {} local tHostnames = {} -function open( sModem ) - expect(1, sModem, "string") - if peripheral.getType( sModem ) ~= "modem" then - error( "No such modem: " .. sModem, 2 ) +--- Opens a modem with the given @{peripheral} name, allowing it to send and +--- receive messages over rednet. +-- +-- This will open the modem on two channels: one which has the same +-- @{os.getComputerID|ID} as the computer, and another on +-- @{CHANNEL_BROADCAST|the broadcast channel}. +-- +-- @tparam string modem The name of the modem to open. +-- @throws If there is no such modem with the given name +function open( modem ) + expect(1, modem, "string") + if peripheral.getType( modem ) ~= "modem" then + error( "No such modem: " .. modem, 2 ) end - peripheral.call( sModem, "open", os.getComputerID() ) - peripheral.call( sModem, "open", CHANNEL_BROADCAST ) + peripheral.call( modem, "open", os.getComputerID() ) + peripheral.call( modem, "open", CHANNEL_BROADCAST ) end -function close( sModem ) - expect(1, sModem, "string", "nil") - if sModem then +--- Close a modem with the given @{peripheral} name, meaning it can no longer +-- send and receive rednet messages. +-- +-- @tparam[opt] string modem The side the modem exists on. If not given, all +-- open modems will be closed. +-- @throws If there is no such modem with the given name +function close( modem ) + expect(1, modem, "string", "nil") + if modem then -- Close a specific modem - if peripheral.getType( sModem ) ~= "modem" then - error( "No such modem: " .. sModem, 2 ) + if peripheral.getType( modem ) ~= "modem" then + error( "No such modem: " .. modem, 2 ) end - peripheral.call( sModem, "close", os.getComputerID() ) - peripheral.call( sModem, "close", CHANNEL_BROADCAST ) + peripheral.call( modem, "close", os.getComputerID() ) + peripheral.call( modem, "close", CHANNEL_BROADCAST ) else -- Close all modems - for _, sModem in ipairs( peripheral.getNames() ) do - if isOpen( sModem ) then - close( sModem ) + for _, modem in ipairs( peripheral.getNames() ) do + if isOpen( modem ) then + close( modem ) end end end end -function isOpen( sModem ) - expect(1, sModem, "string", "nil") - if sModem then +--- Determine if rednet is currently open. +-- +-- @tparam[opt] string modem Which modem to check. If not given, all connected +-- modems will be checked. +-- @treturn boolean If the given modem is open. +function isOpen( modem ) + expect(1, modem, "string", "nil") + if modem then -- Check if a specific modem is open - if peripheral.getType( sModem ) == "modem" then - return peripheral.call( sModem, "isOpen", os.getComputerID() ) and peripheral.call( sModem, "isOpen", CHANNEL_BROADCAST ) + if peripheral.getType( modem ) == "modem" then + return peripheral.call( modem, "isOpen", os.getComputerID() ) and peripheral.call( modem, "isOpen", CHANNEL_BROADCAST ) end else -- Check if any modem is open - for _, sModem in ipairs( peripheral.getNames() ) do - if isOpen( sModem ) then + for _, modem in ipairs( peripheral.getNames() ) do + if isOpen( modem ) then return true end end @@ -53,6 +94,23 @@ function isOpen( sModem ) return false end +--- Allows a computer or turtle with an attached modem to send a message +-- intended for a system with a specific ID. At least one such modem must first +-- be @{rednet.open|opened} before sending is possible. +-- +-- Assuming the target was in range and also had a correctly opened modem, it +-- may then use @{rednet.receive} to collect the message. +-- +-- @tparam number nRecipient The ID of the receiving computer. +-- @param message The message to send. This should not contain coroutines or +-- functions, as they will be converted to @{nil}. +-- @tparam[opt] string sProtocol The "protocol" to send this message under. When +-- using @{rednet.receive} one can filter to only receive messages sent under a +-- particular protocol. +-- @treturn boolean If this message was successfully sent (i.e. if rednet is +-- currently @{rednet.open|open}). Note, this does not guarantee the message was +-- actually _received_. +-- @see rednet.receive function send( nRecipient, message, sProtocol ) expect(1, nRecipient, "number") expect(3, sProtocol, "string", "nil") @@ -91,11 +149,34 @@ function send( nRecipient, message, sProtocol ) return sent end +--- Broadcasts a string message over the predefined @{CHANNEL_BROADCAST} +-- channel. The message will be received by every device listening to rednet. +-- +-- @param message The message to send. This should not contain coroutines or +-- functions, as they will be converted to @{nil}. +-- @tparam[opt] string sProtocol The "protocol" to send this message under. When +-- using @{rednet.receive} one can filter to only receive messages sent under a +-- particular protocol. +-- @see rednet.receive function broadcast( message, sProtocol ) expect(2, sProtocol, "string", "nil") send( CHANNEL_BROADCAST, message, sProtocol ) end +--- Wait for a rednet message to be received, or until `nTimeout` seconds have +-- elapsed. +-- +-- @tparam[opt] string sProtocolFilter The protocol the received message must be +-- sent with. If specified, any messages not sent under this protocol will be +-- discarded. +-- @tparam[opt] number nTimeout The number of seconds to wait if no message is +-- received. +-- @treturn[1] number The computer which sent this message +-- @return[1] The received message +-- @treturn[1] string|nil The protocol this message was sent under. +-- @treturn[2] nil If the timeout elapsed and no message was received. +-- @see rednet.broadcast +-- @see rednet.send function receive( sProtocolFilter, nTimeout ) -- The parameters used to be ( nTimeout ), detect this case for backwards compatibility if type(sProtocolFilter) == "number" and nTimeout == nil then @@ -132,6 +213,24 @@ function receive( sProtocolFilter, nTimeout ) end end +--- Register the system as "hosting" the desired protocol under the specified +-- name. If a rednet @{rednet.lookup|lookup} is performed for that protocol (and +-- maybe name) on the same network, the registered system will automatically +-- respond via a background process, hence providing the system performing the +-- lookup with its ID number. +-- +-- Multiple computers may not register themselves on the same network as having +-- the same names against the same protocols, and the title `localhost` is +-- specifically reserved. They may, however, share names as long as their hosted +-- protocols are different, or if they only join a given network after +-- "registering" themselves before doing so (eg while offline or part of a +-- different network). +-- +-- @tparam string sProtocol The protocol this computer provides. +-- @tparam string sHostname The name this protocol exposes for the given protocol. +-- @throws If trying to register a hostname which is reserved, or currently in use. +-- @see rednet.unhost +-- @see rednet.lookup function host( sProtocol, sHostname ) expect(1, sProtocol, "string") expect(2, sHostname, "string") @@ -146,11 +245,29 @@ function host( sProtocol, sHostname ) end end +--- Stop @{rednet.host|hosting} a specific protocol, meaning it will no longer +--- respond to @{rednet.lookup} requests. +-- +-- @tparam string sProtocol The protocol to unregister your self from. function unhost( sProtocol ) expect(1, sProtocol, "string") tHostnames[ sProtocol ] = nil end +--- Search the local rednet network for systems @{rednet.host|hosting} the +-- desired protocol and returns any computer IDs that respond as "registered" +-- against it. +-- +-- If a hostname is specified, only one ID will be returned (assuming an exact +-- match is found). +-- +-- @tparam string sProtocol The protocol to search for. +-- @tparam[opt] string sHostname The hostname to search for. +-- +-- @treturn[1] { number }|nil A list of computer IDs hosting the given +-- protocol, or @{nil} if none exist. +-- @treturn[2] number|nil The computer ID with the provided hostname and protocol, +-- or @{nil} if none exists. function lookup( sProtocol, sHostname ) expect(1, sProtocol, "string") expect(2, sHostname, "string", "nil") @@ -216,6 +333,8 @@ function lookup( sProtocol, sHostname ) end local bRunning = false + +--- @local function run() if bRunning then error( "rednet is already running", 2 ) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua b/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua index 54da34a32..50501bbaf 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua @@ -1,16 +1,31 @@ +--- The settings API allows to store values and save them to a file for +-- persistent configurations for CraftOS and your programs. +-- +-- By default, the settings API will load its configuration from the +-- `/.settings` file. One can then use @{settings.save} to update the file. +-- +-- @module settings + local expect = dofile("rom/modules/main/cc/expect.lua").expect local tSettings = {} -function set( sName, value ) - expect(1, sName, "string") +--- Set the value of a setting. +-- +-- @tparam string name The name of the setting to set +-- @param value The setting's value. This cannot be `nil`, and must be +-- serialisable by @{textutils.serialize}. +-- @throws If this value cannot be serialised +-- @see settings.unset +function set( name, value ) + expect(1, name, "string") expect(2, value, "number", "string", "boolean", "table") if type(value) == "table" then -- Ensure value is serializeable value = textutils.unserialize( textutils.serialize(value) ) end - tSettings[ sName ] = value + tSettings[ name ] = value end local copy @@ -26,9 +41,15 @@ function copy( value ) end end -function get( sName, default ) - expect(1, sName, "string") - local result = tSettings[ sName ] +--- Get the value of a setting. +-- +-- @tparam string name The name of the setting to get. +-- @param[opt] default The value to use should there be pre-existing value for +-- this setting. Defaults to `nil`. +-- @return The setting's, or `default` if the setting has not been set. +function get( name, default ) + expect(1, name, "string") + local result = tSettings[ name ] if result ~= nil then return copy(result) else @@ -36,15 +57,31 @@ function get( sName, default ) end end -function unset( sName ) - expect(1, sName, "string") - tSettings[ sName ] = nil +--- Remove the value of a setting, clearing it back to `nil`. +-- +-- @{settings.get} will return the default value until the setting's value is +-- @{settings.set|set}, or the computer is rebooted. +-- +-- @tparam string name The name of the setting to unset. +-- @see settings.set +-- @see settings.clear +function unset( name ) + expect(1, name, "string") + tSettings[ name ] = nil end +--- Removes the value of all settings. Equivalent to calling @{settings.unset} +--- on every setting. +-- +-- @see settings.unset function clear() tSettings = {} end +--- Get the names of all currently defined settings. +-- +-- @treturn { string } An alphabetically sorted list of all currently-defined +-- settings. function getNames() local result = {} for k in pairs( tSettings ) do @@ -54,6 +91,17 @@ function getNames() return result end +--- Load settings from the given file. +-- +-- Existing settings will be merged with any pre-existing ones. Conflicting +-- entries will be overwritten, but any others will be preserved. +-- +-- @tparam string sPath The file to load from. +-- @treturn boolean Whether settings were successfully read from this +-- file. Reasons for failure may include the file not existing or being +-- corrupted. +-- +-- @see settings.save function load( sPath ) expect(1, sPath, "string") local file = fs.open( sPath, "r" ) @@ -79,6 +127,15 @@ function load( sPath ) return true end +--- Save settings to the given file. +-- +-- This will entirely overwrite the pre-existing file. Settings defined in the +-- file, but not currently loaded will be removed. +-- +-- @tparam string sPath The path to save settings to. +-- @treturn boolean If the settings were successfully saved. +-- +-- @see settings.load function save( sPath ) expect(1, sPath, "string") local file = fs.open( sPath, "w" ) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/term.lua b/src/main/resources/assets/computercraft/lua/rom/apis/term.lua index c6f9c2a9b..095fe1e9f 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/term.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/term.lua @@ -1,3 +1,8 @@ +--- The Terminal API provides functions for writing text to the terminal and +-- monitors, and drawing ASCII graphics. +-- +-- @module term + local expect = dofile("rom/modules/main/cc/expect.lua").expect local native = term.native and term.native() or term @@ -9,8 +14,26 @@ local function wrap( _sFunction ) end end -local term = {} +local term = _ENV +--- Redirects terminal output to a monitor, a @{window}, or any other custom +-- terminal object. Once the redirect is performed, any calls to a "term" +-- function - or to a function that makes use of a term function, as @{print} - +-- will instead operate with the new terminal object. +-- +-- A "terminal object" is simply a table that contains functions with the same +-- names - and general features - as those found in the term table. For example, +-- a wrapped monitor is suitable. +-- +-- The redirect can be undone by pointing back to the previous terminal object +-- (which this function returns whenever you switch). +-- +-- @tparam Redirect target The terminal redirect the @{term} API will draw to. +-- @treturn Redirect The previous redirect object, as returned by +-- @{term.current}. +-- @usage +-- Redirect to a monitor on the right of the computer. +-- term.redirect(peripheral.wrap("right")) term.redirect = function( target ) expect(1, target, "table") if target == term or target == _G.term then @@ -30,14 +53,24 @@ term.redirect = function( target ) return oldRedirectTarget end +--- Returns the current terminal object of the computer. +-- +-- @treturn Redirect The current terminal redirect +-- @usage +-- Create a new @{window} which draws to the current redirect target +-- window.create(term.current(), 1, 1, 10, 10) term.current = function() return redirectTarget end +--- Get the native terminal object of the current computer. +-- +-- It is recommended you do not use this function unless you absolutely have +-- to. In a multitasked environment, @{term.native} will _not_ be the current +-- terminal object, and so drawing may interfere with other programs. +-- +-- @treturn Redirect The native terminal redirect. term.native = function() - -- NOTE: please don't use this function unless you have to. - -- If you're running in a redirected or multitasked enviorment, term.native() will NOT be - -- the current terminal when your program starts up. It is far better to use term.current() return native end @@ -49,12 +82,7 @@ for _, method in ipairs { "nativePaletteColor", "nativePaletteColour" } do end for k, v in pairs( native ) do - if type( k ) == "string" and type( v ) == "function" and term[k] == nil then + if type( k ) == "string" and type( v ) == "function" and rawget(term, k) == nil then term[k] = wrap( k ) end end - -local env = _ENV -for k, v in pairs( term ) do - env[k] = v -end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua b/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua index de7cd4e95..4be4388f7 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua @@ -1,5 +1,20 @@ +--- The `textutils` API provides helpful utilities for formatting and +-- manipulating strings. +-- +-- @module textutils + local expect = dofile("rom/modules/main/cc/expect.lua").expect +--- Slowly writes string text at current cursor position, +-- character-by-character. +-- +-- Like @{write}, this does not insert a newline at the end. +-- +-- @tparam string sText The the text to write to the screen +-- @tparam[opt] number nRate The number of characters to write each second, +-- Defaults to 20. +-- @usage textutils.slowWrite("Hello, world!") +-- @usage textutils.slowWrite("Hello, world!", 5) function slowWrite( sText, nRate ) expect(2, nRate, "number", "nil") nRate = nRate or 20 @@ -21,11 +36,28 @@ function slowWrite( sText, nRate ) end end +--- Slowly prints string text at current cursor position, +-- character-by-character. +-- +-- Like @{print}, this inserts a newline after printing. +-- +-- @tparam string sText The the text to write to the screen +-- @tparam[opt] number nRate The number of characters to write each second, +-- Defaults to 20. +-- @usage textutils.slowPrint("Hello, world!") +-- @usage textutils.slowPrint("Hello, world!", 5) function slowPrint( sText, nRate ) slowWrite( sText, nRate ) print() end +--- Takes input time and formats it in a more readable format such as `6:30 PM`. +-- +-- @tparam number nTime The time to format, as provided by @{os.time}. +-- @tparam[opt] boolean bTwentyFourHour Whether to format this as a 24-hour +-- clock (`18:30`) rather than a 12-hour one (`6:30 AM`) +-- @treturn string The formatted time +-- @usage textutils.formatTime(os.time()) function formatTime( nTime, bTwentyFourHour ) expect(1, nTime, "number") expect(2, bTwentyFourHour, "boolean", "nil") @@ -71,6 +103,23 @@ local function makePagedScroll( _term, _nFreeLines ) end end +--- Prints a given string to the display. +-- +-- If the action can be completed without scrolling, it acts much the same as +-- @{print}; otherwise, it will throw up a "Press any key to continue" prompt at +-- the bottom of the display. Each press will cause it to scroll down and write +-- a single line more before prompting again, if need be. +-- +-- @tparam string _sText The text to print to the screen. +-- @tparam[opt] number _nFreeLines The number of lines which will be +-- automatically scrolled before the first prompt appears (meaning _nFreeLines + +-- 1 lines will be printed). This can be set to the terminal's height - 2 to +-- always try to fill the screen. Defaults to 0, meaning only one line is +-- displayed before prompting. +-- @treturn number The number of lines printed. +-- @usage +-- local width, height = term.getSize() +-- textutils.pagedPrint(("This is a rather verbose dose of repetition.\n"):rep(30), height - 2) function pagedPrint( _sText, _nFreeLines ) expect(2, _nFreeLines, "number", "nil") -- Setup a redirector @@ -159,10 +208,30 @@ local function tabulateCommon( bPaged, ... ) end end +--- Prints tables in a structured form. +-- +-- This accepts multiple arguments, either a table or a number. When +-- encountering a table, this will be treated as a table row, with each column +-- width being auto-adjusted. +-- +-- When encountering a number, this sets the text color of the subsequent rows to it. +-- +-- @tparam {string...}|number ... The rows and text colors to display. +-- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" }) function tabulate( ... ) return tabulateCommon( false, ... ) end +--- Prints tables in a structured form, stopping and prompting for input should +-- the result not fit on the terminal. +-- +-- This functions identically to @{textutils.tabulate}, but will prompt for user +-- input should the whole output not fit on the display. +-- +-- @tparam {string...}|number ... The rows and text colors to display. +-- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" }) +-- @see textutils.tabulate +-- @see textutils.pagedPrint function pagedTabulate( ... ) return tabulateCommon( true, ... ) end @@ -238,6 +307,13 @@ local function serializeImpl( t, tTracking, sIndent ) end end +--- A table representing an empty JSON array, in order to distinguish it from an +-- empty JSON object. +-- +-- The contents of this table should not be modified. +-- +-- @usage textutils.serialiseJSON(textutils.empty_json_array) +-- @see textutils.serialiseJSON empty_json_array = setmetatable({}, { __newindex = function() error("attempt to mutate textutils.empty_json_array", 2) @@ -310,11 +386,28 @@ local function serializeJSONImpl( t, tTracking, bNBTStyle ) end end +--- Convert a Lua object into a textual representation, suitable for +-- saving in a file or pretty-printing. +-- +-- @param t The object to serialise +-- @treturn string The serialised representation +-- @throws If the object contains a value which cannot be +-- serialised. This includes functions and tables which appear multiple +-- times. function serialize( t ) local tTracking = {} return serializeImpl( t, tTracking, "" ) end +serialise = serialize -- GB version + +--- Converts a serialised string back into a reassembled Lua object. +-- +-- This is mainly used together with @{textutils.serialize}. +-- +-- @tparam string s The serialised string to deserialise. +-- @return[1] The deserialised object +-- @treturn[2] nil If the object could not be deserialised. function unserialize( s ) expect(1, s, "string") local func = load( "return " .. s, "unserialize", "t", {} ) @@ -327,6 +420,26 @@ function unserialize( s ) return nil end +unserialise = unserialize -- GB version + +--- Returns a JSON representation of the given data. +-- +-- This function attempts to guess whether a table is a JSON array or +-- object. However, empty tables are assumed to be empty objects - use +-- @{textutils.empty_json_array} to mark an empty array. +-- +-- This is largely intended for interacting with various functions from the +-- @{commands} API, though may also be used in making @{http} requests. +-- +-- @param t The value to serialise. Like @{textutils.serialise}, this should not +-- contain recursive tables or functions. +-- @tparam[opt] boolean bNBTStyle Whether to produce NBT-style JSON (non-quoted keys) +-- instead of standard JSON. +-- @treturn string The JSON representation of the input. +-- @throws If the object contains a value which cannot be +-- serialised. This includes functions and tables which appear multiple +-- times. +-- @usage textutils.serializeJSON({ values = { 1, "2", true } }) function serializeJSON( t, bNBTStyle ) expect(1, t, "table", "string", "number", "boolean") expect(2, bNBTStyle, "boolean", "nil") @@ -334,6 +447,13 @@ function serializeJSON( t, bNBTStyle ) return serializeJSONImpl( t, tTracking, bNBTStyle or false ) end +serialiseJSON = serializeJSON -- GB version + +--- Replaces certain characters in a string to make it safe for use in URLs or POST data. +-- +-- @tparam string str The string to encode +-- @treturn string The encoded string. +-- @usage print("https://example.com/?view=" .. textutils.urlEncode(read())) function urlEncode( str ) expect(1, str, "string") if str then @@ -356,6 +476,23 @@ function urlEncode( str ) end local tEmpty = {} + +--- Provides a list of possible completions for a partial Lua expression. +-- +-- If the completed element is a table, suggestions will have `.` appended to +-- them. Similarly, functions have `(` appended to them. +-- +-- @tparam string sSearchText The partial expression to complete, such as a +-- variable name or table index. +-- +-- @tparam[opt] table tSearchTable The table to find variables in, defaulting to +-- the global environment (@{_G}). The function also searches the "parent" +-- environment via the `__index` metatable field. +-- +-- @treturn { string... } The (possibly empty) list of completions. +-- @see shell.setCompletionFunction +-- @see read +-- @usage textutils.complete( "pa", getfenv() ) function complete( sSearchText, tSearchTable ) expect(1, sSearchText, "string") expect(2, tSearchTable, "table", "nil") @@ -431,8 +568,3 @@ function complete( sSearchText, tSearchTable ) table.sort( tResults ) return tResults end - --- GB versions -serialise = serialize -unserialise = unserialize -serialiseJSON = serializeJSON diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua b/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua index c471212a4..9b5fab369 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua @@ -1,3 +1,6 @@ +--- The turtle API allows you to control your turtle. +-- +-- @module turtle if not turtle then error( "Cannot load turtle API on computer", 2 ) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/vector.lua b/src/main/resources/assets/computercraft/lua/rom/apis/vector.lua index 4d6c159f9..e19937bba 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/vector.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/vector.lua @@ -1,85 +1,178 @@ +--- The vector API provides methods to create and manipulate vectors. +-- +-- An introduction to vectors can be found on [Wikipedia][wiki]. +-- +-- [wiki]: http://en.wikipedia.org/wiki/Euclidean_vector +-- +-- @module vector +--- A 3-dimensional vector, with `x`, `y`, and `z` values. +-- +-- This is suitable for representing both position and directional vectors. +-- +-- @type Vector local vector = { - add = function( self, o ) - return vector.new( - self.x + o.x, - self.y + o.y, - self.z + o.z - ) - end, - sub = function( self, o ) - return vector.new( - self.x - o.x, - self.y - o.y, - self.z - o.z - ) - end, - mul = function( self, m ) - return vector.new( - self.x * m, - self.y * m, - self.z * m - ) - end, - div = function( self, m ) - return vector.new( - self.x / m, - self.y / m, - self.z / m - ) - end, - unm = function( self ) - return vector.new( - -self.x, - -self.y, - -self.z - ) - end, - dot = function( self, o ) - return self.x * o.x + self.y * o.y + self.z * o.z - end, - cross = function( self, o ) - return vector.new( - self.y * o.z - self.z * o.y, - self.z * o.x - self.x * o.z, - self.x * o.y - self.y * o.x - ) - end, - length = function( self ) - return math.sqrt( self.x * self.x + self.y * self.y + self.z * self.z ) - end, - normalize = function( self ) - return self:mul( 1 / self:length() ) - end, - round = function( self, nTolerance ) - nTolerance = nTolerance or 1.0 - return vector.new( - math.floor( (self.x + nTolerance * 0.5) / nTolerance ) * nTolerance, - math.floor( (self.y + nTolerance * 0.5) / nTolerance ) * nTolerance, - math.floor( (self.z + nTolerance * 0.5) / nTolerance ) * nTolerance - ) - end, - tostring = function( self ) - return self.x .. "," .. self.y .. "," .. self.z - end, + --- Adds two vectors together. + -- + -- @tparam Vector self The first vector to add. + -- @tparam Vector o The second vector to add. + -- @treturn Vector The resulting vector + -- @usage v1:add(v2) + -- @usage v1 + v2 + add = function(self, o) + return vector.new( + self.x + o.x, + self.y + o.y, + self.z + o.z + ) + end, + + --- Subtracts one vector from another. + -- + -- @tparam Vector self The vector to subtract from. + -- @tparam Vector o The vector to subtract. + -- @treturn Vector The resulting vector + -- @usage v1:sub(v2) + -- @usage v1 - v2 + sub = function(self, o) + return vector.new( + self.x - o.x, + self.y - o.y, + self.z - o.z + ) + end, + + --- Multiplies a vector by a scalar value. + -- + -- @tparam Vector self The vector to multiply. + -- @tparam number m The scalar value to multiply with. + -- @treturn Vector A vector with value `(x * m, y * m, z * m)`. + -- @usage v:mul(3) + -- @usage v * 3 + mul = function(self, m) + return vector.new( + self.x * m, + self.y * m, + self.z * m + ) + end, + + --- Divides a vector by a scalar value. + -- + -- @tparam Vector self The vector to divide. + -- @tparam number m The scalar value to divide by. + -- @treturn Vector A vector with value `(x / m, y / m, z / m)`. + -- @usage v:div(3) + -- @usage v / 3 + div = function(self, m) + return vector.new( + self.x / m, + self.y / m, + self.z / m + ) + end, + + --- Negate a vector + -- + -- @tparam Vector self The vector to negate. + -- @treturn Vector The negated vector. + -- @usage -v + unm = function(self) + return vector.new( + -self.x, + -self.y, + -self.z + ) + end, + + --- Compute the dot product of two vectors + -- + -- @tparam Vector self The first vector to compute the dot product of. + -- @tparam Vector o The second vector to compute the dot product of. + -- @treturn Vector The dot product of `self` and `o`. + -- @usage v1:dot(v2) + dot = function(self, o) + return self.x * o.x + self.y * o.y + self.z * o.z + end, + + --- Compute the cross product of two vectors + -- + -- @tparam Vector self The first vector to compute the cross product of. + -- @tparam Vector o The second vector to compute the cross product of. + -- @treturn Vector The cross product of `self` and `o`. + -- @usage v1:cross(v2) + cross = function(self, o) + return vector.new( + self.y * o.z - self.z * o.y, + self.z * o.x - self.x * o.z, + self.x * o.y - self.y * o.x + ) + end, + + --- Get the length (also referred to as magnitude) of this vector. + -- @tparam Vector self This vector. + -- @treturn number The length of this vector. + length = function(self) + return math.sqrt( self.x * self.x + self.y * self.y + self.z * self.z ) + end, + + --- Divide this vector by its length, producing with the same direction, but + -- of length 1. + -- + -- @tparam Vector self The vector to normalise + -- @treturn Vector The normalised vector + -- @usage v:normalize() + normalize = function(self) + return self:mul( 1 / self:length() ) + end, + + --- Construct a vector with each dimension rounded to the nearest value. + -- + -- @tparam Vector self The vector to round + -- @tparam[opt] number tolerance The tolerance that we should round to, + -- defaulting to 1. For instance, a tolerance of 0.5 will round to the + -- nearest 0.5. + -- @treturn Vector The rounded vector. + round = function(self, tolerance) + tolerance = tolerance or 1.0 + return vector.new( + math.floor((self.x + tolerance * 0.5) / tolerance) * tolerance, + math.floor((self.y + tolerance * 0.5) / tolerance) * tolerance, + math.floor((self.z + tolerance * 0.5) / tolerance) * tolerance + ) + end, + + --- Convert this vector into a string, for pretty printing. + -- + -- @tparam Vector self This vector. + -- @treturn string This vector's string representation. + -- @usage v:tostring() + -- @usage tostring(v) + tostring = function(self) + return self.x .. "," .. self.y .. "," .. self.z + end, } local vmetatable = { - __index = vector, - __add = vector.add, - __sub = vector.sub, - __mul = vector.mul, - __div = vector.div, - __unm = vector.unm, - __tostring = vector.tostring, + __index = vector, + __add = vector.add, + __sub = vector.sub, + __mul = vector.mul, + __div = vector.div, + __unm = vector.unm, + __tostring = vector.tostring, } -function new( x, y, z ) - local v = { - x = tonumber(x) or 0, - y = tonumber(y) or 0, - z = tonumber(z) or 0, - } - setmetatable( v, vmetatable ) - return v +--- Construct a new @{Vector} with the given coordinates. +-- +-- @tparam number x The X coordinate or direction of the vector. +-- @tparam number y The Y coordinate or direction of the vector. +-- @tparam number z The Z coordinate or direction of the vector. +-- @treturn Vector The constructed vector. +function new(x, y, z) + return setmetatable({ + x = tonumber(x) or 0, + y = tonumber(y) or 0, + z = tonumber(z) or 0, + }, vmetatable) end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/window.lua b/src/main/resources/assets/computercraft/lua/rom/apis/window.lua index 0e24b7696..5617c2175 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/window.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/window.lua @@ -1,3 +1,32 @@ +--- The Window API allows easy definition of spaces within the display that can +-- be written/drawn to, then later redrawn/repositioned/etc as need be. The API +-- itself contains only one function, @{window.create}, which returns the +-- windows themselves. +-- +-- Windows are considered terminal objects - as such, they have access to nearly +-- all the commands in the term API (plus a few extras of their own, listed +-- within said API) and are valid targets to redirect to. +-- +-- Each window has a "parent" terminal object, which can be the computer's own +-- display, a monitor, another window or even other, user-defined terminal +-- objects. Whenever a window is rendered to, the actual screen-writing is +-- performed via that parent (or, if that has one too, then that parent, and so +-- forth). Bear in mind that the cursor of a window's parent will hence be moved +-- around etc when writing a given child window. +-- +-- Windows retain a memory of everything rendered "through" them (hence acting +-- as display buffers), and if the parent's display is wiped, the window's +-- content can be easily redrawn later. A window may also be flagged as +-- invisible, preventing any changes to it from being rendered until it's +-- flagged as visible once more. +-- +-- A parent terminal object may have multiple children assigned to it, and +-- windows may overlap. For example, the Multishell system functions by +-- assigning each tab a window covering the screen, each using the starting +-- terminal display as its parent, and only one of which is visible at a time. +-- +-- @module window + local expect = dofile("rom/modules/main/cc/expect.lua").expect local tHex = { @@ -23,6 +52,24 @@ local type = type local string_rep = string.rep local string_sub = string.sub +--- Returns a terminal object that is a space within the specified parent +-- terminal object. This can then be used (or even redirected to) in the same +-- manner as eg a wrapped monitor. Refer to @{term|the term API} for a list of +-- functions available to it. +-- +-- @{term} itself may not be passed as the parent, though @{term.native} is +-- acceptable. Generally, @{term.current} or a wrapped monitor will be most +-- suitable, though windows may even have other windows assigned as their +-- parents. +-- +-- @tparam term.Redirect parent The parent terminal redirect to draw to. +-- @tparam number nX The x coordinate this window is drawn at in the parent terminal +-- @tparam number nY The y coordinate this window is drawn at in the parent terminal +-- @tparam number nWidth The width of this window +-- @tparam number nHeight The height of this window +-- @tparam[opt] boolean bStartVisible Whether this window is visible by +-- default. Defaults to `true`. +-- @treturn Window The constructed window function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) expect(1, parent, "table") expect(2, nX, "number") @@ -182,7 +229,9 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) end end - -- Terminal implementation + --- Terminal implementation + -- + -- @type Window local window = {} function window.write( sText ) diff --git a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua index 7b1a53e8c..1791a29a0 100644 --- a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua +++ b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua @@ -7,7 +7,7 @@ local native_select, native_type = select, type --- Expect an argument to have a specific type. -- --- @tparam int index The 1-based argument index. +-- @tparam number index The 1-based argument index. -- @param value The argument's value. -- @tparam string ... The allowed types of the argument. -- @throws If the value is not one of the allowed types. diff --git a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua index 2fdaee187..5a34ad389 100644 --- a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua +++ b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua @@ -2,7 +2,7 @@ -- aesthetically pleasing manner. -- -- In order to display something using @{cc.pretty}, you build up a series of --- @{documents|Doc}. These behave a little bit like strings; you can concatenate +-- @{Doc|documents}. These behave a little bit like strings; you can concatenate -- them together and then print them to the screen. -- -- However, documents also allow you to control how they should be printed. There @@ -28,7 +28,7 @@ local function append(out, value) out[n], out.n = value, n end ---- A document, which +--- A document containing formatted text, with multiple possible layouts. -- -- Documents effectively represent a sequence of strings in alternative layouts, -- which we will try to print in the most compact form necessary. @@ -61,7 +61,7 @@ end -- -- @tparam string text The string to construct a new document with. -- @tparam[opt] number colour The colour this text should be printed with. If not given, we default to the current --- colour. +-- colour. -- @treturn Doc The document with the provided text. local function text(text, colour) expect(1, text, "string") @@ -94,7 +94,7 @@ local function text(text, colour) end --- Concatenate several documents together. This behaves very similar to string concatenation. - +-- -- @tparam Doc|string ... The documents to concatenate. -- @treturn Doc The concatenated documents. -- @usage pretty.concat(doc1, " - ", doc2) @@ -113,7 +113,7 @@ local function concat(...) return setmetatable(args, Doc) end -Doc.__concat = concat +Doc.__concat = concat --- @local --- Indent later lines of the given document with the given number of spaces. -- @@ -121,7 +121,7 @@ Doc.__concat = concat -- ```txt -- foo -- bar --- `` +-- ``` -- by two spaces will produce -- ```txt -- foo @@ -271,8 +271,9 @@ end -- -- @tparam Doc doc The document to render. -- @tparam[opt] number width The maximum width of this document. Note that long strings will not be wrapped to --- fit this width - it is only used for finding the best layout. +-- fit this width - it is only used for finding the best layout. -- @tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in. +-- @treturn string The rendered document as a string. local function render(doc, width, ribbon_frac) if getmetatable(doc) ~= Doc then expect(1, doc, "document") end expect(2, width, "number", "nil") @@ -316,6 +317,8 @@ local function render(doc, width, ribbon_frac) return table.concat(out, "", 1, out.n) end +Doc.__tostring = render --- @local + local keywords = { [ "and" ] = true, [ "break" ] = true, [ "do" ] = true, [ "else" ] = true, [ "elseif" ] = true, [ "end" ] = true, [ "false" ] = true, [ "for" ] = true, diff --git a/src/test/resources/test-rom/mcfly.lua b/src/test/resources/test-rom/mcfly.lua index 20dcd4cd2..2fe853b95 100644 --- a/src/test/resources/test-rom/mcfly.lua +++ b/src/test/resources/test-rom/mcfly.lua @@ -356,32 +356,33 @@ function expect_mt:called_with_matching(...) return called_with_check(matches, self, ...) end -local expect = setmetatable({ - --- Construct an expectation on the error message calling this function - -- produces - -- - -- @tparam fun The function to call - -- @param ... The function arguments - -- @return The new expectation - error = function(fun, ...) - local ok, res = pcall(fun, ...) local _, line = pcall(error, "", 2) - if ok then fail("expected function to error") end - if res:sub(1, #line) == line then - res = res:sub(#line + 1) - elseif res:sub(1, 7) == "pcall: " then - res = res:sub(8) - end - return setmetatable({ value = res }, expect_mt) - end, -}, { - --- Construct a new expectation from the provided value - -- - -- @param value The value to apply assertions to - -- @return The new expectation - __call = function(_, value) - return setmetatable({ value = value }, expect_mt) - end, -}) +local expect = {} +setmetatable(expect, expect) + +--- Construct an expectation on the error message calling this function +-- produces +-- +-- @tparam fun The function to call +-- @param ... The function arguments +-- @return The new expectation +function expect.error(fun, ...) + local ok, res = pcall(fun, ...) local _, line = pcall(error, "", 2) + if ok then fail("expected function to error") end + if res:sub(1, #line) == line then + res = res:sub(#line + 1) + elseif res:sub(1, 7) == "pcall: " then + res = res:sub(8) + end + return setmetatable({ value = res }, expect_mt) +end + +--- Construct a new expectation from the provided value +-- +-- @param value The value to apply assertions to +-- @return The new expectation +function expect:__call(value) + return setmetatable({ value = value }, expect_mt) +end --- The stack of "describe"s. local test_stack = { n = 0 } @@ -390,7 +391,8 @@ local test_stack = { n = 0 } local tests_locked = false --- The list of tests that we'll run -local test_list, test_map, test_count = { }, { }, 0 +local test_list = {} +local test_map, test_count = {}, 0 --- Add a new test to our queue. -- diff --git a/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua b/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua index 38b2ecc3c..2949c1a75 100644 --- a/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua +++ b/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua @@ -163,7 +163,7 @@ describe("cc.pretty", function() end) 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. local function pretty(x, width) return pp.render(pp.pretty(x), width) end