diff --git a/src/actions/base.ts b/src/actions/base.ts index ea3699845e6..89b7921eb77 100644 --- a/src/actions/base.ts +++ b/src/actions/base.ts @@ -17,6 +17,12 @@ export class BaseAction { public canBeRepeatedWithDot = false; + /** + * Whether we should change desiredColumn in VimState. + */ + public doesntChangeDesiredColumn(): boolean { + return false; + } /** * Modes that this action can be run in. */ diff --git a/src/actions/baseMotion.ts b/src/actions/baseMotion.ts index c7162103205..83e3adfcbeb 100644 --- a/src/actions/baseMotion.ts +++ b/src/actions/baseMotion.ts @@ -53,11 +53,6 @@ export abstract class BaseMovement extends BaseAction { */ isRepeat = false; - /** - * Whether we should change desiredColumn in VimState. - */ - public doesntChangeDesiredColumn = false; - /** * This is for commands like $ which force the desired column to be at * the end of even the longest line. diff --git a/src/actions/commands/actions.ts b/src/actions/commands/actions.ts index cab9387e20f..a01687bb13b 100644 --- a/src/actions/commands/actions.ts +++ b/src/actions/commands/actions.ts @@ -711,6 +711,9 @@ abstract class CommandEditorScroll extends BaseCommand { @RegisterAction class CommandCtrlE extends CommandEditorScroll { keys = ['']; + doesntChangeDesiredColumn() { + return true; + } to: EditorScrollDirection = 'down'; by: EditorScrollByUnit = 'line'; } @@ -718,6 +721,9 @@ class CommandCtrlE extends CommandEditorScroll { @RegisterAction class CommandCtrlY extends CommandEditorScroll { keys = ['']; + doesntChangeDesiredColumn() { + return true; + } to: EditorScrollDirection = 'up'; by: EditorScrollByUnit = 'line'; } @@ -2440,6 +2446,10 @@ class CommandCenterScroll extends BaseCommand { modes = [Mode.Normal, Mode.Visual, Mode.VisualLine, Mode.VisualBlock]; keys = ['z', 'z']; + doesntChangeDesiredColumn() { + return true; + } + public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { // Don't run if there's an operator because the Sneak plugin uses z return ( @@ -2490,6 +2500,10 @@ class CommandTopScroll extends BaseCommand { modes = [Mode.Normal]; keys = ['z', 't']; + doesntChangeDesiredColumn() { + return true; + } + public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { // Don't run if there's an operator because the Sneak plugin uses z return ( @@ -2544,6 +2558,10 @@ class CommandBottomScroll extends BaseCommand { modes = [Mode.Normal]; keys = ['z', 'b']; + doesntChangeDesiredColumn() { + return true; + } + public doesActionApply(vimState: VimState, keysPressed: string[]): boolean { // Don't run if there's an operator because the Sneak plugin uses z return ( diff --git a/src/actions/motion.ts b/src/actions/motion.ts index 5bda50ac970..11b3e2f7d03 100644 --- a/src/actions/motion.ts +++ b/src/actions/motion.ts @@ -99,7 +99,9 @@ class MoveDownByScreenLine extends MoveByScreenLine { } abstract class MoveByScreenLineMaintainDesiredColumn extends MoveByScreenLine { - doesntChangeDesiredColumn = true; + doesntChangeDesiredColumn() { + return true; + } public async execAction(position: Position, vimState: VimState): Promise { let prevDesiredColumn = vimState.desiredColumn; let prevLine = vimState.editor.selection.active.line; @@ -214,7 +216,9 @@ class MoveDownFoldFix extends MoveByScreenLineMaintainDesiredColumn { @RegisterAction class MoveDown extends BaseMovement { keys = ['j']; - doesntChangeDesiredColumn = true; + doesntChangeDesiredColumn() { + return true; + } public async execAction(position: Position, vimState: VimState): Promise { if (configuration.foldfix && vimState.currentMode !== Mode.VisualBlock) { @@ -237,7 +241,9 @@ class MoveDownArrow extends MoveDown { @RegisterAction class MoveUp extends BaseMovement { keys = ['k']; - doesntChangeDesiredColumn = true; + doesntChangeDesiredColumn() { + return true; + } public async execAction(position: Position, vimState: VimState): Promise { if (configuration.foldfix && vimState.currentMode !== Mode.VisualBlock) { @@ -857,7 +863,9 @@ class MoveUpByScreenLineVisualBlock extends BaseMovement { ['g', 'k'], ['g', ''], ]; - doesntChangeDesiredColumn = true; + doesntChangeDesiredColumn() { + return true; + } public async execAction(position: Position, vimState: VimState): Promise { return position.getUp(vimState.desiredColumn); @@ -876,7 +884,9 @@ class MoveDownByScreenLineVisualBlock extends BaseMovement { ['g', 'j'], ['g', ''], ]; - doesntChangeDesiredColumn = true; + doesntChangeDesiredColumn() { + return true; + } public async execAction(position: Position, vimState: VimState): Promise { return position.getDown(vimState.desiredColumn); diff --git a/src/mode/modeHandler.ts b/src/mode/modeHandler.ts index 6bab803b27f..0b2e1f7bad6 100644 --- a/src/mode/modeHandler.ts +++ b/src/mode/modeHandler.ts @@ -564,8 +564,10 @@ export class ModeHandler implements vscode.Disposable { const movement = action instanceof BaseMovement ? action : undefined; if ( - (movement && !movement.doesntChangeDesiredColumn) || - (!movement && vimState.currentMode !== Mode.VisualBlock) + (movement && !movement.doesntChangeDesiredColumn()) || + (!movement && + vimState.currentMode !== Mode.VisualBlock && + !action.doesntChangeDesiredColumn()) ) { // We check !operator here because e.g. d$ should NOT set the desired column to EOL. diff --git a/test/mode/modeNormal.test.ts b/test/mode/modeNormal.test.ts index d7526194112..9ae1feaddc0 100644 --- a/test/mode/modeNormal.test.ts +++ b/test/mode/modeNormal.test.ts @@ -1093,6 +1093,34 @@ suite('Mode Normal', () => { end: ['text te|x'], }); + newTest({ + title: 'A should update desiredColumn', + start: ['|longer line', 'short'], + keysPressed: 'Ajk', + end: ['longer lin|e', 'short'], + }); + + newTest({ + title: 'I should updated desiredColumn', + start: [' hell|o', 'a'], + keysPressed: 'Ijk', + end: [' | hello', 'a'], + }); + + newTest({ + title: 'leaving insert mode should update desired column when entered with a', + start: ['a long line of text', 'shor|t'], + keysPressed: 'amorekj', + end: ['a long line of text', 'shortmor|e'], + }); + + newTest({ + title: 'leaving insert mode should update desired column when entered with i', + start: ['a long line of text', 'shor|t'], + keysPressed: 'imorekj', + end: ['a long line of text', 'shormor|et'], + }); + newTest({ title: "Can handle 'yy' without changing cursor position", start: ['one', 'tw|o'], diff --git a/test/mode/modeVisualLine.test.ts b/test/mode/modeVisualLine.test.ts index 95a3a703fe6..8cfc2c2a333 100644 --- a/test/mode/modeVisualLine.test.ts +++ b/test/mode/modeVisualLine.test.ts @@ -470,5 +470,12 @@ suite('Mode Visual Line', () => { keysPressed: 'VjjA_', end: ['111', '222_|', ' ', '444_', '555'], }); + + newTest({ + title: 'updates desired column correctly', + start: ['|111111', '222', '333'], + keysPressed: 'VjjAjk', + end: ['11111|1', '222', '333'], + }); }); }); diff --git a/test/mode/normalModeTests/motions.test.ts b/test/mode/normalModeTests/motions.test.ts index 4b394099824..eb11f8b7da8 100644 --- a/test/mode/normalModeTests/motions.test.ts +++ b/test/mode/normalModeTests/motions.test.ts @@ -803,4 +803,41 @@ suite('Motions in Normal Mode', () => { keysPressed: 'gjgj', end: ['blah', 'duh', 'a', 'hu|r '], }); + + suite("doesn't update desiredColumn when it shouldn't", () => { + newTest({ + title: 'Preserves desired cursor position when pressing zz', + start: ['very long line of text....|.', 'short line'], + keysPressed: 'jzzk', + end: ['very long line of text....|.', 'short line'], + }); + + newTest({ + title: 'Preserves desired cursor position when pressing zt', + start: ['very long line of text....|.', 'short line'], + keysPressed: 'jztk', + end: ['very long line of text....|.', 'short line'], + }); + + newTest({ + title: 'Preserves desired cursor position when pressing zb', + start: ['very long line of text....|.', 'short line'], + keysPressed: 'jzbk', + end: ['very long line of text....|.', 'short line'], + }); + + newTest({ + title: 'Preserves desired cursor position when pressing ', + start: ['very long line of text....|.', 'short line'], + keysPressed: 'jk', + end: ['very long line of text....|.', 'short line'], + }); + + newTest({ + title: 'Preserves desired cursor position when pressing ', + start: ['short line', 'very long line of text....|.'], + keysPressed: 'kj', + end: ['short line', 'very long line of text....|.'], + }); + }); });