An initial stab at documentation generation (#360)

This adds documentation comments to many of CC's Lua APIs, and
a couple of the Java ones, through the use of stubs. We then
export these to HTML using illuaminate [1] and upload them to our
documentation site [2].

Uploads currently occur on pushes to master and any release/tag. The
site is entirely static - there is no way to switch between versions,
etc... but hopefully we can improve this in the future.

[1]: github.com/SquidDev/illuaminate/
[2]: https://tweaked.cc/
This commit is contained in:
Jonathan Coates 2020-04-10 10:27:53 +01:00 committed by GitHub
parent 1ccd687c00
commit ef8da8054f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 2094 additions and 336 deletions

View File

@ -14,5 +14,9 @@ trim_trailing_whitespace = false
[*.sexp]
indent_size = 2
[*.yml]
indent_size = 2
[*.properties]
insert_final_newline = false

16
.github/workflows/make-doc.sh vendored Executable file
View File

@ -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"

29
.github/workflows/make-doc.yml vendored Normal file
View File

@ -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 }}

1
.gitignore vendored
View File

@ -3,6 +3,7 @@
/logs
/build
/out
/doc/**/*.html
# Runtime directories
/run

View File

@ -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,

13
doc/index.md Normal file
View File

@ -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

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

6
doc/stub/commands.lua Normal file
View File

@ -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

42
doc/stub/fs.lua Normal file
View File

@ -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

213
doc/stub/http.lua Normal file
View File

@ -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

17
doc/stub/os.lua Normal file
View File

@ -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

14
doc/stub/redstone.lua Normal file
View File

@ -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

52
doc/stub/term.lua Normal file
View File

@ -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

42
doc/stub/turtle.lua Normal file
View File

@ -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

186
doc/styles.css Normal file
View File

@ -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; }

View File

@ -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)))

View File

@ -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

View File

@ -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 )

View File

@ -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

View File

@ -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, ... )

View File

@ -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

View File

@ -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")

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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] )

View File

@ -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

View File

@ -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

View File

@ -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 )

View File

@ -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" )

View File

@ -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

View File

@ -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

View File

@ -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 )

View File

@ -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

View File

@ -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 )

View File

@ -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.

View File

@ -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,

View File

@ -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.
--

View File

@ -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