diff --git a/src/actions/commands/put.ts b/src/actions/commands/put.ts index 4c7ffa1f..a2fd2578 100644 --- a/src/actions/commands/put.ts +++ b/src/actions/commands/put.ts @@ -18,6 +18,16 @@ function firstNonBlankChar(text: string): number { return text.match(/\S/)?.index ?? 0; } +type GetCursorPositionParams = { + document: TextDocument; + mode: Mode; + replaceRange: vscode.Range; + registerMode: RegisterMode; + count: number; + text: string; + returnToInsertAfterCommand: boolean; +}; + abstract class BasePutCommand extends BaseCommand { modes = [Mode.Normal, Mode.Visual, Mode.VisualLine, Mode.VisualBlock]; override createsUndoPoint = true; @@ -61,14 +71,15 @@ abstract class BasePutCommand extends BaseCommand { ); } - const newCursorPosition = this.getCursorPosition( - vimState.document, + const newCursorPosition = this.getCursorPosition({ + document: vimState.document, + returnToInsertAfterCommand: vimState.returnToInsertAfterCommand, mode, replaceRange, registerMode, count, text, - ); + }); vimState.recordedState.transformer.moveCursor( PositionDiff.exactPosition(newCursorPosition), @@ -314,14 +325,7 @@ abstract class BasePutCommand extends BaseCommand { protected abstract shouldAdjustIndent(mode: Mode, registerMode: RegisterMode): boolean; - protected abstract getCursorPosition( - document: TextDocument, - mode: Mode, - replaceRange: vscode.Range, - registerMode: RegisterMode, - count: number, - text: string, - ): Position; + protected abstract getCursorPosition(params: GetCursorPositionParams): Position; } @RegisterAction @@ -373,18 +377,23 @@ class PutCommand extends BasePutCommand { return false; } - protected getCursorPosition( - document: TextDocument, - mode: Mode, - replaceRange: vscode.Range, - registerMode: RegisterMode, - count: number, - text: string, - ): Position { + protected getCursorPosition({ + mode, + replaceRange, + registerMode, + text, + returnToInsertAfterCommand, + }: GetCursorPositionParams): Position { const rangeStart = replaceRange.start; if (mode === Mode.Normal || mode === Mode.Visual) { if (registerMode === RegisterMode.CharacterWise) { - return text.includes('\n') ? rangeStart : rangeStart.advancePositionByText(text).getLeft(); + if (text.includes('\n')) { + return rangeStart; + } else if (returnToInsertAfterCommand) { + return rangeStart.advancePositionByText(text); + } else { + return rangeStart.advancePositionByText(text).getLeft(); + } } else if (registerMode === RegisterMode.LineWise) { return new Position(rangeStart.line + 1, firstNonBlankChar(text)); } else if (registerMode === RegisterMode.BlockWise) { @@ -445,14 +454,13 @@ class PutBeforeCommand extends PutCommand { return super.getReplaceRange(mode, cursor, registerMode); } - protected override getCursorPosition( - document: TextDocument, - mode: Mode, - replaceRange: vscode.Range, - registerMode: RegisterMode, - count: number, - text: string, - ): Position { + protected override getCursorPosition({ + mode, + replaceRange, + text, + registerMode, + ...params + }: GetCursorPositionParams): Position { const rangeStart = replaceRange.start; if (mode === Mode.Normal || mode === Mode.VisualBlock) { if (registerMode === RegisterMode.LineWise) { @@ -460,20 +468,21 @@ class PutBeforeCommand extends PutCommand { } } - return super.getCursorPosition(document, mode, replaceRange, registerMode, count, text); + return super.getCursorPosition({ mode, replaceRange, text, registerMode, ...params }); } } function PlaceCursorAfterText PutCommand>(Base: TBase) { return class CursorAfterText extends Base { - protected override getCursorPosition( - document: TextDocument, - mode: Mode, - replaceRange: vscode.Range, - registerMode: RegisterMode, - count: number, - text: string, - ): Position { + protected override getCursorPosition({ + document, + mode, + replaceRange, + registerMode, + count, + text, + ...params + }: GetCursorPositionParams): Position { const rangeStart = replaceRange.start; if (mode === Mode.Normal || mode === Mode.Visual) { if (registerMode === RegisterMode.CharacterWise) { @@ -518,7 +527,15 @@ function PlaceCursorAfterText PutCommand>( } } - return super.getCursorPosition(document, mode, replaceRange, registerMode, count, text); + return super.getCursorPosition({ + document, + mode, + replaceRange, + registerMode, + count, + text, + ...params, + }); } }; } @@ -584,14 +601,10 @@ function ExCommand PutCommand>(Base: TBase return new vscode.Range(pos, pos); } - protected override getCursorPosition( - document: TextDocument, - mode: Mode, - replaceRange: vscode.Range, - registerMode: RegisterMode, - count: number, - text: string, - ): Position { + protected override getCursorPosition({ + replaceRange, + text, + }: GetCursorPositionParams): Position { const lines = text.split('\n'); return new Position( replaceRange.start.line + lines.length - (this.putBefore() ? 1 : 0), diff --git a/src/mode/modeHandler.ts b/src/mode/modeHandler.ts index e73d16cd..f2ca8617 100644 --- a/src/mode/modeHandler.ts +++ b/src/mode/modeHandler.ts @@ -908,7 +908,7 @@ export class ModeHandler implements vscode.Disposable, IModeHandler { const documentLineCount = this.vimState.document.lineCount; this.vimState.cursors = this.vimState.cursors.map((cursor: Cursor) => { - // adjust start/stop + // Adjust start/stop if (cursor.start.line >= documentLineCount) { cursor = cursor.withNewStart(documentEndPosition); } @@ -916,8 +916,13 @@ export class ModeHandler implements vscode.Disposable, IModeHandler { cursor = cursor.withNewStop(documentEndPosition); } - // adjust column - if (this.vimState.currentMode === Mode.Normal || isVisualMode(this.vimState.currentMode)) { + // Adjust column. When getting from insert into normal mode with , + // the cursor position should remain even if it is behind the last + // character in the line + if ( + !this.vimState.returnToInsertAfterCommand && + (this.vimState.currentMode === Mode.Normal || isVisualMode(this.vimState.currentMode)) + ) { const currentLineLength = TextEditor.getLineLength(cursor.stop.line); const currentStartLineLength = TextEditor.getLineLength(cursor.start.line); diff --git a/test/mode/modeInsert.test.ts b/test/mode/modeInsert.test.ts index 9eb4db9f..3b67ca19 100644 --- a/test/mode/modeInsert.test.ts +++ b/test/mode/modeInsert.test.ts @@ -323,35 +323,81 @@ suite('Mode Insert', () => { endMode: Mode.Normal, }); - newTest({ - title: 'Can after entering insert mode from ', - start: ['|'], - keysPressed: 'ii', - end: ['|'], - endMode: Mode.Normal, - }); - - newTest({ - title: 'Can perform to exit and perform one command in normal', - start: ['testtest|'], - keysPressed: 'a123b123', - end: ['123|testtest123'], - }); - - newTest({ - title: 'Can after entering insert mode from ', - start: ['|'], - keysPressed: 'ii', - end: ['|'], - endMode: Mode.Normal, - }); - newTest({ title: - 'Can perform to exit and perform one command in normal at the beginning of a row', + 'Can perform to exit and move cursor back one character from the most right position', start: ['|testtest'], - keysPressed: 'il123', - end: ['t123|esttest'], + keysPressed: 'A', + end: ['testtes|t'], + endMode: Mode.Normal, + }); + + newTest({ + title: 'Can perform to exit and move cursor back one character from middle of text', + start: ['test|test'], + keysPressed: 'i', + end: ['tes|ttest'], + endMode: Mode.Normal, + }); + + suite('', () => { + newTest({ + title: 'Can after entering insert mode from ', + start: ['|'], + keysPressed: 'ii', + end: ['|'], + endMode: Mode.Normal, + }); + + newTest({ + title: 'Can perform after entering insert mode from ', + start: ['test|test'], + keysPressed: 'ii', + end: ['test|test'], + endMode: Mode.Normal, + }); + + newTest({ + title: + 'Can perform to exit and perform one command in normal at the beginning of a line', + start: ['|testtest'], + keysPressed: 'il123', + end: ['t123|esttest'], + endMode: Mode.Insert, + }); + + newTest({ + title: + 'Can perform to exit and perform one command in normal at the middle of a row', + start: ['test|test'], + keysPressed: 'il123', + end: ['testt123|est'], + endMode: Mode.Insert, + }); + + newTest({ + title: 'Can perform to exit and perform one command in normal at the end of a row', + start: ['testtest|'], + keysPressed: 'a123zz', + end: ['testtest123|'], + endMode: Mode.Insert, + }); + + newTest({ + title: 'Can perform to exit and paste', + start: ['|XXX', '123456'], + keysPressed: 'ye' + 'j' + 'Ap', + end: ['XXX', '123456XXX|'], + endMode: Mode.Insert, + }); + + newTest({ + title: 'Can perform to exit and paste', + start: ['|XXX', '123456'], + keysPressed: 'ye' + 'j2|' + 'ip', + end: ['XXX', '12XXX|3456'], + endMode: Mode.Insert, + }); }); newTest({