Skip to content
This repository was archived by the owner on Apr 6, 2018. It is now read-only.

Commit f49f5b3

Browse files
author
Max Brunsfeld
committed
Merge pull request #740 from jacekkopecky/fix-visual-selection-repeat
fix redoing visual mode operations with `.`
2 parents 7aeac71 + 886444f commit f49f5b3

File tree

4 files changed

+176
-3
lines changed

4 files changed

+176
-3
lines changed

lib/motions/general-motions.coffee

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,38 @@ class Motion
110110
class CurrentSelection extends Motion
111111
constructor: (@editor, @vimState) ->
112112
super(@editor, @vimState)
113-
@selection = @editor.getSelectedBufferRanges()
113+
@lastSelectionRange = @editor.getSelectedBufferRange()
114+
@wasLinewise = @isLinewise()
114115

115116
execute: (count=1) ->
116117
_.times(count, -> true)
117118

118119
select: (count=1) ->
119-
@editor.setSelectedBufferRanges(@selection)
120+
# in visual mode, the current selections are already there
121+
# if we're not in visual mode, we are repeating some operation and need to re-do the selections
122+
unless @vimState.mode is 'visual'
123+
if @wasLinewise
124+
@selectLines()
125+
else
126+
@selectCharacters()
127+
120128
_.times(count, -> true)
121129

130+
selectLines: ->
131+
lastSelectionExtent = @lastSelectionRange.getExtent()
132+
for selection in @editor.getSelections()
133+
cursor = selection.cursor.getBufferPosition()
134+
selection.setBufferRange [[cursor.row, 0], [cursor.row + lastSelectionExtent.row, 0]]
135+
return
136+
137+
selectCharacters: ->
138+
lastSelectionExtent = @lastSelectionRange.getExtent()
139+
for selection in @editor.getSelections()
140+
{start} = selection.getBufferRange()
141+
newEnd = start.traverse(lastSelectionExtent)
142+
selection.setBufferRange([start, newEnd])
143+
return
144+
122145
# Public: Generic class for motions that require extra input
123146
class MotionWithInput extends Motion
124147
constructor: (@editor, @vimState) ->

spec/motions-spec.coffee

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,16 +1599,36 @@ describe "Motions", ->
15991599
normalModeInputKeydown('b')
16001600
expect(editor.getText()).toBe 'bcabcabcabc\n'
16011601

1602+
describe 'the v keybinding', ->
1603+
beforeEach ->
1604+
editor.setText("01\n002\n0003\n00004\n000005\n")
1605+
editor.setCursorScreenPosition([1, 1])
1606+
1607+
it "selects down a line", ->
1608+
keydown('v')
1609+
keydown('j')
1610+
keydown('j')
1611+
expect(editor.getSelectedText()).toBe "02\n0003\n00"
1612+
expect(editor.getSelectedBufferRange().isSingleLine()).toBeFalsy()
1613+
1614+
it "selects right", ->
1615+
keydown('v')
1616+
keydown('l')
1617+
expect(editor.getSelectedText()).toBe "02"
1618+
expect(editor.getSelectedBufferRange().isSingleLine()).toBeTruthy()
1619+
16021620
describe 'the V keybinding', ->
16031621
beforeEach ->
16041622
editor.setText("01\n002\n0003\n00004\n000005\n")
16051623
editor.setCursorScreenPosition([1, 1])
16061624

16071625
it "selects down a line", ->
16081626
keydown('V', shift: true)
1627+
expect(editor.getSelectedBufferRange().isSingleLine()).toBeFalsy()
16091628
keydown('j')
16101629
keydown('j')
16111630
expect(editor.getSelectedText()).toBe "002\n0003\n00004\n"
1631+
expect(editor.getSelectedBufferRange().isSingleLine()).toBeFalsy()
16121632

16131633
it "selects up a line", ->
16141634
keydown('V', shift: true)

spec/operators-spec.coffee

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,130 @@ describe "Operators", ->
731731
keydown('escape')
732732
expect(editor.getText()).toBe("12345\n\nABCDE")
733733

