diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml new file mode 100644 index 000000000..16f34b3e2 --- /dev/null +++ b/.github/workflows/main-ci.yml @@ -0,0 +1,18 @@ +name: Build + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + - name: Build with Gradle + run: ./gradlew build --no-daemon diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0e4be822e..000000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: java - -script: ./gradlew build --no-daemon - -before_cache: - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/s - -jdk: - - openjdk8 diff --git a/README.md b/README.md index f353fe778..d75213c02 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # ![CC: Tweaked](logo.png) -[![Current build status](https://travis-ci.org/SquidDev-CC/CC-Tweaked.svg?branch=master)](https://travis-ci.org/SquidDev-CC/CC-Tweaked "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") +[![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, turtles and more to Minecraft. diff --git a/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java b/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java index de52751fc..fc31c6fce 100644 --- a/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java +++ b/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java @@ -85,7 +85,7 @@ private Object[] doCommand( String command ) { receiver.clearOutput(); int result = commandManager.handleCommand( m_computer.getSource(), command ); - return new Object[] { result > 0, receiver.copyOutput() }; + return new Object[] { result > 0, receiver.copyOutput(), result }; } catch( Throwable t ) { diff --git a/src/main/java/dan200/computercraft/shared/util/DropConsumer.java b/src/main/java/dan200/computercraft/shared/util/DropConsumer.java index ca28204bf..411b58c59 100644 --- a/src/main/java/dan200/computercraft/shared/util/DropConsumer.java +++ b/src/main/java/dan200/computercraft/shared/util/DropConsumer.java @@ -36,7 +36,6 @@ private DropConsumer() private static Function dropConsumer; private static List remainingDrops; private static WeakReference dropWorld; - private static BlockPos dropPos; private static AxisAlignedBB dropBounds; private static WeakReference dropEntity; @@ -46,7 +45,6 @@ public static void set( Entity entity, Function consumer ) remainingDrops = new ArrayList<>(); dropEntity = new WeakReference<>( entity ); dropWorld = new WeakReference<>( entity.world ); - dropPos = null; dropBounds = new AxisAlignedBB( entity.getPosition() ).grow( 2, 2, 2 ); entity.captureDrops( new ArrayList<>() ); @@ -55,10 +53,9 @@ public static void set( Entity entity, Function consumer ) public static void set( World world, BlockPos pos, Function consumer ) { dropConsumer = consumer; - remainingDrops = new ArrayList<>(); + remainingDrops = new ArrayList<>( 2 ); dropEntity = null; dropWorld = new WeakReference<>( world ); - dropPos = pos; dropBounds = new AxisAlignedBB( pos ).grow( 2, 2, 2 ); } @@ -83,7 +80,6 @@ public static List clear() remainingDrops = null; dropEntity = null; dropWorld = null; - dropPos = null; dropBounds = null; return remainingStacks; @@ -95,34 +91,7 @@ private static void handleDrops( ItemStack stack ) if( !remaining.isEmpty() ) remainingDrops.add( remaining ); } - @SubscribeEvent( priority = EventPriority.LOWEST ) - public static void onEntityLivingDrops( LivingDropsEvent event ) - { - // Capture any mob drops for the current entity - if( dropEntity != null && event.getEntity() == dropEntity.get() ) - { - Collection drops = event.getDrops(); - for( ItemEntity entityItem : drops ) handleDrops( entityItem.getItem() ); - drops.clear(); - } - } - - @SubscribeEvent( priority = EventPriority.LOWEST ) - public static void onHarvestDrops( BlockEvent.HarvestDropsEvent event ) - { - // Capture block drops for the current entity - if( dropWorld != null && dropWorld.get() == event.getWorld() - && dropPos != null && dropPos.equals( event.getPos() ) ) - { - for( ItemStack item : event.getDrops() ) - { - if( event.getWorld().getRandom().nextFloat() < event.getDropChance() ) handleDrops( item ); - } - event.getDrops().clear(); - } - } - - @SubscribeEvent( priority = EventPriority.LOWEST ) + @SubscribeEvent( priority = EventPriority.HIGHEST ) public static void onEntitySpawn( EntityJoinWorldEvent event ) { // Capture any nearby item spawns diff --git a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/completion.lua b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/completion.lua new file mode 100644 index 000000000..4634bcc98 --- /dev/null +++ b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/completion.lua @@ -0,0 +1,105 @@ +--- A collection of helper methods for working with input completion, such +-- as that require by @{read}. +-- +-- @module craftos.completion +-- @see cc.shell.completion For additional helpers to use with +-- @{shell.setCompletionFunction}. + +local expect = require "cc.expect".expect + +local function choice_impl(text, choices, add_space) + local results = {} + for n = 1, #choices do + local option = choices[n] + if #option + (add_space and 1 or 0) > #text and option:sub(1, #text) == text then + local result = option:sub(#text + 1) + if add_space then + table.insert(results, result .. " ") + else + table.insert(results, result) + end + end + end + return results +end + +--- Complete from a choice of one or more strings. +-- +-- @tparam string text The input string to complete. +-- @tparam { string... } choices The list of choices to complete from. +-- @tparam[opt] boolean add_space Whether to add a space after the completed item. +-- @treturn { string... } A list of suffixes of matching strings. +-- @usage Call @{read}, completing the names of various animals. +-- +-- local animals = { "dog", "cat", "lion", "unicorn" } +-- read(nil, nil, function(text) return choice(text, animals) end) +local function choice(text, choices, add_space) + expect(1, text, "string") + expect(2, choices, "table") + expect(3, add_space, "boolean", "nil") + return choice_impl(text, choices, add_space) +end + +--- Complete the name of a currently attached peripheral. +-- +-- @tparam string text The input string to complete. +-- @tparam[opt] boolean add_space Whether to add a space after the completed name. +-- @treturn { string... } A list of suffixes of matching peripherals. +-- @usage read(nil, nil, peripheral) +local function peripheral_(text, add_space) + expect(1, text, "string") + expect(2, add_space, "boolean", "nil") + return choice_impl(text, peripheral.getNames(), add_space) +end + +local sides = redstone.getSides() + +--- Complete the side of a computer. +-- +-- @tparam string text The input string to complete. +-- @tparam[opt] boolean add_space Whether to add a space after the completed side. +-- @treturn { string... } A list of suffixes of matching sides. +-- @usage read(nil, nil, side) +local function side(text, add_space) + expect(1, text, "string") + expect(2, add_space, "boolean", "nil") + return choice_impl(text, sides, add_space) +end + +--- Complete a @{settings|setting}. +-- +-- @tparam string text The input string to complete. +-- @tparam[opt] boolean add_space Whether to add a space after the completed settings. +-- @treturn { string... } A list of suffixes of matching settings. +-- @usage read(nil, nil, setting) +local function setting(text, add_space) + expect(1, text, "string") + expect(2, add_space, "boolean", "nil") + return choice_impl(text, settings.getNames(), add_space) +end + +local command_list + +--- Complete the name of a Minecraft @{commands|command}. +-- +-- @tparam string text The input string to complete. +-- @tparam[opt] boolean add_space Whether to add a space after the completed command. +-- @treturn { string... } A list of suffixes of matching commands. +-- @usage read(nil, nil, command) +local function command(text, add_space) + expect(1, text, "string") + expect(2, add_space, "boolean", "nil") + if command_list == nil then + command_list = commands and commands.list() or {} + end + + return choice_impl(text, command_list, add_space) +end + +return { + choice = choice, + peripheral = peripheral_, + side = side, + setting = setting, + command = command, +} diff --git a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/shell/completion.lua b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/shell/completion.lua new file mode 100644 index 000000000..023e500fc --- /dev/null +++ b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/shell/completion.lua @@ -0,0 +1,151 @@ +--- A collection of helper methods for working with shell completion. +-- +-- Most programs may be completed using the @{build} helper method, rather than +-- manually switching on the argument index. +-- +-- Note, the helper functions within this module do not accept an argument index, +-- and so are not directly usable with the @{shell.setCompletionFunction}. Instead, +-- wrap them using @{build}, or your own custom function. +-- +-- @module craftos.shell.completion +-- @see cc.completion For more general helpers, suitable for use with @{read}. +-- @see shell.setCompletionFunction + +local expect = require "cc.expect".expect +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 { string... } choices The list of choices to complete from. +-- @treturn { string... } A list of suffixes of matching files. +local function file(shell, text) + return fs.complete(text, shell.dir(), true, false) +end + +--- Complete the name of a directory relative to the current working directory. +-- +-- @tparam shell 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) + return fs.complete(text, shell.dir(), false, true) +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 { 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. +-- @treturn { string... } A list of suffixes of matching files and directories. +local function dirOrFile(shell, text, previous, add_space) + local results = fs.complete(text, shell.dir(), true, true) + if add_space then + for n = 1, #results do + local result = results[n] + if result:sub(-1) ~= "/" then + results[n] = result .. " " + end + end + end + return results +end + +local function wrap(func) + return function(shell, text, previous, ...) + return func(text, ...) + end +end + +--- Complete the name of a program. +-- +-- @tparam shell 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. +local function program(shell, text) + return shell.completeProgram(text) +end + +--- A helper function for building shell completion arguments. +-- +-- This accepts a series of single-argument completion functions, and combines +-- them into a function suitable for use with @{shell.setCompletionFunction}. +-- +-- @tparam nil|table|function ... Every argument to @{build} represents an argument +-- to the program you wish to complete. Each argument can be one of three types: +-- +-- - `nil`: This argument will not be completed. +-- +-- - A function: This argument will be completed with the given function. It is +-- called with the @{shell} object, the string to complete and the arguments +-- before this one. +-- +-- - A table: This acts as a more powerful version of the function case. The table +-- must have a function as the first item - this will be called with the shell, +-- string and preceding arguments as above, but also followed by any additional +-- items in the table. This provides a more convenient interface to pass +-- options to your completion functions. +-- +-- If this table is the last argument, it may also set the `many` key to true, +-- which states this function should be used to complete any remaining arguments. +-- +-- @usage Prompt for a choice of options, followed by a directory, and then multiple +-- files. +-- +-- complete.build( +-- { complete.choice, { "get", "put" } }, +-- complete.dir, +-- } complete.file, many = true } +-- ) +local function build(...) + local arguments = table.pack(...) + for i = 1, arguments.n do + local arg = arguments[i] + if arg ~= nil then + expect(i, arg, "table", "function") + if type(arg) == "function" then + arg = { arg } + arguments[i] = arg + end + + if type(arg[1]) ~= "function" then + error(("Bad table entry #1 at argument #%d (expected function, got %s)"):format(i, type(arg[1])), 2) + end + + if arg.many and i < arguments.n then + error(("Unexpected 'many' field on argument #%d (should only occur on the last argument)"):format(i), 2) + end + end + end + + return function(shell, index, text, previous) + local arg = arguments[index] + if not arg then + if index <= arguments.n then return end + + arg = arguments[arguments.n] + if not arg or not arg.many then return end + end + + return arg[1](shell, text, previous, table.unpack(arg, 2)) + end +end + +return { + file = file, + dir = dir, + dirOrFile = dirOrFile, + program = program, + + -- Re-export various other functions + help = wrap(help.completeTopic), + choice = wrap(completion.choice), + peripheral = wrap(completion.peripheral), + side = wrap(completion.side), + setting = wrap(completion.setting), + command = wrap(completion.command), + + build = build, +} diff --git a/src/main/resources/data/computercraft/lua/rom/apis/window.lua b/src/main/resources/data/computercraft/lua/rom/apis/window.lua index b1e1b3db4..87ede5cbd 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/window.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/window.lua @@ -431,16 +431,20 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) return nX, nY end - function window.reposition( nNewX, nNewY, nNewWidth, nNewHeight ) + function window.reposition( nNewX, nNewY, nNewWidth, nNewHeight, newParent ) if type(nNewX) ~= "number" then expect(1, nNewX, "number") end if type(nNewY) ~= "number" then expect(2, nNewY, "number") end if nNewWidth ~= nil or nNewHeight ~= nil then expect(3, nNewWidth, "number") expect(4, nNewHeight, "number") end + if newParent ~= nil and type(newParent) ~= "table" then expect(5, newParent, "table") end nX = nNewX nY = nNewY + + if newParent then parent = newParent end + if nNewWidth and nNewHeight then local tNewLines = {} createEmptyLines( nNewWidth ) diff --git a/src/main/resources/data/computercraft/lua/rom/programs/help.lua b/src/main/resources/data/computercraft/lua/rom/programs/help.lua index 5dc7f09d9..2925cf72d 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/help.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/help.lua @@ -7,7 +7,7 @@ else end if sTopic == "index" then - print( "Help topics availiable:" ) + print( "Help topics available:" ) local tTopics = help.topics() textutils.pagedTabulate( tTopics ) return diff --git a/src/main/resources/data/computercraft/lua/rom/startup.lua b/src/main/resources/data/computercraft/lua/rom/startup.lua index 2bc19adca..e6b396b39 100644 --- a/src/main/resources/data/computercraft/lua/rom/startup.lua +++ b/src/main/resources/data/computercraft/lua/rom/startup.lua @@ -1,3 +1,4 @@ +local completion = require "cc.shell.completion" -- Setup paths local sPath = ".:/rom/programs" @@ -39,217 +40,86 @@ if term.isColor() then end -- Setup completion functions -local function completeMultipleChoice( sText, tOptions, bAddSpaces ) - local tResults = {} - for n=1,#tOptions do - local sOption = tOptions[n] - if #sOption + (bAddSpaces and 1 or 0) > #sText and string.sub( sOption, 1, #sText ) == sText then - local sResult = string.sub( sOption, #sText + 1 ) - if bAddSpaces then - table.insert( tResults, sResult .. " " ) - else - table.insert( tResults, sResult ) - end - end - end - return tResults -end -local function completePeripheralName( sText, bAddSpaces ) - return completeMultipleChoice( sText, peripheral.getNames(), bAddSpaces ) -end -local tRedstoneSides = redstone.getSides() -local function completeSide( sText, bAddSpaces ) - return completeMultipleChoice( sText, tRedstoneSides, bAddSpaces ) -end -local function completeFile( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return fs.complete( sText, shell.dir(), true, false ) + +local function completePastebinPut(shell, text, previous) + if previous[2] == "put" then + return fs.complete(text, shell.dir(), true, false ) end end -local function completeFileMany( shell, nIndex, sText, tPreviousText ) - return fs.complete( sText, shell.dir(), true, false ) -end -local function completeDir( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return fs.complete( sText, shell.dir(), false, true ) - end -end -local function completeEither( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return fs.complete( sText, shell.dir(), true, true ) - end -end -local function completeEitherMany( shell, nIndex, sText, tPreviousText ) - return fs.complete( sText, shell.dir(), true, true ) -end -local function completeEitherEither( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - local tResults = fs.complete( sText, shell.dir(), true, true ) - for n=1,#tResults do - local sResult = tResults[n] - if string.sub( sResult, #sResult, #sResult ) ~= "/" then - tResults[n] = sResult .. " " - end - end - return tResults - elseif nIndex == 2 then - return fs.complete( sText, shell.dir(), true, true ) - end -end -local function completeProgram( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return shell.completeProgram( sText ) - end -end -local function completeHelp( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return help.completeTopic( sText ) - end -end -local function completeAlias( shell, nIndex, sText, tPreviousText ) - if nIndex == 2 then - return shell.completeProgram( sText ) - end -end -local function completePeripheral( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return completePeripheralName( sText ) - end -end -local tGPSOptions = { "host", "host ", "locate" } -local function completeGPS( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return completeMultipleChoice( sText, tGPSOptions ) - end -end -local tLabelOptions = { "get", "get ", "set ", "clear", "clear " } -local function completeLabel( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return completeMultipleChoice( sText, tLabelOptions ) - elseif nIndex == 2 then - return completePeripheralName( sText ) - end -end -local function completeMonitor( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return completePeripheralName( sText, true ) - elseif nIndex == 2 then - return shell.completeProgram( sText ) - end -end -local tRedstoneOptions = { "probe", "set ", "pulse " } -local function completeRedstone( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return completeMultipleChoice( sText, tRedstoneOptions ) - elseif nIndex == 2 then - return completeSide( sText ) - end -end -local tDJOptions = { "play", "play ", "stop " } -local function completeDJ( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return completeMultipleChoice( sText, tDJOptions ) - elseif nIndex == 2 then - return completePeripheralName( sText ) - end -end -local tPastebinOptions = { "put ", "get ", "run " } -local function completePastebin( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return completeMultipleChoice( sText, tPastebinOptions ) - elseif nIndex == 2 then - if tPreviousText[2] == "put" then - return fs.complete( sText, shell.dir(), true, false ) - end - end -end -local tChatOptions = { "host ", "join " } -local function completeChat( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return completeMultipleChoice( sText, tChatOptions ) - end -end -local function completeSet( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return completeMultipleChoice( sText, settings.getNames(), true ) - end -end -local tCommands -if commands then - tCommands = commands.list() -end -local function completeExec( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 and commands then - return completeMultipleChoice( sText, tCommands, true ) - end -end -local tWgetOptions = { "run" } -local function completeWget( shell, nIndex, sText, tPreviousText ) - if nIndex == 1 then - return completeMultipleChoice( sText, tWgetOptions, true ) - end -end -shell.setCompletionFunction( "rom/programs/alias.lua", completeAlias ) -shell.setCompletionFunction( "rom/programs/cd.lua", completeDir ) -shell.setCompletionFunction( "rom/programs/copy.lua", completeEitherEither ) -shell.setCompletionFunction( "rom/programs/delete.lua", completeEitherMany ) -shell.setCompletionFunction( "rom/programs/drive.lua", completeDir ) -shell.setCompletionFunction( "rom/programs/edit.lua", completeFile ) -shell.setCompletionFunction( "rom/programs/eject.lua", completePeripheral ) -shell.setCompletionFunction( "rom/programs/gps.lua", completeGPS ) -shell.setCompletionFunction( "rom/programs/help.lua", completeHelp ) -shell.setCompletionFunction( "rom/programs/id.lua", completePeripheral ) -shell.setCompletionFunction( "rom/programs/label.lua", completeLabel ) -shell.setCompletionFunction( "rom/programs/list.lua", completeDir ) -shell.setCompletionFunction( "rom/programs/mkdir.lua", completeFileMany ) -shell.setCompletionFunction( "rom/programs/monitor.lua", completeMonitor ) -shell.setCompletionFunction( "rom/programs/move.lua", completeEitherEither ) -shell.setCompletionFunction( "rom/programs/redstone.lua", completeRedstone ) -shell.setCompletionFunction( "rom/programs/rename.lua", completeEitherEither ) -shell.setCompletionFunction( "rom/programs/shell.lua", completeProgram ) -shell.setCompletionFunction( "rom/programs/type.lua", completeEither ) -shell.setCompletionFunction( "rom/programs/set.lua", completeSet ) -shell.setCompletionFunction( "rom/programs/advanced/bg.lua", completeProgram ) -shell.setCompletionFunction( "rom/programs/advanced/fg.lua", completeProgram ) -shell.setCompletionFunction( "rom/programs/fun/dj.lua", completeDJ ) -shell.setCompletionFunction( "rom/programs/fun/advanced/paint.lua", completeFile ) -shell.setCompletionFunction( "rom/programs/http/pastebin.lua", completePastebin ) -shell.setCompletionFunction( "rom/programs/rednet/chat.lua", completeChat ) -shell.setCompletionFunction( "rom/programs/command/exec.lua", completeExec ) -shell.setCompletionFunction( "rom/programs/http/wget.lua", completeWget ) + +shell.setCompletionFunction( "rom/programs/alias.lua", completion.build(nil, completion.program) ) +shell.setCompletionFunction( "rom/programs/cd.lua", completion.build(completion.dir) ) +shell.setCompletionFunction( "rom/programs/copy.lua", completion.build( + { completion.dirOrFile, true }, + completion.dirOrFile +) ) +shell.setCompletionFunction( "rom/programs/delete.lua", completion.build({ completion.dirOrFile, many = true }) ) +shell.setCompletionFunction( "rom/programs/drive.lua", completion.build(completion.dir) ) +shell.setCompletionFunction( "rom/programs/edit.lua", completion.build(completion.file) ) +shell.setCompletionFunction( "rom/programs/eject.lua", completion.build(completion.peripheral) ) +shell.setCompletionFunction( "rom/programs/gps.lua", completion.build({ completion.choice, { "host", "host ", "locate" } }) ) +shell.setCompletionFunction( "rom/programs/help.lua", completion.build(completion.help) ) +shell.setCompletionFunction( "rom/programs/id.lua", completion.build(completion.peripheral) ) +shell.setCompletionFunction( "rom/programs/label.lua", completion.build( + { completion.choice, { "get", "get ", "set ", "clear", "clear " } }, + completion.peripheral +) ) +shell.setCompletionFunction( "rom/programs/list.lua", completion.build(completion.dir) ) +shell.setCompletionFunction( "rom/programs/mkdir.lua", completion.build({ completion.dir, many = true }) ) +shell.setCompletionFunction( "rom/programs/monitor.lua", completion.build( + { completion.peripheral, true }, + completion.program +) ) +shell.setCompletionFunction( "rom/programs/move.lua", completion.build( + { completion.dirOrFile, true }, + completion.dirOrFile +) ) +shell.setCompletionFunction( "rom/programs/redstone.lua", completion.build( + { completion.choice, { "probe", "set ", "pulse " } }, + completion.side +) ) +shell.setCompletionFunction( "rom/programs/rename.lua", completion.build( + { completion.dirOrFile, true }, + completion.dirOrFile +) ) +shell.setCompletionFunction( "rom/programs/shell.lua", completion.build(completion.program) ) +shell.setCompletionFunction( "rom/programs/type.lua", completion.build(completion.dirOrFile) ) +shell.setCompletionFunction( "rom/programs/set.lua", completion.build({ completion.setting, true }) ) +shell.setCompletionFunction( "rom/programs/advanced/bg.lua", completion.build(completion.program) ) +shell.setCompletionFunction( "rom/programs/advanced/fg.lua", completion.build(completion.program) ) +shell.setCompletionFunction( "rom/programs/fun/dj.lua", completion.build( + { completion.choice, { "play", "play ", "stop " } }, + completion.peripheral +) ) +shell.setCompletionFunction( "rom/programs/fun/advanced/paint.lua", completion.build(completion.file) ) +shell.setCompletionFunction( "rom/programs/http/pastebin.lua", completion.build( + { completion.choice, { "put ", "get ", "run " } }, + completePastebinPut +) ) +shell.setCompletionFunction( "rom/programs/rednet/chat.lua", completion.build({ completion.choice, { "host ", "join " } }) ) +shell.setCompletionFunction( "rom/programs/command/exec.lua", completion.build(completion.command) ) +shell.setCompletionFunction( "rom/programs/http/wget.lua", completion.build({ completion.choice, { "run " } }) ) if turtle then - local tGoOptions = { "left", "right", "forward", "back", "down", "up" } - local function completeGo( shell, nIndex, sText ) - return completeMultipleChoice( sText, tGoOptions, true) - end - local tTurnOptions = { "left", "right" } - local function completeTurn( shell, nIndex, sText ) - return completeMultipleChoice( sText, tTurnOptions, true ) - end - local tEquipOptions = { "left", "right" } - local function completeEquip( shell, nIndex, sText ) - if nIndex == 2 then - return completeMultipleChoice( sText, tEquipOptions ) - end - end - local function completeUnequip( shell, nIndex, sText ) - if nIndex == 1 then - return completeMultipleChoice( sText, tEquipOptions ) - end - end - shell.setCompletionFunction( "rom/programs/turtle/go.lua", completeGo ) - shell.setCompletionFunction( "rom/programs/turtle/turn.lua", completeTurn ) - shell.setCompletionFunction( "rom/programs/turtle/equip.lua", completeEquip ) - shell.setCompletionFunction( "rom/programs/turtle/unequip.lua", completeUnequip ) + shell.setCompletionFunction( "rom/programs/turtle/go.lua", completion.build( + { completion.choice, { "left", "right", "forward", "back", "down", "up" }, true, many = true } + ) ) + shell.setCompletionFunction( "rom/programs/turtle/turn.lua", completion.build( + { completion.choice, { "left", "right" }, true, many = true } + )) + shell.setCompletionFunction( "rom/programs/turtle/equip.lua", completion.build( + nil, + { completion.choice, { "left", "right" } } + ) ) + shell.setCompletionFunction( "rom/programs/turtle/unequip.lua", completion.build( + { completion.choice, { "left", "right" } } + ) ) end - -- Run autorun files if fs.exists( "/rom/autorun" ) and fs.isDir( "/rom/autorun" ) then local tFiles = fs.list( "/rom/autorun" ) - table.sort( tFiles ) - for n, sFile in ipairs( tFiles ) do + for _, sFile in ipairs( tFiles ) do if string.sub( sFile, 1, 1 ) ~= "." then local sPath = "/rom/autorun/"..sFile if not fs.isDir( sPath ) then diff --git a/src/test/resources/test-rom/spec/apis/window_spec.lua b/src/test/resources/test-rom/spec/apis/window_spec.lua index 2fb6d119d..cb2905951 100644 --- a/src/test/resources/test-rom/spec/apis/window_spec.lua +++ b/src/test/resources/test-rom/spec/apis/window_spec.lua @@ -118,6 +118,26 @@ describe("The window library", function() expect.error(w.reposition, 1, 1, false, 1):eq("bad argument #3 (expected number, got boolean)") expect.error(w.reposition, 1, 1, nil, 1):eq("bad argument #3 (expected number, got nil)") expect.error(w.reposition, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)") + expect.error(w.reposition, 1, 1, 1, 1, true):eq("bad argument #5 (expected table, got boolean)") + end) + + it("can change the buffer", function() + local a, b = mk(), mk() + local target = window.create(a, 1, 1, a.getSize()) + + target.write("Test") + expect((a.getLine(1))):equal("Test ") + expect({ a.getCursorPos() }):same { 5, 1 } + + target.reposition(1, 1, nil, nil, b) + + target.redraw() + expect((a.getLine(1))):equal("Test ") + expect({ a.getCursorPos() }):same { 5, 1 } + + target.setCursorPos(1, 1) target.write("More") + expect((a.getLine(1))):equal("Test ") + expect((b.getLine(1))):equal("More ") end) end) diff --git a/src/test/resources/test-rom/spec/modules/cc/completion_spec.lua b/src/test/resources/test-rom/spec/modules/cc/completion_spec.lua new file mode 100644 index 000000000..41e7ae5bc --- /dev/null +++ b/src/test/resources/test-rom/spec/modules/cc/completion_spec.lua @@ -0,0 +1,57 @@ +describe("cc.completion", function() + local c = require("cc.completion") + + describe("choice", function() + it("provides all choices", function() + expect(c.choice("", { "some text", "some other", "other" })) + :same { "some text", "some other", "other" } + end) + + it("provides a filtered list of choices", function() + expect(c.choice("som", { "some text", "some other", "other" })) + :same { "e text", "e other" } + + expect(c.choice("none", { "some text", "some other", "other" })) + :same { } + end) + + it("adds text if needed", function() + expect(c.choice("som", { "some text", "some other", "other" }, true)) + :same { "e text ", "e other " } + end) + end) + + describe("peripheral", function() + it("provides a choice of peripherals", function() + stub(peripheral, "getNames", function() return { "drive_0", "left" } end) + + expect(c.peripheral("dri")):same { "ve_0" } + expect(c.peripheral("dri", true)):same { "ve_0 " } + end) + end) + + describe("side", function() + it("provides a choice of sides", function() + expect(c.side("le")):same { "ft" } + expect(c.side("le", true)):same { "ft " } + end) + end) + + describe("setting", function() + it("provides a choice of setting names", function() + stub(settings, "getNames", function() return { "shell.allow_startup", "list.show_hidden" } end) + + expect(c.setting("li")):same { "st.show_hidden" } + expect(c.setting("li", true)):same { "st.show_hidden " } + end) + end) + + describe("command", function() + it("provides a choice of command names", function() + stub(_G, "commands", { list = function() return { "list", "say" } end }) + + expect(c.command("li")):same { "st" } + expect(c.command("li", true)):same { "st " } + end) + end) +end) diff --git a/src/test/resources/test-rom/spec/modules/cc/shell/completion_spec.lua b/src/test/resources/test-rom/spec/modules/cc/shell/completion_spec.lua new file mode 100644 index 000000000..7f7fd0a40 --- /dev/null +++ b/src/test/resources/test-rom/spec/modules/cc/shell/completion_spec.lua @@ -0,0 +1,41 @@ +describe("cc.shell.completion", function() + local c = require "cc.shell.completion" + + describe("dirOrFile", function() + it("completes both", function() + expect(c.dirOrFile(shell, "rom/")):same { + "apis/", "apis", "autorun/", "autorun", "help/", "help", + "modules/", "modules", "motd.txt", "programs/", "programs", "startup.lua" + } + end) + + it("adds a space", function() + expect(c.dirOrFile(shell, "rom/", nil, true)):same { + "apis/", "apis ", "autorun/", "autorun ", "help/", "help ", + "modules/", "modules ", "motd.txt ", "programs/", "programs ", "startup.lua ", + } + end) + end) + + describe("build", function() + it("completes multiple arguments", function() + local spec = c.build( + function() return { "a", "b", "c" } end, + nil, + { c.choice, { "d", "e", "f"} } + ) + + expect(spec(shell, 1, "")):same { "a", "b", "c" } + expect(spec(shell, 2, "")):same(nil) + expect(spec(shell, 3, "")):same { "d", "e", "f" } + expect(spec(shell, 4, "")):same(nil) + end) + + it("supports variadic completions", function() + local spec = c.build({ function() return { "a", "b", "c" } end, many = true }) + + expect(spec(shell, 1, "")):same({ "a", "b", "c" }) + expect(spec(shell, 2, "")):same({ "a", "b", "c" }) + end) + end) +end) diff --git a/src/test/resources/test-rom/spec/programs/mkdir_spec.lua b/src/test/resources/test-rom/spec/programs/mkdir_spec.lua index 91dcaf437..fb7327519 100644 --- a/src/test/resources/test-rom/spec/programs/mkdir_spec.lua +++ b/src/test/resources/test-rom/spec/programs/mkdir_spec.lua @@ -23,7 +23,7 @@ describe("The mkdir program", function() io.open("/test-files.a.txt", "w"):close() local complete = shell.getCompletionInfo()["rom/programs/mkdir.lua"].fnComplete - expect(complete(shell, 1, "/test-files/", {})):same { "a/", "b/" } - expect(complete(shell, 2, "/test-files/", { "/" })):same { "a/", "b/" } + expect(complete(shell, 1, "/test-files/", {})):same { "a/", "a", "b/", "b" } + expect(complete(shell, 2, "/test-files/", { "/" })):same { "a/", "a", "b/", "b" } end) end)