Redesign commands around multiple selections

Every command returns an array of buffer ranges to select from its execute method. The composite command loops through each current selection with each command, then sets the new selection to the aggregated buffer ranges returned by all commands at that step.
This commit is contained in:
Nathan Sobo
2012-04-04 12:09:33 -06:00
parent abff6cf387
commit 8d05802290
9 changed files with 57 additions and 35 deletions

View File

@@ -42,18 +42,35 @@ describe "CommandInterpreter", ->
expect(editor.getSelection().getBufferRange()).toEqual [[0,0], [12,2]]
describe ".", ->
it 'maintains the current selection', ->
editor.setSelectionBufferRange([[1,1], [2,2]])
interpreter.eval(editor, '.')
expect(editor.getSelection().getBufferRange()).toEqual [[1,1], [2,2]]
describe "when a single selection", ->
it 'maintains the current selection', ->
editor.clearSelections()
editor.setSelectionBufferRange([[1,1], [2,2]])
interpreter.eval(editor, '.')
expect(editor.getSelection().getBufferRange()).toEqual [[1,1], [2,2]]
editor.setSelectionBufferRange([[1,1], [2,2]])
interpreter.eval(editor, '.,')
expect(editor.getSelection().getBufferRange()).toEqual [[1,1], [12,2]]
editor.setSelectionBufferRange([[1,1], [2,2]])
interpreter.eval(editor, '.,')
expect(editor.getSelection().getBufferRange()).toEqual [[1,1], [12,2]]
editor.setSelectionBufferRange([[1,1], [2,2]])
interpreter.eval(editor, ',.')
expect(editor.getSelection().getBufferRange()).toEqual [[0,0], [2,2]]
editor.setSelectionBufferRange([[1,1], [2,2]])
interpreter.eval(editor, ',.')
expect(editor.getSelection().getBufferRange()).toEqual [[0,0], [2,2]]
describe "with multiple selections", ->
it "maintains the current selections", ->
preSelections = editor.getSelections()
expect(preSelections.length).toBe 3
[preRange1, preRange2, preRange3] = preSelections.map (s) -> s.getScreenRange()
interpreter.eval(editor, '.')
selections = editor.getSelections()
expect(selections.length).toBe 3
[selection1, selection2, selection3] = selections
expect(selection1.getScreenRange()).toEqual preRange1
expect(selection2.getScreenRange()).toEqual preRange2
expect(selection3.getScreenRange()).toEqual preRange3
describe "/regex/", ->
beforeEach ->
@@ -69,14 +86,16 @@ describe "CommandInterpreter", ->
interpreter.eval(editor, '/pivot')
expect(editor.getSelection().getBufferRange()).toEqual [[6,16], [6,21]]
it "searches from the end of the selection furthest forward in the buffer", ->
it "searches from the end of each selection in the buffer", ->
editor.clearSelections()
editor.setSelectionBufferRange([[4,16], [4,20]])
editor.addSelectionForBufferRange([[1,16], [2,20]])
expect(editor.getSelections().length).toBe 2
interpreter.eval(editor, '/pivot')
expect(editor.getSelections().length).toBe 1
expect(editor.getSelection().getBufferRange()).toEqual [[6,16], [6,21]]
selections = editor.getSelections()
expect(selections.length).toBe 2
expect(selections[0].getBufferRange()).toEqual [[3,8], [3,13]]
expect(selections[1].getBufferRange()).toEqual [[6,16], [6,21]]
it "wraps around to the beginning of the buffer, but doesn't infinitely loop if no matches are found", ->
editor.setSelectionBufferRange([[10, 0], [10,3]])

View File

@@ -5,8 +5,8 @@ module.exports =
class AddressRange extends Address
constructor: (@startAddress, @endAddress) ->
getRange: (editor) ->
new Range(@startAddress.getRange(editor).start, @endAddress.getRange(editor).end)
getRange: (editor, currentRange) ->
new Range(@startAddress.getRange(editor, currentRange).start, @endAddress.getRange(editor, currentRange).end)
isRelative: ->
@startAddress.isRelative() or @endAddress.isRelative()

