Fix <C-o> behavior when cursor is behind last character in INSERT mode or when using p next (#8949)

Fixes #8948
Fixes #1791
This commit is contained in:
Dzmitry Harunou 2024-03-26 02:08:11 +01:00 committed by GitHub
parent f7a3ff5e30
commit c8017c8a80
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 139 additions and 75 deletions

View File

@ -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<TBase extends new (...args: any[]) => 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<TBase extends new (...args: any[]) => 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<TBase extends new (...args: any[]) => 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),

View File

@ -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 <C-o>,
// 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);

View File

@ -323,35 +323,81 @@ suite('Mode Insert', () => {
endMode: Mode.Normal,
});
newTest({
title: 'Can <Esc> after entering insert mode from <ctrl+o>',
start: ['|'],
keysPressed: 'i<C-o>i<Esc>',
end: ['|'],
endMode: Mode.Normal,
});
newTest({
title: 'Can perform <ctrl+o> to exit and perform one command in normal',
start: ['testtest|'],
keysPressed: 'a123<C-o>b123',
end: ['123|testtest123'],
});
newTest({
title: 'Can <ctrl-o> after entering insert mode from <ctrl-o>',
start: ['|'],
keysPressed: 'i<C-o>i<C-o>',
end: ['|'],
endMode: Mode.Normal,
});
newTest({
title:
'Can perform <ctrl+o> to exit and perform one command in normal at the beginning of a row',
'Can perform <Esc> to exit and move cursor back one character from the most right position',
start: ['|testtest'],
keysPressed: 'i<C-o>l123',
end: ['t123|esttest'],
keysPressed: 'A<Esc>',
end: ['testtes|t'],
endMode: Mode.Normal,
});
newTest({
title: 'Can perform <Esc> to exit and move cursor back one character from middle of text',
start: ['test|test'],
keysPressed: 'i<Esc>',
end: ['tes|ttest'],
endMode: Mode.Normal,
});
suite('<C-o>', () => {
newTest({
title: 'Can <Esc> after entering insert mode from <ctrl+o>',
start: ['|'],
keysPressed: 'i<C-o>i<Esc>',
end: ['|'],
endMode: Mode.Normal,
});
newTest({
title: 'Can perform <ctrl-o> after entering insert mode from <ctrl-o>',
start: ['test|test'],
keysPressed: 'i<C-o>i<C-o>',
end: ['test|test'],
endMode: Mode.Normal,
});
newTest({
title:
'Can perform <ctrl-o> to exit and perform one command in normal at the beginning of a line',
start: ['|testtest'],
keysPressed: 'i<C-o>l123',
end: ['t123|esttest'],
endMode: Mode.Insert,
});
newTest({
title:
'Can perform <ctrl-o> to exit and perform one command in normal at the middle of a row',
start: ['test|test'],
keysPressed: 'i<C-o>l123',
end: ['testt123|est'],
endMode: Mode.Insert,
});
newTest({
title: 'Can perform <ctrl-o> to exit and perform one command in normal at the end of a row',
start: ['testtest|'],
keysPressed: 'a123<C-o>zz',
end: ['testtest123|'],
endMode: Mode.Insert,
});
newTest({
title: 'Can perform <ctrl-o> to exit and paste',
start: ['|XXX', '123456'],
keysPressed: 'ye' + 'j' + 'A<C-o>p',
end: ['XXX', '123456XXX|'],
endMode: Mode.Insert,
});
newTest({
title: 'Can perform <ctrl-o> to exit and paste',
start: ['|XXX', '123456'],
keysPressed: 'ye' + 'j2|' + 'i<C-o>p',
end: ['XXX', '12XXX|3456'],
endMode: Mode.Insert,
});
});
newTest({