diff --git a/src/actions/commands/insert.ts b/src/actions/commands/insert.ts index 6a43e88b13b..4a81f561d6f 100644 --- a/src/actions/commands/insert.ts +++ b/src/actions/commands/insert.ts @@ -237,9 +237,9 @@ export class CommandBackspaceInInsertMode extends BaseCommand { public async exec(position: Position, vimState: VimState): Promise { const line = TextEditor.getLineAt(position).text; - const selection = TextEditor.getSelection(); + const selection = vimState.editor.selections.find((s) => s.contains(position)); - if (!selection.isEmpty) { + if (selection && !selection.isEmpty) { // If a selection is active, delete it vimState.recordedState.transformations.push({ type: 'deleteRange', @@ -310,18 +310,11 @@ export class CommandInsertInInsertMode extends BaseCommand { public async exec(position: Position, vimState: VimState): Promise { const char = this.keysPressed[this.keysPressed.length - 1]; - if (vimState.isMultiCursor) { - vimState.recordedState.transformations.push({ - type: 'insertText', - text: char, - position: vimState.cursorStopPosition, - }); - } else { - vimState.recordedState.transformations.push({ - type: 'insertTextVSCode', - text: char, - }); - } + vimState.recordedState.transformations.push({ + type: 'insertTextVSCode', + text: char, + isMultiCursor: vimState.isMultiCursor, + }); return vimState; } diff --git a/src/mode/modeHandler.ts b/src/mode/modeHandler.ts index 0a9ea8030e6..65177946405 100644 --- a/src/mode/modeHandler.ts +++ b/src/mode/modeHandler.ts @@ -27,7 +27,7 @@ import { VsCodeContext } from '../util/vscode-context'; import { commandLine } from '../cmd_line/commandLine'; import { configuration } from '../configuration/configuration'; import { decoration } from '../configuration/decoration'; -import { getCursorsAfterSync, scrollView, partition } from '../util/util'; +import { getCursorsAfterSync, scrollView } from '../util/util'; import { BaseCommand, CommandQuitRecordMacro, @@ -37,7 +37,9 @@ import { areAnyTransformationsOverlapping, isTextTransformation, TextTransformations, - Transformation, + areAllSameTransformation, + isMultiCursorTextTransformation, + InsertTextVSCodeTransformation, } from './../transformations/transformations'; import { globalState } from '../state/globalState'; import { reportSearch } from '../util/statusBarTextUtils'; @@ -814,10 +816,16 @@ export class ModeHandler implements vscode.Disposable { return vimState; } - const [textTransformations, otherTransformations] = partition( - transformations, - isTextTransformation - ) as [TextTransformations[], Transformation[]]; + const textTransformations: TextTransformations[] = transformations.filter((x) => + isTextTransformation(x) + ) as any; + const multicursorTextTransformations: InsertTextVSCodeTransformation[] = transformations.filter( + (x) => isMultiCursorTextTransformation(x) + ) as any; + + const otherTransformations = transformations.filter( + (x) => !isTextTransformation(x) && !isMultiCursorTextTransformation(x) + ); let accumulatedPositionDifferences: { [key: number]: PositionDiff[] } = {}; @@ -890,6 +898,26 @@ export class ModeHandler implements vscode.Disposable { } } + if (multicursorTextTransformations.length > 0) { + if (areAllSameTransformation(multicursorTextTransformations)) { + /** + * Apply the transformation only once instead of to each cursor + * if they are all the same. + * + * This lets VSCode do multicursor snippets, auto braces and + * all the usual jazz VSCode does on text insertion. + */ + const { text } = multicursorTextTransformations[0]; + + // await vscode.commands.executeCommand('default:type', { text }); + await TextEditor.insert(text); + } else { + this._logger.warn( + `Unhandled multicursor transformations. Not all transformations are the same!` + ); + } + } + for (const transformation of otherTransformations) { switch (transformation.type) { case 'insertTextVSCode': diff --git a/src/transformations/transformations.ts b/src/transformations/transformations.ts index 8241174c880..e053ff05374 100644 --- a/src/transformations/transformations.ts +++ b/src/transformations/transformations.ts @@ -99,6 +99,11 @@ export interface InsertTextVSCodeTransformation { */ text: string; + /** + * Whether this transformation was created in multicursor mode. + */ + isMultiCursor?: boolean; + /** * The index of the cursor that this transformation applies to. */ @@ -291,6 +296,9 @@ export const isTextTransformation = (x: Transformation): x is TextTransformation x.type === 'moveCursor' ); }; +export const isMultiCursorTextTransformation = (x: Transformation): Boolean => { + return (x.type === 'insertTextVSCode' && x.isMultiCursor) ?? false; +}; const getRangeFromTextTransformation = (transformation: TextTransformations): Range | undefined => { switch (transformation.type) { @@ -330,3 +338,13 @@ export const areAnyTransformationsOverlapping = (transformations: TextTransforma return false; }; + +export const areAllSameTransformation = (transformations: Transformation[]): Boolean => { + const firstTransformation = transformations[0]; + + return transformations.every((t) => { + return Object.entries(t).every(([key, value]) => { + return firstTransformation[key] === value; + }); + }); +}; diff --git a/src/util/util.ts b/src/util/util.ts index 08efead0d05..8325b06bab4 100644 --- a/src/util/util.ts +++ b/src/util/util.ts @@ -53,18 +53,3 @@ export function scrollView(vimState: VimState, offset: number) { }); } } - -/** - * Partitions an array into two according to a given predicate - * @param array Objects to partition - * @param predicate Function according to which objects will be partitioned - * @returns Array which contains all elements of `array` for which `predicate` is true, and one which contains the rest - */ -export function partition(array: T[], predicate: (x: T) => boolean): [T[], T[]] { - return array.reduce( - ([pass, fail], elem) => { - return predicate(elem) ? [[...pass, elem], fail] : [pass, [...fail, elem]]; - }, - [[], []] - ); -}