@@ -20,7 +20,9 @@ class Motion
2020 value = for selection in @editor .getSelections ()
2121 if @ isLinewise ()
2222 @ moveSelectionLinewise (selection, count, options)
23- else if @ isInclusive ()
23+ else if @vimState .mode is ' visual'
24+ @ moveSelectionVisual (selection, count, options)
25+ else if @operatesInclusively
2426 @ moveSelectionInclusively (selection, count, options)
2527 else
2628 @ moveSelection (selection, count, options)
@@ -61,17 +63,35 @@ class Motion
6163 selection .setBufferRange ([[newStartRow, 0 ], [newEndRow + 1 , 0 ]])
6264
6365 moveSelectionInclusively : (selection , count , options ) ->
66+ return @ moveSelectionVisual (selection, count, options) unless selection .isEmpty ()
67+
68+ selection .modifySelection =>
69+ @ moveCursor (selection .cursor , count, options)
70+ return if selection .isEmpty ()
71+
72+ if selection .isReversed ()
73+ # for backward motion, add the original starting character of the motion
74+ {start , end } = selection .getBufferRange ()
75+ selection .setBufferRange ([start, [end .row , end .column + 1 ]])
76+ else
77+ # for forward motion, add the ending character of the motion
78+ selection .cursor .moveRight ()
79+
80+ moveSelectionVisual : (selection , count , options ) ->
6481 selection .modifySelection =>
6582 range = selection .getBufferRange ()
6683 [oldStart , oldEnd ] = [range .start , range .end ]
6784
85+ # in visual mode, atom cursor is after the last selected character,
86+ # so here put cursor in the expected place for the following motion
6887 wasEmpty = selection .isEmpty ()
6988 wasReversed = selection .isReversed ()
7089 unless wasEmpty or wasReversed
7190 selection .cursor .moveLeft ()
7291
7392 @ moveCursor (selection .cursor , count, options)
7493
94+ # put cursor back after the last character so it is also selected
7595 isEmpty = selection .isEmpty ()
7696 isReversed = selection .isReversed ()
7797 unless isEmpty or isReversed
@@ -80,10 +100,15 @@ class Motion
80100 range = selection .getBufferRange ()
81101 [newStart , newEnd ] = [range .start , range .end ]
82102
103+ # if we reversed or emptied a normal selection
104+ # we need to select again the last character deselected above the motion
83105 if (isReversed or isEmpty) and not (wasReversed or wasEmpty)
84106 selection .setBufferRange ([newStart, [newEnd .row , oldStart .column + 1 ]])
107+
108+ # if we re-reversed a reversed non-empty selection,
109+ # we need to keep the last character of the old selection selected
85110 if wasReversed and not wasEmpty and not isReversed
86- selection .setBufferRange ([[newStart .row , oldEnd .column - 1 ], newEnd])
111+ selection .setBufferRange ([[oldEnd .row , oldEnd .column - 1 ], newEnd])
87112
88113 # keep a single-character selection non-reversed
89114 range = selection .getBufferRange ()
@@ -104,9 +129,6 @@ class Motion
104129 else
105130 @operatesLinewise
106131
107- isInclusive : ->
108- @vimState .mode is ' visual' or @operatesInclusively
109-
110132class CurrentSelection extends Motion
111133 constructor : (@editor , @vimState ) ->
112134 super (@editor , @vimState )
0 commit comments