diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index 819e0079e..953fb0c48 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -130,6 +130,8 @@ # Atom Specific 'ctrl-W': 'editor:select-word' + 'cmd-ctrl-left': 'editor:move-selection-left' + 'cmd-ctrl-right': 'editor:move-selection-right' # Sublime Parity 'cmd-a': 'core:select-all' diff --git a/keymaps/linux.cson b/keymaps/linux.cson index e676ed5ab..7d67e2ce5 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -14,6 +14,8 @@ 'ctrl-shift-pageup': 'pane:move-item-left' 'ctrl-shift-pagedown': 'pane:move-item-right' 'f11': 'window:toggle-full-screen' + 'alt-shift-left': 'editor:move-selection-left' + 'alt-shift-right': 'editor:move-selection-right' # Sublime Parity 'ctrl-,': 'application:show-settings' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index 5869a3ed8..10450ec18 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -20,6 +20,8 @@ 'ctrl-shift-left': 'pane:move-item-left' 'ctrl-shift-right': 'pane:move-item-right' 'f11': 'window:toggle-full-screen' + 'alt-shift-left': 'editor:move-selection-left' + 'alt-shift-right': 'editor:move-selection-right' # Sublime Parity 'ctrl-,': 'application:show-settings' diff --git a/menus/darwin.cson b/menus/darwin.cson index 7fa2aaf6d..53cc4cbc4 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -75,6 +75,13 @@ { label: 'Join Lines', command: 'editor:join-lines' } ] } + { + label: 'Columns', + submenu: [ + { label: 'Move Selection Left', command: 'editor:move-selection-left' } + { label: 'Move Selection Right', command: 'editor:move-selection-right' } + ] + } { label: 'Text', submenu: [ diff --git a/menus/linux.cson b/menus/linux.cson index 7cfd72885..be11c1430 100644 --- a/menus/linux.cson +++ b/menus/linux.cson @@ -48,6 +48,13 @@ { label: '&Join Lines', command: 'editor:join-lines' } ] } + { + label: 'Columns', + submenu: [ + { label: 'Move Selection &Left', command: 'editor:move-selection-left' } + { label: 'Move Selection &Right', command: 'editor:move-selection-right' } + ] + } { label: 'Text', submenu: [ diff --git a/menus/win32.cson b/menus/win32.cson index 349e3e064..738b52f00 100644 --- a/menus/win32.cson +++ b/menus/win32.cson @@ -56,6 +56,13 @@ { label: '&Join Lines', command: 'editor:join-lines' } ] } + { + label: 'Columns', + submenu: [ + { label: 'Move Selection &Left', command: 'editor:move-selection-left' } + { label: 'Move Selection &Right', command: 'editor:move-selection-right' } + ] + } { label: 'Text', submenu: [ diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 9d2a2a58c..4de7168b7 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -4561,6 +4561,136 @@ describe "TextEditor", -> expect(cursor1.getBufferPosition()).toEqual [0, 0] expect(cursor3.getBufferPosition()).toEqual [1, 2] + describe ".moveSelectionLeft()", -> + it "moves one active selection on one line one column to the left", -> + editor.setSelectedBufferRange [[0, 4], [0, 13]] + expect(editor.getSelectedText()).toBe 'quicksort' + + editor.moveSelectionLeft() + + expect(editor.getSelectedText()).toBe 'quicksort' + expect(editor.getSelectedBufferRange()).toEqual [[0, 3], [0, 12]] + + it "moves multiple active selections on one line one column to the left", -> + editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[0, 16], [0, 24]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'function' + + editor.moveSelectionLeft() + + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'function' + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 3], [0, 12]], [[0, 15], [0, 23]]] + + it "moves multiple active selections on multiple lines one column to the left", -> + editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'sort' + + editor.moveSelectionLeft() + + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'sort' + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 3], [0, 12]], [[1, 5], [1, 9]]] + + describe "when a selection is at the first column of a line", -> + it "does not change the selection", -> + editor.setSelectedBufferRanges([[[0, 0], [0, 3]], [[1, 0], [1, 3]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'var' + expect(selections[1].getText()).toBe ' v' + + editor.moveSelectionLeft() + editor.moveSelectionLeft() + + expect(selections[0].getText()).toBe 'var' + expect(selections[1].getText()).toBe ' v' + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]], [[1, 0], [1, 3]]] + + describe "when multiple selections are active on one line", -> + it "does not change the selection", -> + editor.setSelectedBufferRanges([[[0, 0], [0, 3]], [[0, 4], [0, 13]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'var' + expect(selections[1].getText()).toBe 'quicksort' + + editor.moveSelectionLeft() + + expect(selections[0].getText()).toBe 'var' + expect(selections[1].getText()).toBe 'quicksort' + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]], [[0, 4], [0, 13]]] + + describe ".moveSelectionRight()", -> + it "moves one active selection on one line one column to the right", -> + editor.setSelectedBufferRange [[0, 4], [0, 13]] + expect(editor.getSelectedText()).toBe 'quicksort' + + editor.moveSelectionRight() + + expect(editor.getSelectedText()).toBe 'quicksort' + expect(editor.getSelectedBufferRange()).toEqual [[0, 5], [0, 14]] + + it "moves multiple active selections on one line one column to the right", -> + editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[0, 16], [0, 24]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'function' + + editor.moveSelectionRight() + + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'function' + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 5], [0, 14]], [[0, 17], [0, 25]]] + + it "moves multiple active selections on multiple lines one column to the right", -> + editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'sort' + + editor.moveSelectionRight() + + expect(selections[0].getText()).toBe 'quicksort' + expect(selections[1].getText()).toBe 'sort' + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 5], [0, 14]], [[1, 7], [1, 11]]] + + describe "when a selection is at the last column of a line", -> + it "does not change the selection", -> + editor.setSelectedBufferRanges([[[2, 34], [2, 40]], [[5, 22], [5, 30]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'items;' + expect(selections[1].getText()).toBe 'shift();' + + editor.moveSelectionRight() + editor.moveSelectionRight() + + expect(selections[0].getText()).toBe 'items;' + expect(selections[1].getText()).toBe 'shift();' + expect(editor.getSelectedBufferRanges()).toEqual [[[2, 34], [2, 40]], [[5, 22], [5, 30]]] + + describe "when multiple selections are active on one line", -> + it "does not change the selection", -> + editor.setSelectedBufferRanges([[[2, 27], [2, 33]], [[2, 34], [2, 40]]]) + selections = editor.getSelections() + + expect(selections[0].getText()).toBe 'return' + expect(selections[1].getText()).toBe 'items;' + + editor.moveSelectionRight() + + expect(selections[0].getText()).toBe 'return' + expect(selections[1].getText()).toBe 'items;' + expect(editor.getSelectedBufferRanges()).toEqual [[[2, 27], [2, 33]], [[2, 34], [2, 40]]] + describe 'reading text', -> it '.lineTextForScreenRow(row)', -> editor.foldBufferRow(4) diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index dacb1d228..bb3630117 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -207,6 +207,8 @@ module.exports = ({commandRegistry, commandInstaller, config}) -> 'editor:checkout-head-revision': -> @checkoutHeadRevision() 'editor:move-line-up': -> @moveLineUp() 'editor:move-line-down': -> @moveLineDown() + 'editor:move-selection-left': -> @moveSelectionLeft() + 'editor:move-selection-right': -> @moveSelectionRight() 'editor:duplicate-lines': -> @duplicateLines() 'editor:join-lines': -> @joinLines() ) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 7d8d955e9..58ecf4712 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1074,6 +1074,50 @@ class TextEditor extends Model @autoIndentSelectedRows() if @shouldAutoIndent() @scrollToBufferPosition([newSelectionRanges[0].start.row - 1, 0]) + # Move any active selections one column to the left. + moveSelectionLeft: -> + selections = @getSelectedBufferRanges() + noSelectionAtStartOfLine = selections.every((selection) -> + selection.start.column isnt 0 + ) + + translationDelta = [0, -1] + translatedRanges = [] + + if noSelectionAtStartOfLine + @transact => + for selection in selections + charToLeftOfSelection = new Range(selection.start.translate(translationDelta), selection.start) + charTextToLeftOfSelection = @buffer.getTextInRange(charToLeftOfSelection) + + @buffer.insert(selection.end, charTextToLeftOfSelection) + @buffer.delete(charToLeftOfSelection) + translatedRanges.push(selection.translate(translationDelta)) + + @setSelectedBufferRanges(translatedRanges) + + # Move any active selections one column to the right. + moveSelectionRight: -> + selections = @getSelectedBufferRanges() + noSelectionAtEndOfLine = selections.every((selection) => + selection.end.column isnt @buffer.lineLengthForRow(selection.end.row) + ) + + translationDelta = [0, 1] + translatedRanges = [] + + if noSelectionAtEndOfLine + @transact => + for selection in selections + charToRightOfSelection = new Range(selection.end, selection.end.translate(translationDelta)) + charTextToRightOfSelection = @buffer.getTextInRange(charToRightOfSelection) + + @buffer.delete(charToRightOfSelection) + @buffer.insert(selection.start, charTextToRightOfSelection) + translatedRanges.push(selection.translate(translationDelta)) + + @setSelectedBufferRanges(translatedRanges) + # Duplicate the most recent cursor's current line. duplicateLines: -> @transact =>