diff --git a/spec/atom/command-interpreter-spec.coffee b/spec/atom/command-interpreter-spec.coffee index b09816913..55e428540 100644 --- a/spec/atom/command-interpreter-spec.coffee +++ b/spec/atom/command-interpreter-spec.coffee @@ -55,7 +55,7 @@ describe "CommandInterpreter", -> it 'does not require the trailing slash', -> editor.getSelection().setBufferRange([[4,16], [4,20]]) interpreter.eval(editor, '/pivot') - expect(editor.selection.getBufferRange()).toEqual [[6,16], [6,21]] + expect(editor.getSelection().getBufferRange()).toEqual [[6,16], [6,21]] describe "address range", -> describe "when two addresses are specified", -> @@ -78,7 +78,6 @@ describe "CommandInterpreter", -> interpreter.eval(editor, ',') expect(editor.selection.getBufferRange()).toEqual [[0, 0], [12, 2]] - describe "substitution", -> it "does nothing if there are no matches", -> editor.selection.setBufferRange([[6, 0], [6, 44]]) @@ -104,3 +103,25 @@ describe "CommandInterpreter", -> expect(buffer.lineForRow(4)).toBe '!!!!while(items.length!>!0)!{' expect(buffer.lineForRow(5)).toBe '!!!!!!current!=!items.shift();' expect(buffer.lineForRow(6)).toBe ' current < pivot ? left.push(current) : right.push(current);' + + describe ".repeatLastRelativeAddress()", -> + it "repeats the last search command", -> + editor.setCursorScreenPosition([4, 0]) + + interpreter.eval(editor, '/current') + expect(editor.getSelection().getBufferRange()).toEqual [[5,6], [5,13]] + + interpreter.repeatLastRelativeAddress(editor) + expect(editor.getSelection().getBufferRange()).toEqual [[6,6], [6,13]] + + interpreter.eval(editor, 's/r/R/g') + + interpreter.repeatLastRelativeAddress(editor) + expect(editor.getSelection().getBufferRange()).toEqual [[6,34], [6,41]] + + interpreter.eval(editor, '0') + interpreter.eval(editor, '/sort/ s/r/R/') # this contains a substitution... won't be repeated + + interpreter.repeatLastRelativeAddress(editor) + expect(editor.getSelection().getBufferRange()).toEqual [[3,31], [3,38]] + diff --git a/src/atom/command-interpreter.coffee b/src/atom/command-interpreter.coffee index 2429c36ea..765a14317 100644 --- a/src/atom/command-interpreter.coffee +++ b/src/atom/command-interpreter.coffee @@ -6,7 +6,11 @@ class CommandInterpreter constructor: -> @parser = PEG.buildParser(fs.read(require.resolve 'commands.pegjs')) - eval: (editor, command) -> - operations = @parser.parse(command) - operation.execute(editor) for operation in operations + eval: (editor, string) -> + command = @parser.parse(string) + @lastRelativeAddress = command if command.isRelativeAddress() + command.execute(editor) + + repeatLastRelativeAddress: (editor) -> + @lastRelativeAddress.execute(editor) diff --git a/src/atom/command-interpreter/address-range.coffee b/src/atom/command-interpreter/address-range.coffee index a73029f4f..d7302a65a 100644 --- a/src/atom/command-interpreter/address-range.coffee +++ b/src/atom/command-interpreter/address-range.coffee @@ -7,3 +7,6 @@ class AddressRange extends Address getRange: (editor) -> new Range(@startAddress.getRange(editor).start, @endAddress.getRange(editor).end) + + isRelative: -> + @startAddress.isRelative() or @endAddress.isRelative() diff --git a/src/atom/command-interpreter/address.coffee b/src/atom/command-interpreter/address.coffee index 36ef72c15..c587e2486 100644 --- a/src/atom/command-interpreter/address.coffee +++ b/src/atom/command-interpreter/address.coffee @@ -1,4 +1,8 @@ +Command = require 'command-interpreter/command' + module.exports = -class Address +class Address extends Command execute: (editor) -> editor.getSelection().setBufferRange(@getRange(editor)) + + isAddress: -> true diff --git a/src/atom/command-interpreter/command.coffee b/src/atom/command-interpreter/command.coffee new file mode 100644 index 000000000..c60b685b0 --- /dev/null +++ b/src/atom/command-interpreter/command.coffee @@ -0,0 +1,5 @@ +_ = require 'underscore' + +module.exports = +class Command + isAddress: -> false diff --git a/src/atom/command-interpreter/composite-command.coffee b/src/atom/command-interpreter/composite-command.coffee new file mode 100644 index 000000000..efe281fdc --- /dev/null +++ b/src/atom/command-interpreter/composite-command.coffee @@ -0,0 +1,12 @@ +_ = require 'underscore' + +module.exports = +class CompositeCommand + constructor: (@subcommands) -> + + execute: (editor) -> + command.execute(editor) for command in @subcommands + + isRelativeAddress: -> + _.all(@subcommands, (command) -> command.isAddress() and command.isRelative()) + diff --git a/src/atom/command-interpreter/current-selection-address.coffee b/src/atom/command-interpreter/current-selection-address.coffee index 7bf5d2b8a..9b8eef8a8 100644 --- a/src/atom/command-interpreter/current-selection-address.coffee +++ b/src/atom/command-interpreter/current-selection-address.coffee @@ -5,3 +5,5 @@ module.exports = class CurrentSelectionAddress extends Address getRange: (editor) -> editor.getSelection().getBufferRange() + + isRelative: -> true diff --git a/src/atom/command-interpreter/eof-address.coffee b/src/atom/command-interpreter/eof-address.coffee index 7901944f4..f8b14d967 100644 --- a/src/atom/command-interpreter/eof-address.coffee +++ b/src/atom/command-interpreter/eof-address.coffee @@ -6,4 +6,6 @@ class EofAddress extends Address getRange: (editor) -> lastRow = editor.getLastBufferRow() column = editor.getBufferLineLength(lastRow) - new Range([lastRow, column], [lastRow, column]) \ No newline at end of file + new Range([lastRow, column], [lastRow, column]) + + isRelative: -> false diff --git a/src/atom/command-interpreter/line-address.coffee b/src/atom/command-interpreter/line-address.coffee index 28e94543f..245c3d669 100644 --- a/src/atom/command-interpreter/line-address.coffee +++ b/src/atom/command-interpreter/line-address.coffee @@ -8,3 +8,5 @@ class LineAddress extends Address getRange: -> new Range([@row, 0], [@row + 1, 0]) + + isRelative: -> false diff --git a/src/atom/command-interpreter/regex-address.coffee b/src/atom/command-interpreter/regex-address.coffee index ac35cc7be..bec05c71b 100644 --- a/src/atom/command-interpreter/regex-address.coffee +++ b/src/atom/command-interpreter/regex-address.coffee @@ -23,4 +23,6 @@ class RegexAddress extends Address return new Range(startPosition, endPosition) else - return selectedRange \ No newline at end of file + return selectedRange + + isRelative: -> true diff --git a/src/atom/command-interpreter/substitution.coffee b/src/atom/command-interpreter/substitution.coffee index 282779efe..86a93b7b0 100644 --- a/src/atom/command-interpreter/substitution.coffee +++ b/src/atom/command-interpreter/substitution.coffee @@ -1,5 +1,7 @@ +Command = require 'command-interpreter/command' + module.exports = -class Substitution +class Substitution extends Command global: false constructor: (@findText, @replaceText, @options) -> @@ -28,3 +30,4 @@ class Substitution text = text[(match.index + match[0].length)..] startIndex = matchStartIndex + @replaceText.length @replace(editor, text, startIndex) + diff --git a/src/atom/commands.pegjs b/src/atom/commands.pegjs index 449f3f8b6..6d2f85a52 100644 --- a/src/atom/commands.pegjs +++ b/src/atom/commands.pegjs @@ -1,4 +1,5 @@ { + var CompositeCommand = require('command-interpreter/composite-command') var Substitution = require('command-interpreter/substitution'); var LineAddress = require('command-interpreter/line-address'); var AddressRange = require('command-interpreter/address-range'); @@ -9,10 +10,11 @@ start = address:address? _ command:substitution? { - var operations = []; - if (address) operations.push(address); - if (command) operations.push(command); - return operations; + var commands = []; + if (address) commands.push(address); + if (command) commands.push(command); + + return new CompositeCommand(commands); } address = addressRange / primitiveAddress