734+
describe "in visual mode", ->
735+
beforeEach ->
736+
editor.setText "123456789\nabcde\nfghijklmnopq\nuvwxyz"
737+
editor.setCursorScreenPosition [1, 1]
738+
739+
describe "with characterwise selection on a single line", ->
740+
it "repeats with .", ->
741+
keydown 'v'
742+
keydown '2'
743+
keydown 'l'
744+
keydown 'c'
745+
editor.insertText "ab"
746+
keydown 'escape'
747+
expect(editor.getText()).toBe "123456789\naabe\nfghijklmnopq\nuvwxyz"
748+
749+
editor.setCursorScreenPosition [0, 1]
750+
keydown '.'
751+
expect(editor.getText()).toBe "1ab56789\naabe\nfghijklmnopq\nuvwxyz"
752+
753+
it "repeats shortened with . near the end of the line", ->
754+
editor.setCursorScreenPosition [0, 2]
755+
keydown 'v'
756+
keydown '4'
757+
keydown 'l'
758+
keydown 'c'
759+
editor.insertText "ab"
760+
keydown 'escape'
761+
expect(editor.getText()).toBe "12ab89\nabcde\nfghijklmnopq\nuvwxyz"
762+
763+
editor.setCursorScreenPosition [1, 3]
764+
keydown '.'
765+
expect(editor.getText()).toBe "12ab89\nabcab\nfghijklmnopq\nuvwxyz"
766+
767+
it "repeats shortened with . near the end of the line regardless of whether motion wrapping is enabled", ->
768+
atom.config.set('vim-mode.wrapLeftRightMotion', true)
769+
editor.setCursorScreenPosition [0, 2]
770+
keydown 'v'
771+
keydown '4'
772+
keydown 'l'
773+
keydown 'c'
774+
editor.insertText "ab"
775+
keydown 'escape'
776+
expect(editor.getText()).toBe "12ab89\nabcde\nfghijklmnopq\nuvwxyz"
777+
778+
editor.setCursorScreenPosition [1, 3]
779+
keydown '.'
780+
# this differs from VIM, which would eat the \n before fghij...
781+
expect(editor.getText()).toBe "12ab89\nabcab\nfghijklmnopq\nuvwxyz"
782+
783+
describe "is repeatable with characterwise selection over multiple lines", ->
784+
it "repeats with .", ->
785+
keydown 'v'
786+
keydown 'j'
787+
keydown '3'
788+
keydown 'l'
789+
keydown 'c'
790+
editor.insertText "x"
791+
keydown 'escape'
792+
expect(editor.getText()).toBe "123456789\naxklmnopq\nuvwxyz"
793+
794+
editor.setCursorScreenPosition [0, 1]
795+
keydown '.'
796+
expect(editor.getText()).toBe "1xnopq\nuvwxyz"
797+
798+
it "repeats shortened with . near the end of the line", ->
799+
# this behaviour is unlike VIM, see #737
800+
keydown 'v'
801+
keydown 'j'
802+
keydown '6'
803+
keydown 'l'
804+
keydown 'c'
805+
editor.insertText "x"
806+
keydown 'escape'
807+
expect(editor.getText()).toBe "123456789\naxnopq\nuvwxyz"
808+
809+
editor.setCursorScreenPosition [0, 1]
810+
keydown '.'
811+
expect(editor.getText()).toBe "1x\nuvwxyz"
812+
813+
describe "is repeatable with linewise selection", ->
814+
describe "with one line selected", ->
815+
it "repeats with .", ->
816+
keydown 'V', shift: true
817+
keydown 'c'
818+
editor.insertText "x"
819+
keydown 'escape'
820+
expect(editor.getText()).toBe "123456789\nx\nfghijklmnopq\nuvwxyz"
821+
822+
editor.setCursorScreenPosition [0, 7]
823+
keydown '.'
824+
expect(editor.getText()).toBe "x\nx\nfghijklmnopq\nuvwxyz"
825+
826+
editor.setCursorScreenPosition [2, 0]
827+
keydown '.'
828+
expect(editor.getText()).toBe "x\nx\nx\nuvwxyz"
829+
830+
describe "with multiple lines selected", ->
831+
it "repeats with .", ->
832+
keydown 'V', shift: true
833+
keydown 'j'
834+
keydown 'c'
835+
editor.insertText "x"
836+
keydown 'escape'
837+
expect(editor.getText()).toBe "123456789\nx\nuvwxyz"
838+
839+
editor.setCursorScreenPosition [0, 7]
840+
keydown '.'
841+
expect(editor.getText()).toBe "x\nuvwxyz"
842+
843+
it "repeats shortened with . near the end of the file", ->
844+
keydown 'V', shift: true
845+
keydown 'j'
846+
keydown 'c'
847+
editor.insertText "x"
848+
keydown 'escape'
849+
expect(editor.getText()).toBe "123456789\nx\nuvwxyz"
850+
851+
editor.setCursorScreenPosition [1, 7]
852+
keydown '.'
853+
expect(editor.getText()).toBe "123456789\nx\n"
854+
855+
xdescribe "is repeatable with block selection", ->
856+
# there is no block selection yet
857+
734858
describe "the C keybinding", ->
735859
beforeEach ->
736860
editor.getBuffer().setText("012\n")

spec/vim-state-spec.coffee

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,19 @@ describe "VimState", ->
8686
expect(editor.getCursors().length).toBe 1
8787

8888
describe "the v keybinding", ->
89-
beforeEach -> keydown('v')
89+
beforeEach ->
90+
editor.setText("012345\nabcdef")
91+
editor.setCursorScreenPosition([0, 0])
92+
keydown('v')
9093

9194
it "puts the editor into visual characterwise mode", ->
9295
expect(editorElement.classList.contains('visual-mode')).toBe(true)
9396
expect(vimState.submode).toEqual 'characterwise'
9497
expect(editorElement.classList.contains('normal-mode')).toBe(false)
9598

99+
it "selects the current character", ->
100+
expect(editor.getLastSelection().getText()).toEqual '0'
101+
96102
describe "the V keybinding", ->
97103
beforeEach ->
98104
editor.setText("012345\nabcdef")

0 commit comments

Comments
 (0)