View File

@@ -3,8 +3,6 @@ Command = require 'command-interpreter/command'
module.exports =
class Address extends Command
execute: (editor, currentRange) ->
range = @getRange(editor, currentRange)
editor.clearSelections()
editor.setSelectionBufferRange(range)
[@getRange(editor, currentRange)]
isAddress: -> true

View File

@@ -6,9 +6,11 @@ class CompositeCommand
execute: (editor) ->
for command in @subcommands
ranges = editor.getSelectionsOrderedByBufferPosition().map (selection) -> selection.getBufferRange()
for range in ranges
command.execute(editor, range)
newRanges = []
currentRanges = editor.getSelectionsOrderedByBufferPosition().map (selection) -> selection.getBufferRange()
for currentRange in currentRanges
newRanges.push(command.execute(editor, currentRange)...)
editor.setSelectedBufferRanges(newRanges)
isRelativeAddress: ->
_.all(@subcommands, (command) -> command.isAddress() and command.isRelative())

View File

@@ -3,7 +3,7 @@ Range = require 'range'
module.exports =
class CurrentSelectionAddress extends Address
getRange: (editor) ->
editor.getSelection().getBufferRange()
getRange: (editor, currentRange) ->
currentRange
isRelative: -> true

View File

@@ -8,12 +8,8 @@ class SelectAllMatches extends Command
constructor: (pattern) ->
@regex = new RegExp(pattern, 'g')
execute: (editor) ->
execute: (editor, currentRange) ->
rangesToSelect = []
for selection in editor.getSelections()
editor.buffer.scanRegexMatchesInRange @regex, selection.getBufferRange(), (match, range) ->
rangesToSelect.push(range)
editor.clearSelections()
editor.setSelectionBufferRange(rangesToSelect[0])
editor.addSelectionForBufferRange(range) for range in rangesToSelect[1..]
editor.buffer.scanRegexMatchesInRange @regex, currentRange, (match, range) ->
rangesToSelect.push(range)
rangesToSelect

View File

@@ -9,7 +9,7 @@ class Substitution extends Command
@replacementText = replacementText
@regex = new RegExp(pattern, options.join(''))
execute: (editor, range) ->
editor.buffer.scanRegexMatchesInRange @regex, range, (match, matchRange, { replace }) =>
execute: (editor, currentRange) ->
editor.buffer.scanRegexMatchesInRange @regex, currentRange, (match, matchRange, { replace }) =>
replace(@replacementText)
[currentRange]

View File

@@ -19,7 +19,6 @@ class CompositeSeleciton
getLastSelection: ->
_.last(@selections)
getSelectionsOrderedByBufferPosition: ->
@getSelections().sort (a, b) ->
aRange = a.getBufferRange()
@@ -57,6 +56,13 @@ class CompositeSeleciton
setBufferRange: (bufferRange, options) ->
@getLastSelection().setBufferRange(bufferRange, options)
setBufferRanges: (bufferRanges) ->
@clearSelections()
@setBufferRange(bufferRanges[0])
for bufferRange in bufferRanges[1..]
@addSelectionForBufferRange(bufferRange)
@mergeIntersectingSelections()
getBufferRange: (bufferRange) ->
@getLastSelection().getBufferRange()

View File

@@ -388,6 +388,7 @@ class Editor extends View
getLastSelectionInBuffer: -> @compositeSelection.getLastSelectionInBuffer()
getSelectedText: -> @compositeSelection.getSelection().getText()
setSelectionBufferRange: (bufferRange, options) -> @compositeSelection.setBufferRange(bufferRange, options)
setSelectedBufferRanges: (bufferRanges) -> @compositeSelection.setBufferRanges(bufferRanges)
addSelectionForBufferRange: (bufferRange, options) -> @compositeSelection.addSelectionForBufferRange(bufferRange, options)
selectRight: -> @compositeSelection.selectRight()
selectLeft: -> @compositeSelection.selectLeft()