diff --git a/doc/index.md b/doc/index.md index c22acd230..9aca61855 100644 --- a/doc/index.md +++ b/doc/index.md @@ -1,4 +1,4 @@ -# ![CC: Tweaked](logo.png) [![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](logo.png) [![Download CC: Tweaked on CurseForge](https://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], adding programmable computers, turtles and more to Minecraft. diff --git a/doc/styles.css b/doc/styles.css index ef14a7d94..436a8c535 100644 --- a/doc/styles.css +++ b/doc/styles.css @@ -23,7 +23,7 @@ body { "Droid Sans", "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; } -code, pre, .parameter, .type, .definition-name, .reference { +code, pre, .parameter, .type, .definition-name, .reference-code { font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; } diff --git a/gradle.properties b/gradle.properties index 1cc272285..c5ee4ae3b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # Mod properties -mod_version=1.86.2 +mod_version=1.87.0 # Minecraft properties (update mods.toml when changing) mc_version=1.15.2 diff --git a/illuaminate.sexp b/illuaminate.sexp index 22219f22c..32acc06de 100644 --- a/illuaminate.sexp +++ b/illuaminate.sexp @@ -2,10 +2,11 @@ (sources /doc/stub/ - /src/main/resources/data/computercraft/lua/bios.lua - /src/main/resources/data/computercraft/lua/rom/ + /src/main/resources/*/computercraft/lua/bios.lua + /src/main/resources/*/computercraft/lua/rom/ /src/test/resources/test-rom) + (doc (title "CC: Tweaked") (index doc/index.md) @@ -14,13 +15,13 @@ (library-path /doc/stub/ - /src/main/resources/data/computercraft/lua/rom/apis - /src/main/resources/data/computercraft/lua/rom/apis/command - /src/main/resources/data/computercraft/lua/rom/apis/turtle + /src/main/resources/*/computercraft/lua/rom/apis + /src/main/resources/*/computercraft/lua/rom/apis/command + /src/main/resources/*/computercraft/lua/rom/apis/turtle - /src/main/resources/data/computercraft/lua/rom/modules/main - /src/main/resources/data/computercraft/lua/rom/modules/command - /src/main/resources/data/computercraft/lua/rom/modules/turtle)) + /src/main/resources/*/computercraft/lua/rom/modules/main + /src/main/resources/*/computercraft/lua/rom/modules/command + /src/main/resources/*/computercraft/lua/rom/modules/turtle)) (at / (linters @@ -34,9 +35,7 @@ ;; be good to find a compromise in the future, but this works for now. -var:unused-arg - ;; Suppress a couple of documentation comments warnings for now. We'll - ;; hopefully be able to remove them in the future. - -doc:undocumented -doc:undocumented-arg -doc:unresolved-reference + ;; Some APIS (keys, colour and os mainly) are incomplete right now. -var:unresolved-member) (lint (bracket-spaces @@ -49,8 +48,8 @@ ;; We disable the unused global linter in bios.lua and the APIs. In the future ;; hopefully we'll get illuaminate to handle this. (at - (/src/main/resources/data/computercraft/lua/bios.lua - /src/main/resources/data/computercraft/lua/rom/apis/) + (/src/main/resources/*/computercraft/lua/bios.lua + /src/main/resources/*/computercraft/lua/rom/apis/) (linters -var:unused-global) (lint (allow-toplevel-global true))) @@ -59,19 +58,27 @@ (linters -var:unused-global) (lint (allow-toplevel-global true))) -;; Ensure any fully documented modules stay fully documented. +;; Suppress warnings for currently undocumented modules. (at - (/src/main/resources/data/computercraft/lua/rom/apis/colors.lua - /src/main/resources/data/computercraft/lua/rom/apis/colours.lua - /src/main/resources/data/computercraft/lua/rom/apis/disk.lua - /src/main/resources/data/computercraft/lua/rom/apis/gps.lua - /src/main/resources/data/computercraft/lua/rom/apis/help.lua - /src/main/resources/data/computercraft/lua/rom/apis/keys.lua - /src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua - /src/main/resources/data/computercraft/lua/rom/apis/parallel.lua - /src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua - /src/main/resources/data/computercraft/lua/rom/apis/rednet.lua - /src/main/resources/data/computercraft/lua/rom/apis/settings.lua - /src/main/resources/data/computercraft/lua/rom/apis/texutils.lua - /src/main/resources/data/computercraft/lua/rom/apis/vector.lua) - (linters doc:undocumented doc:undocumented-arg)) + (/doc/stub/commands.lua + /doc/stub/fs.lua + /doc/stub/http.lua + /doc/stub/os.lua + /doc/stub/redstone.lua + /doc/stub/term.lua + /doc/stub/turtle.lua + /src/main/resources/*/computercraft/lua/rom/apis/command/commands.lua + /src/main/resources/*/computercraft/lua/rom/apis/io.lua + /src/main/resources/*/computercraft/lua/rom/apis/window.lua + /src/main/resources/*/computercraft/lua/rom/modules/main/cc/shell/completion.lua) + + (linters -doc:undocumented -doc:undocumented-arg)) + +;; These currently rely on unknown references. +(at + (/src/main/resources/*/computercraft/lua/rom/apis/textutils.lua + /src/main/resources/*/computercraft/lua/rom/modules/main/cc/completion.lua + /src/main/resources/*/computercraft/lua/rom/modules/main/cc/shell/completion.lua + /src/main/resources/*/computercraft/lua/rom/programs/advanced/multishell.lua + /src/main/resources/*/computercraft/lua/rom/programs/shell.lua) + (linters -doc:unresolved-reference)) diff --git a/src/main/resources/data/computercraft/lua/bios.lua b/src/main/resources/data/computercraft/lua/bios.lua index f1e80cef4..687c9c798 100644 --- a/src/main/resources/data/computercraft/lua/bios.lua +++ b/src/main/resources/data/computercraft/lua/bios.lua @@ -521,14 +521,11 @@ function os.run(_tEnv, _sPath, ...) expect(1, _tEnv, "table") expect(2, _sPath, "string") - local tArgs = table.pack(...) local tEnv = _tEnv setmetatable(tEnv, { __index = _G }) local fnFile, err = loadfile(_sPath, nil, tEnv) if fnFile then - local ok, err = pcall(function() - fnFile(table.unpack(tArgs, 1, tArgs.n)) - end) + local ok, err = pcall(fnFile, ...) if not ok then if err and err ~= "" then printError(err) @@ -926,7 +923,7 @@ settings.define("list.show_hidden", { }) settings.define("motd.enable", { - default = false, + default = pocket == nil, description = "Display a random message when the computer starts up.", type = "boolean", }) diff --git a/src/main/resources/data/computercraft/lua/rom/apis/disk.lua b/src/main/resources/data/computercraft/lua/rom/apis/disk.lua index fcc907121..9e136b22c 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/disk.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/disk.lua @@ -106,7 +106,7 @@ end -- 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 +-- @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 diff --git a/src/main/resources/data/computercraft/lua/rom/apis/settings.lua b/src/main/resources/data/computercraft/lua/rom/apis/settings.lua index c1fcb45f6..f9925d36f 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/settings.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/settings.lua @@ -32,7 +32,7 @@ for _, v in ipairs(valid_types) do valid_types[v] = true end -- you to provide defaults and additional metadata. -- -- @tparam string name The name of this option --- @tparam[opt] { description? = string, default? = value, type? = string } options +-- @tparam[opt] { description? = string, default? = any, type? = string } options -- Options for this setting. This table accepts the following fields: -- -- - `description`: A description which may be printed when running the `set` program. @@ -127,7 +127,7 @@ end --- Get details about a specific setting. -- -- @tparam string name The name of the setting to get. --- @treturn { description? = string, default? = value, type? = string, value? = value } +-- @treturn { description? = string, default? = any, type? = string, value? = any } -- Information about this setting. This includes all information from @{settings.define}, -- as well as this setting's value. function getDetails(name) diff --git a/src/main/resources/data/computercraft/lua/rom/help/changelog.txt b/src/main/resources/data/computercraft/lua/rom/help/changelog.txt index 94233bdfe..3afe2d05f 100644 --- a/src/main/resources/data/computercraft/lua/rom/help/changelog.txt +++ b/src/main/resources/data/computercraft/lua/rom/help/changelog.txt @@ -1,3 +1,33 @@ +# New features in CC: Tweaked 1.87.0 + +* Add documentation to many Lua functions. This is published online at https://tweaked.cc/. +* Replace to pretty-printer in the Lua REPL. It now supports displaying functions and recursive tables. This printer is may be used within your own code through the `cc.pretty` module. +* Add `fs.getCapacity`. A complement to `fs.getFreeSpace`, this returns the capacity of the supplied drive. +* Add `fs.getAttributes`. This provides file size and type, as well as creation and modification time. +* Update Cobalt version. This backports several features from Lua 5.2 and 5.3: + - The `__len` metamethod may now be used by tables. + - Add `\z`, hexadecimal (`\x00`) and unicode (`\u0000`) string escape codes. + - Add `utf8` lib. + - Mirror Lua's behaviour of tail calls more closely. Native functions are no longer tail called, and tail calls are displayed in the stack trace. + - `table.unpack` now uses `__len` and `__index` metamethods. + - Parser errors now include the token where the error occured. +* Add `textutils.unserializeJSON`. This can be used to decode standard JSON and stringified-NBT. +* The `settings` API now allows "defining" settings. This allows settings to specify a default value and description. +* Enable the motd on non-pocket computers. +* Allow using the menu with the mouse in edit and paint (JakobDev). +* Add Danish and Korean translations (ChristianLW, mindy15963) +* Fire `mouse_up` events in the monitor program. +* Allow specifying a timeout to `websocket.receive`. +* Increase the maximimum limit for websocket messages. +* Optimise capacity checking of computer/disk folders. + +And several bug fixes: +* Fix turtle texture being incorrectly oriented (magiczocker10). +* Prevent copying folders into themselves. +* Normalise file paths within shell.setDir (JakobDev) +* Fix turtles treating waterlogged blocks as water. +* Register an entity renderer for the turtle's fake player. + # New features in CC: Tweaked 1.86.2 * Fix peripheral.getMethods returning an empty table diff --git a/src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt b/src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt index 81c5e7ae2..952c5605b 100644 --- a/src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt +++ b/src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt @@ -1,7 +1,31 @@ -New features in CC: Tweaked 1.86.2 +New features in CC: Tweaked 1.87.0 -* Fix peripheral.getMethods returning an empty table -* Update to Minecraft 1.15.2. This is currently alpha-quality and so is missing - missing features and may be unstable. +* Add documentation to many Lua functions. This is published online at https://tweaked.cc/. +* Replace to pretty-printer in the Lua REPL. It now supports displaying functions and recursive tables. This printer is may be used within your own code through the `cc.pretty` module. +* Add `fs.getCapacity`. A complement to `fs.getFreeSpace`, this returns the capacity of the supplied drive. +* Add `fs.getAttributes`. This provides file size and type, as well as creation and modification time. +* Update Cobalt version. This backports several features from Lua 5.2 and 5.3: + - The `__len` metamethod may now be used by tables. + - Add `\z`, hexadecimal (`\x00`) and unicode (`\u0000`) string escape codes. + - Add `utf8` lib. + - Mirror Lua's behaviour of tail calls more closely. Native functions are no longer tail called, and tail calls are displayed in the stack trace. + - `table.unpack` now uses `__len` and `__index` metamethods. + - Parser errors now include the token where the error occured. +* Add `textutils.unserializeJSON`. This can be used to decode standard JSON and stringified-NBT. +* The `settings` API now allows "defining" settings. This allows settings to specify a default value and description. +* Enable the motd on non-pocket computers. +* Allow using the menu with the mouse in edit and paint (JakobDev). +* Add Danish and Korean translations (ChristianLW, mindy15963) +* Fire `mouse_up` events in the monitor program. +* Allow specifying a timeout to `websocket.receive`. +* Increase the maximimum limit for websocket messages. +* Optimise capacity checking of computer/disk folders. + +And several bug fixes: +* Fix turtle texture being incorrectly oriented (magiczocker10). +* Prevent copying folders into themselves. +* Normalise file paths within shell.setDir (JakobDev) +* Fix turtles treating waterlogged blocks as water. +* Register an entity renderer for the turtle's fake player. Type "help changelog" to see the full version history. diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua index d89c72a91..6be54fe2f 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua @@ -16,7 +16,7 @@ local completion = require "cc.completion" --- Complete the name of a file relative to the current working directory. -- --- @tparam shell shell The shell we're completing in +-- @tparam table shell The shell we're completing in -- @tparam { string... } choices The list of choices to complete from. -- @treturn { string... } A list of suffixes of matching files. local function file(shell, text) @@ -25,7 +25,7 @@ end --- Complete the name of a directory relative to the current working directory. -- --- @tparam shell shell The shell we're completing in +-- @tparam table shell The shell we're completing in -- @tparam { string... } choices The list of choices to complete from. -- @treturn { string... } A list of suffixes of matching directories. local function dir(shell, text) @@ -35,7 +35,7 @@ end --- Complete the name of a file or directory relative to the current working -- directory. -- --- @tparam shell shell The shell we're completing in +-- @tparam table shell The shell we're completing in -- @tparam { string... } choices The list of choices to complete from. -- @tparam { string... } previous The shell arguments before this one. -- @tparam[opt] boolean add_space Whether to add a space after the completed item. @@ -61,9 +61,10 @@ end --- Complete the name of a program. -- --- @tparam shell shell The shell we're completing in +-- @tparam table shell The shell we're completing in -- @tparam { string... } choices The list of choices to complete from. -- @treturn { string... } A list of suffixes of matching programs. +-- @see shell.completeProgram local function program(shell, text) return shell.completeProgram(text) end diff --git a/src/main/resources/data/computercraft/lua/rom/motd.txt b/src/main/resources/data/computercraft/lua/rom/motd.txt index 87f3f0503..8c8f16945 100644 --- a/src/main/resources/data/computercraft/lua/rom/motd.txt +++ b/src/main/resources/data/computercraft/lua/rom/motd.txt @@ -1,43 +1,23 @@ -View the source code at https://github.com/SquidDev-CC/CC-Tweaked +Please report bugs at https://github.com/SquidDev-CC/CC-Tweaked. Thanks! View the documentation at https://wiki.computercraft.cc -Visit the forum at https://forums.computercraft.cc +Show off your programs or ask for help at our forum: https://forums.computercraft.cc You can disable these messages by running "set motd.enable false". -You can create directories with "mkdir". -Want to see hidden files? Run "set list.show_hidden true". -Run "list" or "ls" to see all files in a directory. -You can delete files and directories with "delete" or "rm". Use "pastebin put" to upload a program to pastebin. -Use "pastebin get" to download a program from pastebin. -Use "pastebin run" to run a program from pastebin. Use the "edit" program to create and edit your programs. -You can copy files with "copy" or "cp". -You can use "wget run " to run a program from the internet. You can use "wget" to download a file from the internet. On an advanced computer you can use "fg" or "bg" to run multiple programs at the same time. -Use "type" to see if a path is a file or a directory. -Get a list of all programs with "programs". Use an advanced computer to use colours and the mouse. With a speaker you can play sounds. -Use "motd" to print the Message of the Day. -You can disable the startup from a computer with "set shell.allow_startup false". -You can disable the startup from a disk with "set shell.allow_disk_startup false". Programs that are placed in the "startup" folder in the root of a computer are started on boot. Use a modem to connect with other computers. With the "gps" program you can get the position of a computer. Use "monitor" to run a program on a attached monitor. -View all attached peripherals with "peripherals". -Use "time" to see the in-game time. -You can set the label of a computer with "label set". -A computer needs a label to keep its files if it is destroyed. -You can disable auto completion in the shell with "set shell.autocomplete false". -You can disable auto completion in edit with "set edit.autocomplete false". +Don't forget to label your computer with "label set". Feeling creative? Use a printer to print a book! -Files beginning with a "." character are hidden from "list" by default. +Files beginning with a "." are hidden from "list" by default. Running "set" lists the current values of all settings. -Some programs are only available on advanced computers, turtles, pocket computers, or command computers. -The "equip" and "unequip" programs let you add or remove supported upgrades from a turtle or pocket computer without crafting. -You can change the color of a disk by crafting it with dye. -Right-clicking a turtle with a dye changes its color. +Some programs are only available on advanced computers, turtles, pocket computers or command computers. +The "equip" programs let you add upgrades to a turtle without crafting. +You can change the color of a disk by crafting or right clicking it with dye. You can print on a printed page again to get multiple colors. -Holding the Control and T keys terminates the running program. -Holding Control and S or R shuts down or reboots the computer you are using. +Holding the Ctrl and T keys terminates the running program. diff --git a/src/main/resources/data/computercraft/lua/rom/programs/advanced/multishell.lua b/src/main/resources/data/computercraft/lua/rom/programs/advanced/multishell.lua index c675f2dc6..1ff73ab9f 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/advanced/multishell.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/advanced/multishell.lua @@ -1,3 +1,21 @@ +--- Multishell allows multiple programs to be run at the same time. +-- +-- When multiple programs are running, it displays a tab bar at the top of the +-- screen, which allows you to switch between programs. New programs can be +-- launched using the `fg` or `bg` programs, or using the @{shell.openTab} and +-- @{multishell.launch} functions. +-- +-- Each process is identified by its ID, which corresponds to its position in +-- the tab list. As tabs may be opened and closed, this ID is _not_ constant +-- over a program's run. As such, be careful not to use stale IDs. +-- +-- As with @{shell}, @{multishell} is not a "true" API. Instead, it is a +-- standard program, which launches a shell and injects its API into the shell's +-- environment. This API is not available in the global environment, and so is +-- not available to @{os.loadAPI|APIs}. +-- +-- @module[module] multishell + local expect = dofile("rom/modules/main/cc/expect.lua").expect -- Setup process switching @@ -190,12 +208,26 @@ local function setMenuVisible(bVis) end end -local multishell = {} +local multishell = {} --- @export +--- Get the currently visible process. This will be the one selected on +-- the tab bar. +-- +-- Note, this is different to @{getCurrent}, which returns the process which is +-- currently executing. +-- +-- @treturn number The currently visible process's index. +-- @see setFocus function multishell.getFocus() return nCurrentProcess end +--- Change the currently visible process. +-- +-- @tparam number n The process index to switch to. +-- @treturn boolean If the process was changed successfully. This will +-- return @{false} if there is no process with this id. +-- @see getFocus function multishell.setFocus(n) expect(1, n, "number") if n >= 1 and n <= #tProcesses then @@ -206,6 +238,13 @@ function multishell.setFocus(n) return false end +--- Get the title of the given tab. +-- +-- This starts as the name of the program, but may be changed using +-- @{multishell.setTitle}. +-- @tparam number n The process index. +-- @treturn string|nil The current process title, or @{nil} if the +-- process doesn't exist. function multishell.getTitle(n) expect(1, n, "number") if n >= 1 and n <= #tProcesses then @@ -214,19 +253,45 @@ function multishell.getTitle(n) return nil end -function multishell.setTitle(n, sTitle) +--- Set the title of the given process. +-- +-- @tparam number n The process index. +-- @tparam string title The new process title. +-- @see getTitle +-- @usage Change the title of the current process +-- +-- multishell.setTitle(multishell.getCurrent(), "Hello") +function multishell.setTitle(n, title) expect(1, n, "number") - expect(2, sTitle, "string") + expect(2, title, "string") if n >= 1 and n <= #tProcesses then - setProcessTitle(n, sTitle) + setProcessTitle(n, title) redrawMenu() end end +--- Get the index of the currently running process. +-- +-- @treturn number The currently running process. function multishell.getCurrent() return nRunningProcess end +--- Start a new process, with the given environment, program and arguments. +-- +-- The returned process index is not constant over the program's run. It can be +-- safely used immediately after launching (for instance, to update the title or +-- switch to that tab). However, after your program has yielded, it may no +-- longer be correct. +-- +-- @tparam table tProgramEnv The environment to load the path under. +-- @tparam string sProgramPath The path to the program to run. +-- @param ... Additional arguments to pass to the program. +-- @treturn number The index of the created process. +-- @see os.run +-- @usage Run the "hello" program, and set its title to "Hello!" +-- local id = multishell.launch({}, "/rom/programs/fun/hello.lua") +-- multishell.setTitle(id, "Hello!") function multishell.launch(tProgramEnv, sProgramPath, ...) expect(1, tProgramEnv, "table") expect(2, sProgramPath, "string") @@ -238,6 +303,9 @@ function multishell.launch(tProgramEnv, sProgramPath, ...) return nResult end +--- Get the number of processes within this multishell. +-- +-- @treturn number The number of processes. function multishell.getCount() return #tProcesses end diff --git a/src/main/resources/data/computercraft/lua/rom/programs/edit.lua b/src/main/resources/data/computercraft/lua/rom/programs/edit.lua index a6ea4d8d0..007f37a24 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/edit.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/edit.lua @@ -15,7 +15,7 @@ end -- Create .lua files by default if not fs.exists(sPath) and not string.find(sPath, "%.") then - local sExtension = settings.get("edit.default_extension", "") + local sExtension = settings.get("edit.default_extension") if sExtension ~= "" and type(sExtension) == "string" then sPath = sPath .. "." .. sExtension end @@ -61,7 +61,12 @@ if peripheral.find("printer") then end table.insert(tMenuItems, "Exit") -local sStatus = "Press Ctrl to access menu" +local sStatus +if term.isColour() then + sStatus = "Press Ctrl or click here to access menu" +else + sStatus = "Press Ctrl to access menu" +end if #sStatus > w - 5 then sStatus = "Press Ctrl for menu" end @@ -725,16 +730,35 @@ while bRunning do end elseif sEvent == "mouse_click" then + local cx, cy = param2, param3 if not bMenu then if param == 1 then -- Left click - local cx, cy = param2, param3 if cy < h then local newY = math.min(math.max(scrollY + cy, 1), #tLines) local newX = math.min(math.max(scrollX + cx, 1), #tLines[newY] + 1) setCursor(newX, newY) + else + bMenu = true + redrawMenu() end end + else + if cy == h then + local nMenuPosEnd = 1 + local nMenuPosStart = 1 + for n, sMenuItem in ipairs(tMenuItems) do + nMenuPosEnd = nMenuPosEnd + #sMenuItem + 1 + if cx > nMenuPosStart and cx < nMenuPosEnd then + doMenuItem(n) + end + nMenuPosEnd = nMenuPosEnd + 1 + nMenuPosStart = nMenuPosEnd + end + else + bMenu = false + redrawMenu() + end end elseif sEvent == "mouse_scroll" then diff --git a/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/paint.lua b/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/paint.lua index fbe228091..b7c44526a 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/paint.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/paint.lua @@ -19,7 +19,7 @@ local canvas = {} local mChoices = { "Save", "Exit" } -- The message displayed in the footer bar -local fMessage = "Press Ctrl to access menu" +local fMessage = "Press Ctrl or click here to access menu" ------------------------- -- Initialisation -- @@ -46,7 +46,7 @@ end -- Create .nfp files by default if not fs.exists(sPath) and not string.find(sPath, "%.") then - local sExtension = settings.get("paint.default_extension", "") + local sExtension = settings.get("paint.default_extension") if sExtension ~= "" and type(sExtension) == "string" then sPath = sPath .. "." .. sExtension end @@ -252,6 +252,29 @@ local function drawCanvas() end end +local menu_choices = { + Save = function() + if bReadOnly then + fMessage = "Access denied" + return false + end + local success, err = save(sPath) + if success then + fMessage = "Saved to " .. sPath + else + if err then + fMessage = "Error saving to " .. err + else + fMessage = "Error saving to " .. sPath + end + end + return false + end, + Exit = function() + return true + end, +} + --[[ Draws menu options and handles input from within the menu. returns: true if the program is to be exited; false otherwise @@ -261,6 +284,7 @@ local function accessMenu() local selection = 1 term.setBackgroundColour(colours.black) + while true do -- Draw the menu term.setCursorPos(1, h) @@ -269,27 +293,28 @@ local function accessMenu() for k, v in pairs(mChoices) do if selection == k then term.setTextColour(colours.yellow) - local ox = term.getCursorPos() - term.write("[" .. string.rep(" ", #v) .. "]") - term.setCursorPos(ox + 1, h) + term.write("[") term.setTextColour(colours.white) term.write(v) - term.setCursorPos(term.getCursorPos() + 1, h) + term.setTextColour(colours.yellow) + term.write("]") + term.setTextColour(colours.white) else term.write(" " .. v .. " ") end end -- Handle input in the menu - local id, key = os.pullEvent("key") + local id, param1, param2, param3 = os.pullEvent() if id == "key" then - -- S and E are shortcuts - if key == keys.s then - selection = 1 - key = keys.enter - elseif key == keys.e then - selection = 2 - key = keys.enter + local key = param1 + + -- Handle menu shortcuts. + for _, menu_item in ipairs(mChoices) do + local k = keys[menu_item:sub(1, 1):lower()] + if k and k == key then + return menu_choices[menu_item]() + end end if key == keys.right then @@ -308,29 +333,25 @@ local function accessMenu() elseif key == keys.enter then -- Select an option - if mChoices[selection] == "Save" then - if bReadOnly then - fMessage = "Access denied" - return false - end - local success, err = save(sPath) - if success then - fMessage = "Saved to " .. sPath - else - if err then - fMessage = "Error saving to " .. err - else - fMessage = "Error saving to " .. sPath - end - end - return false - elseif mChoices[selection] == "Exit" then - return true - end + return menu_choices[mChoices[selection]]() elseif key == keys.leftCtrl or keys == keys.rightCtrl then -- Cancel the menu return false end + elseif id == "mouse_click" then + local cx, cy = param2, param3 + if cy ~= h then return false end -- Exit the menu + + local nMenuPosEnd = 1 + local nMenuPosStart = 1 + for _, sMenuItem in ipairs(mChoices) do + nMenuPosEnd = nMenuPosEnd + #sMenuItem + 1 + if cx > nMenuPosStart and cx < nMenuPosEnd then + return menu_choices[sMenuItem]() + end + nMenuPosEnd = nMenuPosEnd + 1 + nMenuPosStart = nMenuPosEnd + end end end end @@ -378,6 +399,10 @@ local function handleEvents() canvas[p3][p2] = paintColour drawCanvasPixel(p2, p3) + elseif p3 == h and id == "mouse_click" then + -- Open menu + programActive = not accessMenu() + drawInterface() end elseif id == "key" then if p1 == keys.leftCtrl or p1 == keys.rightCtrl then diff --git a/src/main/resources/data/computercraft/lua/rom/programs/shell.lua b/src/main/resources/data/computercraft/lua/rom/programs/shell.lua index 0d4b9f337..c36cc9abf 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/shell.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/shell.lua @@ -1,3 +1,15 @@ +--- The shell API provides access to CraftOS's command line interface. +-- +-- It allows you to @{run|start programs}, @{setCompletionFunction|add +-- completion for a program}, and much more. +-- +-- @{shell} is not a "true" API. Instead, it is a standard program, which its +-- API into the programs that it launches. This allows for multiple shells to +-- run at the same time, but means that the API is not available in the global +-- environment, and so is unavailable to other @{os.loadAPI|APIs}. +-- +-- @module[module] shell + local expect = dofile("rom/modules/main/cc/expect.lua").expect local multishell = multishell @@ -15,7 +27,7 @@ local tAliases = parentShell and parentShell.aliases() or {} local tCompletionInfo = parentShell and parentShell.getCompletionInfo() or {} local tProgramStack = {} -local shell = {} +local shell = {} --- @export local function createShellEnv(sDir) local tEnv = {} tEnv.shell = shell @@ -172,6 +184,18 @@ local function tokenise(...) end -- Install shell API + +--- Run a program with the supplied arguments. +-- +-- All arguments are concatenated together and then parsed as a command line. As +-- a result, `shell.run("program a b")` is the same as `shell.run("program", +-- "a", "b")`. +-- +-- @tparam string ... The program to run and its arguments. +-- @treturn boolean Whether the program exited successfully. +-- @usage Run `paint my-image` from within your program: +-- +-- shell.run("paint", "my-image") function shell.run(...) local tWords = tokenise(...) local sCommand = tWords[1] @@ -181,38 +205,83 @@ function shell.run(...) return false end +--- Exit the current shell. +-- +-- This does _not_ terminate your program, it simply makes the shell terminate +-- after your program has finished. If this is the toplevel shell, then the +-- computer will be shutdown. function shell.exit() bExit = true end +--- Return the current working directory. This is what is displayed before the +-- `> ` of the shell prompt, and is used by @{shell.resolve} to handle relative +-- paths. +-- +-- @treturn string The current working directory. +-- @see setDir To change the working directory. function shell.dir() return sDir end -function shell.setDir(_sDir) - expect(1, _sDir, "string") - if not fs.isDir(_sDir) then +--- Set the current working directory. +-- +-- @tparam string dir The new working directory. +-- @throws If the path does not exist or is not a directory. +-- @usage Set the working directory to "rom" +-- +-- shell.setDir("rom") +function shell.setDir(dir) + expect(1, dir, "string") + if not fs.isDir(dir) then error("Not a directory", 2) end - sDir = fs.combine(_sDir, "") + sDir = fs.combine(dir, "") end +--- Set the path where programs are located. +-- +-- The path is composed of a list of directory names in a string, each separated +-- by a colon (`:`). On normal turtles will look in the current directory (`.`), +-- `/rom/programs` and `/rom/programs/turtle` folder, making the path +-- `.:/rom/programs:/rom/programs/turtle`. +-- +-- @treturn string The current shell's path. +-- @see setPath To change the current path. function shell.path() return sPath end -function shell.setPath(_sPath) - expect(1, _sPath, "string") - sPath = _sPath +--- Set the @{path|current program path}. +-- +-- Be careful to prefix directories with a `/`. Otherwise they will be searched +-- for from the @{shell.dir|current directory}, rather than the computer's root. +-- +-- @tparam string path The new program path. +function shell.setPath(path) + expect(1, path, "string") + sPath = path end -function shell.resolve(_sPath) - expect(1, _sPath, "string") - local sStartChar = string.sub(_sPath, 1, 1) +--- Resolve a relative path to an absolute path. +-- +-- The @{fs} and @{io} APIs work using absolute paths, and so we must convert +-- any paths relative to the @{dir|current directory} to absolute ones. This +-- does nothing when the path starts with `/`. +-- +-- @tparam string path The path to resolve. +-- @usage Resolve `startup.lua` when in the `rom` folder. +-- +-- shell.setDir("rom") +-- print(shell.resolve("startup.lua")) +-- -- => rom/startup.lua +function shell.resolve(path) + expect(1, path, "string") + local sStartChar = string.sub(path, 1, 1) if sStartChar == "/" or sStartChar == "\\" then - return fs.combine("", _sPath) + return fs.combine("", path) else - return fs.combine(sDir, _sPath) + return fs.combine(sDir, path) end end @@ -226,16 +295,25 @@ local function pathWithExtension(_sPath, _sExt) return _sPath .. "." .. _sExt end -function shell.resolveProgram(_sCommand) - expect(1, _sCommand, "string") +--- Resolve a program, using the @{path|program path} and list of @{aliases|aliases}. +-- +-- @tparam string command The name of the program +-- @treturn string|nil The absolute path to the program, or @{nil} if it could +-- not be found. +-- @usage Locate the `hello` program. +-- +-- shell.resolveProgram("hello") +-- -- => rom/programs/fun/hello.lua +function shell.resolveProgram(command) + expect(1, command, "string") -- Substitute aliases firsts - if tAliases[_sCommand] ~= nil then - _sCommand = tAliases[_sCommand] + if tAliases[command] ~= nil then + command = tAliases[command] end -- If the path is a global path, use it directly - if _sCommand:find("/") or _sCommand:find("\\") then - local sPath = shell.resolve(_sCommand) + if command:find("/") or command:find("\\") then + local sPath = shell.resolve(command) if fs.exists(sPath) and not fs.isDir(sPath) then return sPath else @@ -249,7 +327,7 @@ function shell.resolveProgram(_sCommand) -- Otherwise, look on the path variable for sPath in string.gmatch(sPath, "[^:]+") do - sPath = fs.combine(shell.resolve(sPath), _sCommand) + sPath = fs.combine(shell.resolve(sPath), command) if fs.exists(sPath) and not fs.isDir(sPath) then return sPath else @@ -264,7 +342,15 @@ function shell.resolveProgram(_sCommand) return nil end -function shell.programs(_bIncludeHidden) +--- Return a list of all programs on the @{shell.path|path}. +-- +-- @tparam[opt] boolean include_hidden Include hidden files. Namely, any which +-- start with `.`. +-- @treturn { string } A list of available programs. +-- @usage textutils.tabulate(shell.programs()) +function shell.programs(include_hidden) + expect(1, include_hidden, "boolean", "nil") + local tItems = {} -- Add programs from the path @@ -275,7 +361,7 @@ function shell.programs(_bIncludeHidden) for n = 1, #tList do local sFile = tList[n] if not fs.isDir(fs.combine(sPath, sFile)) and - (_bIncludeHidden or string.sub(sFile, 1, 1) ~= ".") then + (include_hidden or string.sub(sFile, 1, 1) ~= ".") then if #sFile > 4 and sFile:sub(-4) == ".lua" then sFile = sFile:sub(1, -5) end @@ -351,6 +437,21 @@ local function completeProgramArgument(sProgram, nArgument, sPart, tPreviousPart return nil end +--- Complete a shell command line. +-- +-- This accepts an incomplete command, and completes the program name or +-- arguments. For instance, `l` will be completed to `ls`, and `ls ro` will be +-- completed to `ls rom/`. +-- +-- Completion handlers for your program may be registered with +-- @{shell.setCompletionFunction}. +-- +-- @tparam string sLine The input to complete. +-- @treturn { string }|nil The list of possible completions. +-- @see read For more information about completion. +-- @see shell.completeProgram +-- @see shell.setCompletionFunction +-- @see shell.getCompletionInfo function shell.complete(sLine) expect(1, sLine, "string") if #sLine > 0 then @@ -388,23 +489,66 @@ function shell.complete(sLine) return nil end -function shell.completeProgram(sProgram) - expect(1, sProgram, "string") - return completeProgram(sProgram) +--- Complete the name of a program. +-- +-- @tparam string program The name of a program to complete. +-- @treturn { string } A list of possible completions. +-- @see cc.shell.completion.program +function shell.completeProgram(program) + expect(1, program, "string") + return completeProgram(program) end -function shell.setCompletionFunction(sProgram, fnComplete) - expect(1, sProgram, "string") - expect(2, fnComplete, "function") - tCompletionInfo[sProgram] = { - fnComplete = fnComplete, +--- Set the completion function for a program. When the program is entered on +-- the command line, this program will be called to provide auto-complete +-- information. +-- +-- The completion function accepts four arguments: +-- +-- 1. The current shell. As completion functions are inherited, this is not +-- guaranteed to be the shell you registered this function in. +-- 2. The index of the argument currently being completed. +-- 3. The current argument. This may be the empty string. +-- 4. A list of the previous arguments. +-- +-- For instance, when completing `pastebin put rom/st` our pastebin completion +-- function will receive the shell API, an index of 2, `rom/st` as the current +-- argument, and a "previous" table of `{ "put" }`. This function may then wish +-- to return a table containing `artup.lua`, indicating the entire command +-- should be completed to `pastebin put rom/startup.lua`. +-- +-- You completion entries may also be followed by a space, if you wish to +-- indicate another argument is expected. +-- +-- @tparam string program The path to the program. This should be an absolute path +-- _without_ the leading `/`. +-- @tparam function(shell: table, index: number, argument: string, previous: { string }):({ string }|nil) complete +-- The completion function. +-- @see cc.shell.completion Various utilities to help with writing completion functions. +-- @see shell.complete +-- @see read For more information about completion. +function shell.setCompletionFunction(program, complete) + expect(1, program, "string") + expect(2, complete, "function") + tCompletionInfo[program] = { + fnComplete = complete, } end +--- Get a table containing all completion functions. +-- +-- This should only be needed when building custom shells. Use +-- @{setCompletionFunction} to add a completion function. +-- +-- @treturn { [string] = { fnComplete = function } } A table mapping the +-- absolute path of programs, to their completion functions. function shell.getCompletionInfo() return tCompletionInfo end +--- Returns the path to the currently running program. +-- +-- @treturn string The absolute path to the running program. function shell.getRunningProgram() if #tProgramStack > 0 then return tProgramStack[#tProgramStack] @@ -412,17 +556,38 @@ function shell.getRunningProgram() return nil end -function shell.setAlias(_sCommand, _sProgram) - expect(1, _sCommand, "string") - expect(2, _sProgram, "string") - tAliases[_sCommand] = _sProgram +--- Add an alias for a program. +-- +-- @tparam string command The name of the alias to add. +-- @tparam string program The name or path to the program. +-- @usage Alias `vim` to the `edit` program +-- +-- shell.setAlias("vim", "edit") +function shell.setAlias(command, program) + expect(1, command, "string") + expect(2, program, "string") + tAliases[command] = program end -function shell.clearAlias(_sCommand) - expect(1, _sCommand, "string") - tAliases[_sCommand] = nil +--- Remove an alias. +-- +-- @tparam string command The alias name to remove. +function shell.clearAlias(command) + expect(1, command, "string") + tAliases[command] = nil end +--- Get the current aliases for this shell. +-- +-- Aliases are used to allow multiple commands to refer to a single program. For +-- instance, the `list` program is aliased `dir` or `ls`. Running `ls`, `dir` or +-- `list` in the shell will all run the `list` program. +-- +-- @treturn { [string] = string } A table, where the keys are the names of +-- aliases, and the values are the path to the program. +-- @see shell.setAlias +-- @see shell.resolveProgram This uses aliases when resolving a program name to +-- an absolute path. function shell.aliases() -- Copy aliases local tCopy = {} @@ -433,6 +598,20 @@ function shell.aliases() end if multishell then + --- Open a new @{multishell} tab running a command. + -- + -- This behaves similarly to @{shell.run}, but instead returns the process + -- index. + -- + -- This function is only available if the @{multishell} API is. + -- + -- @tparam string ... The command line to run. + -- @see shell.run + -- @see multishell.launch + -- @usage Launch the Lua interpreter and switch to it. + -- + -- local id = shell.openTab("lua") + -- shell.switchTab(id) function shell.openTab(...) local tWords = tokenise(...) local sCommand = tWords[1] @@ -448,9 +627,13 @@ if multishell then end end - function shell.switchTab(nID) - expect(1, nID, "number") - multishell.setFocus(nID) + --- Switch to the @{multishell} tab with the given index. + -- + -- @tparam number id The tab to switch to. + -- @see multishell.setFocus + function shell.switchTab(id) + expect(1, id, "number") + multishell.setFocus(id) end end