From a2ec4a55498811526c54908d1b6bc68ee65c2564 Mon Sep 17 00:00:00 2001 From: Tristan Koch Date: Fri, 21 Sep 2018 22:38:46 +0200 Subject: [PATCH 1/4] Reset multiple to first cursor's position Respect first cursor's position when clearing multiple cursors. Previously the last added cursor's position was used. By reversing the logic clearing now more closely matches the behavior found in default Atom. --- lib/operator-insert.js | 2 +- lib/vim-state.js | 9 ++++++--- spec/vim-state-spec.coffee | 8 ++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/operator-insert.js b/lib/operator-insert.js index ca761675a..5d1929f5e 100644 --- a/lib/operator-insert.js +++ b/lib/operator-insert.js @@ -145,7 +145,7 @@ class ActivateInsertModeBase extends Operator { // This cursor state is restored on undo. // So cursor state has to be updated before next groupChangesSinceCheckpoint() - if (this.getConfig('clearMultipleCursorsOnEscapeInsertMode')) this.vimState.clearSelections() + if (this.getConfig('clearMultipleCursorsOnEscapeInsertMode')) this.vimState.clearSelections({head: 'first'}) // grouping changes for undo checkpoint need to come last this.groupChangesSinceBufferCheckpoint('undo') diff --git a/lib/vim-state.js b/lib/vim-state.js index 35be563c2..a090e0356 100644 --- a/lib/vim-state.js +++ b/lib/vim-state.js @@ -352,10 +352,13 @@ module.exports = class VimState { } // What's this? - // clear all selections and final cursor position becomes head of last selection. + // clear all selections and final cursor position becomes head of last selection (optionally first selection) // editor.clearSelections() does not respect last selection's head, since it merge all selections before clearing. - clearSelections () { - this.editor.setCursorBufferPosition(this.editor.getCursorBufferPosition()) + clearSelections ({head = 'last'} = {}) { + const position = (head === 'first') + ? this.editor.getCursorBufferPositions()[0] + : this.editor.getCursorBufferPosition() + this.editor.setCursorBufferPosition(position) } resetNormalMode (options = {}) { diff --git a/spec/vim-state-spec.coffee b/spec/vim-state-spec.coffee index 35df88f0b..ebc8971ca 100644 --- a/spec/vim-state-spec.coffee +++ b/spec/vim-state-spec.coffee @@ -223,12 +223,12 @@ describe "VimState", -> describe "when enabled, clear multiple cursors on escaping insert-mode", -> beforeEach -> settings.set('clearMultipleCursorsOnEscapeInsertMode', true) - it "clear multiple cursors by respecting last cursor's position", -> - ensure 'escape', mode: 'normal', numCursors: 1, cursor: [0, 1] + it "clear multiple cursors by respecting first added cursor's position", -> + ensure 'escape', mode: 'normal', numCursors: 1, cursor: [0, 0] - it "clear multiple cursors by respecting last cursor's position", -> + it "clear multiple cursors by respecting first added cursor's position", -> set cursor: [[0, 2], [0, 1]] - ensure 'escape', mode: 'normal', numCursors: 1, cursor: [0, 0] + ensure 'escape', mode: 'normal', numCursors: 1, cursor: [0, 1] describe "when disabled", -> beforeEach -> From e8c4754afd0c6945445df64ad3c95958367581a1 Mon Sep 17 00:00:00 2001 From: Tristan Koch Date: Sat, 22 Sep 2018 12:37:14 +0200 Subject: [PATCH 2/4] Introduce clearMultipleCursorsToFirstPosition setting * By default clear to last position (previous behavior) * When enabled always clear to first position (includes undo) --- lib/operator-insert.js | 2 +- lib/settings.js | 1 + lib/vim-state.js | 2 +- spec/vim-state-spec.coffee | 18 ++++++++++++++---- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/operator-insert.js b/lib/operator-insert.js index 5d1929f5e..ca761675a 100644 --- a/lib/operator-insert.js +++ b/lib/operator-insert.js @@ -145,7 +145,7 @@ class ActivateInsertModeBase extends Operator { // This cursor state is restored on undo. // So cursor state has to be updated before next groupChangesSinceCheckpoint() - if (this.getConfig('clearMultipleCursorsOnEscapeInsertMode')) this.vimState.clearSelections({head: 'first'}) + if (this.getConfig('clearMultipleCursorsOnEscapeInsertMode')) this.vimState.clearSelections() // grouping changes for undo checkpoint need to come last this.groupChangesSinceBufferCheckpoint('undo') diff --git a/lib/settings.js b/lib/settings.js index eb063c696..963dbd7b0 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -330,6 +330,7 @@ module.exports = new Settings('vim-mode-plus', { description: 'Start in insert-mode when editorElement matches scope' }, clearMultipleCursorsOnEscapeInsertMode: false, + clearMultipleCursorsToFirstPosition: false, autoSelectPersistentSelectionOnOperate: true, automaticallyEscapeInsertModeOnActivePaneItemChange: { default: false, diff --git a/lib/vim-state.js b/lib/vim-state.js index a090e0356..e979a871d 100644 --- a/lib/vim-state.js +++ b/lib/vim-state.js @@ -355,7 +355,7 @@ module.exports = class VimState { // clear all selections and final cursor position becomes head of last selection (optionally first selection) // editor.clearSelections() does not respect last selection's head, since it merge all selections before clearing. clearSelections ({head = 'last'} = {}) { - const position = (head === 'first') + const position = (this.getConfig('clearMultipleCursorsToFirstPosition')) ? this.editor.getCursorBufferPositions()[0] : this.editor.getCursorBufferPosition() this.editor.setCursorBufferPosition(position) diff --git a/spec/vim-state-spec.coffee b/spec/vim-state-spec.coffee index ebc8971ca..f5be073c3 100644 --- a/spec/vim-state-spec.coffee +++ b/spec/vim-state-spec.coffee @@ -223,12 +223,22 @@ describe "VimState", -> describe "when enabled, clear multiple cursors on escaping insert-mode", -> beforeEach -> settings.set('clearMultipleCursorsOnEscapeInsertMode', true) - it "clear multiple cursors by respecting first added cursor's position", -> - ensure 'escape', mode: 'normal', numCursors: 1, cursor: [0, 0] + it "clear multiple cursors by respecting last cursor's position", -> + ensure 'escape', mode: 'normal', numCursors: 1, cursor: [0, 1] - it "clear multiple cursors by respecting first added cursor's position", -> + it "clear multiple cursors by respecting last cursor's position", -> set cursor: [[0, 2], [0, 1]] - ensure 'escape', mode: 'normal', numCursors: 1, cursor: [0, 1] + ensure 'escape', mode: 'normal', numCursors: 1, cursor: [0, 0] + + describe "clearMultipleCursorsToFirstPosition setting", -> + beforeEach -> + settings.set('clearMultipleCursorsToFirstPosition', true) + it "clear multiple cursors by respecting first cursor's position", -> + ensure 'escape', mode: 'normal', numCursors: 1, cursor: [0, 0] + + it "clear multiple cursors by respecting first cursor's position", -> + set cursor: [[0, 2], [0, 1]] + ensure 'escape', mode: 'normal', numCursors: 1, cursor: [0, 1] describe "when disabled", -> beforeEach -> From 0a48aeb025ec206bd39db1bf7a5783334806978b Mon Sep 17 00:00:00 2001 From: Tristan Koch Date: Sat, 22 Sep 2018 22:35:13 +0200 Subject: [PATCH 3/4] Remove unused parameter --- lib/vim-state.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vim-state.js b/lib/vim-state.js index e979a871d..c22fe6722 100644 --- a/lib/vim-state.js +++ b/lib/vim-state.js @@ -354,7 +354,7 @@ module.exports = class VimState { // What's this? // clear all selections and final cursor position becomes head of last selection (optionally first selection) // editor.clearSelections() does not respect last selection's head, since it merge all selections before clearing. - clearSelections ({head = 'last'} = {}) { + clearSelections () { const position = (this.getConfig('clearMultipleCursorsToFirstPosition')) ? this.editor.getCursorBufferPositions()[0] : this.editor.getCursorBufferPosition() From d86144d2c039247989eb8fc595314bd81007313e Mon Sep 17 00:00:00 2001 From: Tristan Koch Date: Mon, 24 Sep 2018 01:02:34 +0200 Subject: [PATCH 4/4] Workaround clearing to first when using occurence Conditionally skip workaround involving positioning of popups at proper position. Previously with `clearMultipleCursorsToFirstPosition` enabled, and using occurence, it would clear to second instead of first position. --- lib/occurrence-manager.js | 6 ++++-- spec/occurrence-spec.coffee | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/occurrence-manager.js b/lib/occurrence-manager.js index 902aa3230..de5c770fd 100644 --- a/lib/occurrence-manager.js +++ b/lib/occurrence-manager.js @@ -190,8 +190,10 @@ module.exports = class OccurrenceManager { // It is important to show autocomplete+ popup at proper position( popup shows up at last-selection ). // E.g. `c o f`(change occurrence in a-function) show autocomplete+ popup at closest occurrence. const closestRange = this.getClosestRangeForSelection(ranges, selection) - ranges.splice(ranges.indexOf(closestRange), 1) // remove - ranges.push(closestRange) // then push to last + if (!this.vimState.getConfig('clearMultipleCursorsToFirstPosition')) { + ranges.splice(ranges.indexOf(closestRange), 1) // remove + ranges.push(closestRange) // then push to last + } rangesToSelect.push(...ranges) diff --git a/spec/occurrence-spec.coffee b/spec/occurrence-spec.coffee index ae6a6ec85..cb3a32df8 100644 --- a/spec/occurrence-spec.coffee +++ b/spec/occurrence-spec.coffee @@ -1201,3 +1201,19 @@ describe "Occurrence", -> ensure null, mode: "normal", occurrenceText: [] atom.confirm.andCallFake ({buttons}) -> buttons.indexOf("Continue") ensure "g o", mode: "normal", occurrenceText: ['oo', 'oo', 'oo', 'oo', 'oo'] + + describe "clearMultipleCursorsToFirstPosition setting", -> + beforeEach -> + settings.set('clearMultipleCursorsToFirstPosition', true) + + it "clear multiple occurence cursors by respecting first cursor's position", -> + set + text: """ + ooo: xxx: ooo: + xxx: ooo: xxx: + """ + cursor: [0, 0] + ensure "c o a e", mode: 'insert', numCursors: 3 + editor.insertText('===') + ensure "escape" + ensure "escape", mode: 'normal', numCursors: 1, cursor: [0, 2]