From 46d78af068f5f4de1894e49f1e4d6fc83a88ff3d Mon Sep 17 00:00:00 2001 From: SquidDev Date: Sun, 4 Aug 2019 11:05:14 +0100 Subject: [PATCH 01/11] Fix changelog being out-of-sync --- .../resources/assets/computercraft/lua/rom/help/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/computercraft/lua/rom/help/changelog.txt b/src/main/resources/assets/computercraft/lua/rom/help/changelog.txt index c9fb3ad1b..90cd8c0a9 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/changelog.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/changelog.txt @@ -1,4 +1,4 @@ -New features in CC: Tweaked 1.84.0 +# New features in CC: Tweaked 1.84.0 * Improve validation in rename, copy and delete programs * Add window.getLine - the inverse of blit From 1c46949da70a99d9515f7d6a252f3539818490e9 Mon Sep 17 00:00:00 2001 From: Lignum Date: Thu, 15 Aug 2019 07:37:02 +0200 Subject: [PATCH 02/11] Available --- .../resources/assets/computercraft/lua/rom/programs/help.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/help.lua b/src/main/resources/assets/computercraft/lua/rom/programs/help.lua index 5dc7f09d9..2925cf72d 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/help.lua +++ b/src/main/resources/assets/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 From a802f25dd6659826a3c2f82e7c5e36cd25c53651 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Mon, 19 Aug 2019 10:33:53 +0100 Subject: [PATCH 03/11] Do not listen to block/entity drop events It appears several mods inject their own drops on the LOWEST priority, meaning that we capture the existing drops, and the other mod will clear the (now empty) drop list and add its own, resulting in dupe bugs. While I'd argue it's somewhat dubious doing this on the LOWEST priority, it's not a battle I'm prepared to fight. For now, we just remove the block/entity drop handlers, and handle all drop logic when entities are spawned. Fixes #288 --- .../shared/util/DropConsumer.java | 35 +------------------ 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/src/main/java/dan200/computercraft/shared/util/DropConsumer.java b/src/main/java/dan200/computercraft/shared/util/DropConsumer.java index 94a053aa9..4e8a973c2 100644 --- a/src/main/java/dan200/computercraft/shared/util/DropConsumer.java +++ b/src/main/java/dan200/computercraft/shared/util/DropConsumer.java @@ -14,8 +14,6 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.event.entity.EntityJoinWorldEvent; -import net.minecraftforge.event.entity.living.LivingDropsEvent; -import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; @@ -35,7 +33,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; @@ -45,7 +42,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 = true; @@ -54,10 +50,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 +78,6 @@ public static List clear() remainingDrops = null; dropEntity = null; dropWorld = null; - dropPos = null; dropBounds = null; return remainingStacks; @@ -95,33 +89,6 @@ 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() ) - { - List drops = event.getDrops(); - for( EntityItem 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().rand.nextFloat() < event.getDropChance() ) handleDrops( item ); - } - event.getDrops().clear(); - } - } - @SubscribeEvent( priority = EventPriority.LOWEST ) public static void onEntitySpawn( EntityJoinWorldEvent event ) { From 49c37857d40aba66a1e3171082220c98dcdb717d Mon Sep 17 00:00:00 2001 From: SquidDev Date: Fri, 13 Sep 2019 20:55:20 +0100 Subject: [PATCH 04/11] Window.reposition now allow changing the redirect buffer See #270 --- .../computercraft/lua/rom/apis/window.lua | 6 +++++- .../test-rom/spec/apis/window_spec.lua | 20 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/window.lua b/src/main/resources/assets/computercraft/lua/rom/apis/window.lua index b1e1b3db4..87ede5cbd 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/window.lua +++ b/src/main/resources/assets/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/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) From cbc0c1d0b66c4f606b3ef6b32cdd726f1dd5df1b Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 14 Sep 2019 09:16:13 +0100 Subject: [PATCH 05/11] A little experiment with GitHub actions Let's give this a go. --- .github/workflows/main-ci.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/main-ci.yml diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml new file mode 100644 index 000000000..864c86aba --- /dev/null +++ b/.github/workflows/main-ci.yml @@ -0,0 +1,19 @@ +name: Java CI + +on: [push] + +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 From 9bd8c86a948c1927cc84f9afcc2d31a496fc8db2 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Sun, 15 Sep 2019 16:36:47 +0100 Subject: [PATCH 06/11] Change event priority to HIGHEST See the comments in a802f25dd6659826a3c2f82e7c5e36cd25c53651 - effectively we want to make sure we arrive before any other thing which may capture items (some magnet mods, etc...). --- .../java/dan200/computercraft/shared/util/DropConsumer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/dan200/computercraft/shared/util/DropConsumer.java b/src/main/java/dan200/computercraft/shared/util/DropConsumer.java index 4e8a973c2..ec11947aa 100644 --- a/src/main/java/dan200/computercraft/shared/util/DropConsumer.java +++ b/src/main/java/dan200/computercraft/shared/util/DropConsumer.java @@ -89,7 +89,7 @@ private static void handleDrops( ItemStack stack ) if( !remaining.isEmpty() ) remainingDrops.add( remaining ); } - @SubscribeEvent( priority = EventPriority.LOWEST ) + @SubscribeEvent( priority = EventPriority.HIGHEST ) public static void onEntitySpawn( EntityJoinWorldEvent event ) { // Capture any nearby item spawns From 8e4d311cd934e93d7a09ba0c2133d76880d1a1f6 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 15 Sep 2019 18:48:40 +0100 Subject: [PATCH 07/11] Refactor shell completion into a separate module (#281) - Adds cc.completions module, with a couple of helper functions for working with the more general completion functionality (i.e. that provided by read). - Adds cc.shell.completions module, which provides shell-specific completion functions. - Add a "program completion builder", which allows you to write stuff like this: shell.setCompletionFunction( "rom/programs/redstone.lua", completion.build( { completion.choice, { "probe", "set ", "pulse " } }, completion.side) ) Closes #232 --- .../lua/rom/modules/main/cc/completion.lua | 105 +++++++ .../rom/modules/main/cc/shell/completion.lua | 151 ++++++++++ .../assets/computercraft/lua/rom/startup.lua | 274 +++++------------- .../spec/modules/cc/completion_spec.lua | 57 ++++ .../spec/modules/cc/shell/completion_spec.lua | 41 +++ .../test-rom/spec/programs/mkdir_spec.lua | 4 +- 6 files changed, 428 insertions(+), 204 deletions(-) create mode 100644 src/main/resources/assets/computercraft/lua/rom/modules/main/cc/completion.lua create mode 100644 src/main/resources/assets/computercraft/lua/rom/modules/main/cc/shell/completion.lua create mode 100644 src/test/resources/test-rom/spec/modules/cc/completion_spec.lua create mode 100644 src/test/resources/test-rom/spec/modules/cc/shell/completion_spec.lua 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/assets/computercraft/lua/rom/startup.lua b/src/main/resources/assets/computercraft/lua/rom/startup.lua index 2bc19adca..e6b396b39 100644 --- a/src/main/resources/assets/computercraft/lua/rom/startup.lua +++ b/src/main/resources/assets/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/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) From 79cd8b4da532c094098525481a675f967e9e11cb Mon Sep 17 00:00:00 2001 From: SquidDev Date: Sun, 15 Sep 2019 18:37:00 +0100 Subject: [PATCH 08/11] Also return the number of affected entities Closes #293. Doesn't really solve anything there aside from exposing the number, but sadly there's not really anything obvious I can do on my end - the command API just doesn't expose anything else. --- .../dan200/computercraft/shared/computer/apis/CommandAPI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 08e4c4afa..a3365d225 100644 --- a/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java +++ b/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java @@ -83,7 +83,7 @@ private Object[] doCommand( String command ) { sender.clearOutput(); int result = commandManager.executeCommand( sender, command ); - return new Object[] { result > 0, sender.copyOutput() }; + return new Object[] { result > 0, sender.copyOutput(), result }; } catch( Throwable t ) { From 3023f235a4e775d38020dc5b411e1ae311b78963 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Fri, 27 Sep 2019 08:56:53 +0100 Subject: [PATCH 09/11] Goodbye Travis, I'm with GH Actions now --- .github/workflows/main-ci.yml | 2 +- .travis.yml | 14 -------------- README.md | 2 +- 3 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index 864c86aba..54afe58e3 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -1,4 +1,4 @@ -name: Java CI +name: Build on: [push] 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 d7996659e..692e7bb48 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. From db31a53bba4b1da9aee203a3346f731d054f8cfc Mon Sep 17 00:00:00 2001 From: Wendelstein7 <23213464+Wendelstein7@users.noreply.github.com> Date: Mon, 30 Sep 2019 15:15:22 +0200 Subject: [PATCH 10/11] Fixed turtle property category Likely was a leftover from copy pasting and a tired programmer. --- src/main/java/dan200/computercraft/shared/Config.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/dan200/computercraft/shared/Config.java b/src/main/java/dan200/computercraft/shared/Config.java index 76bbe6f43..ffb176c72 100644 --- a/src/main/java/dan200/computercraft/shared/Config.java +++ b/src/main/java/dan200/computercraft/shared/Config.java @@ -277,7 +277,7 @@ public static void load( File configFile ) renameProperty( CATEGORY_GENERAL, "turtlesCanPush", CATEGORY_TURTLE, "can_push" ); renameProperty( CATEGORY_GENERAL, "turtle_disabled_actions", CATEGORY_TURTLE, "disabled_actions" ); - config.getCategory( CATEGORY_HTTP ) + config.getCategory( CATEGORY_TURTLE ) .setComment( "Various options relating to turtles." ); turtlesNeedFuel = config.get( CATEGORY_TURTLE, "need_fuel", ComputerCraft.turtlesNeedFuel ); From 7250f22ff64e4667746c8a4cc38879cd29816418 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Mon, 30 Sep 2019 15:37:52 +0100 Subject: [PATCH 11/11] Update CI to also run on PRs --- .github/workflows/main-ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index 54afe58e3..16f34b3e2 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -1,10 +1,9 @@ name: Build -on: [push] +on: [push, pull_request] jobs: build: - runs-on: ubuntu-latest steps: