diff --git a/src/configuration/iconfiguration.ts b/src/configuration/iconfiguration.ts index 1aa1acf6b50f..7c419faeaa2e 100644 --- a/src/configuration/iconfiguration.ts +++ b/src/configuration/iconfiguration.ts @@ -21,6 +21,7 @@ export interface IAutoSwitchInputMethod { switchIMCmd: string; obtainIMCmd: string; } + export interface IDebugConfiguration { /** * Boolean indicating whether all logs should be suppressed diff --git a/src/configuration/remapper.ts b/src/configuration/remapper.ts index e9ef7452c863..96507cb2b44e 100644 --- a/src/configuration/remapper.ts +++ b/src/configuration/remapper.ts @@ -86,7 +86,7 @@ export class Remapper implements IRemapper { ModeName[vimState.currentMode] }. keybindings=${this._configKey}.` ); - let remapping: IKeyRemapping | undefined = Remapper.findMatchingRemap( + let remapping: IKeyRemapping | undefined = this.findMatchingRemap( userDefinedRemappings, keys, vimState.currentMode @@ -181,7 +181,7 @@ export class Remapper implements IRemapper { } } - protected static findMatchingRemap( + protected findMatchingRemap( userDefinedRemappings: Map, inputtedKeys: string[], currentMode: ModeName @@ -197,6 +197,7 @@ export class Remapper implements IRemapper { for (let sliceLength = startingSliceLength; sliceLength >= range[0]; sliceLength--) { const keySlice = inputtedKeys.slice(-sliceLength).join(''); + this._logger.verbose(`trying to find matching remap for keySlice=${keySlice}.`); if (userDefinedRemappings.has(keySlice)) { // In Insert mode, we allow users to precede remapped commands // with extraneous keystrokes (eg. "hello world jj") diff --git a/src/mode/modeHandler.ts b/src/mode/modeHandler.ts index 41935dfec76a..92722e8c8869 100644 --- a/src/mode/modeHandler.ts +++ b/src/mode/modeHandler.ts @@ -269,25 +269,28 @@ export class ModeHandler implements vscode.Disposable { this.vimState.recordedState.commandList.push(key); try { - // Take the count prefix out to perform the correct remapping. - const withinTimeout = now - this.vimState.lastKeyPressedTimestamp < configuration.timeout; - const isOperatorCombination = this.vimState.recordedState.operator; + const isWithinTimeout = now - this.vimState.lastKeyPressedTimestamp < configuration.timeout; + if (!isWithinTimeout) { + // sufficient time has elapsed since the prior keypress, + // only consider the last keypress for remapping + this.vimState.recordedState.commandList = [ + this.vimState.recordedState.commandList[ + this.vimState.recordedState.commandList.length - 1 + ], + ]; + } let handled = false; + const isOperatorCombination = this.vimState.recordedState.operator; - /** - * Check that - * - * 1) We are not already performing a nonrecursive remapping. - * 2) We aren't in normal mode performing on an operator - * Note: ciwjj should be remapped if jj -> in insert mode - * dd should not remap the second "d", if d -> "_d in normal mode - * 3) We haven't timed out of our previous remapping. - */ + // Check for remapped keys if: + // 1. We are not currently performing a non-recursive remapping + // 2. We are not in normal mode performing on an oeprator + // Example: ciwjj should be remapped if jj -> in insert mode + // dd should not remap the second "d", if d -> "_d in normal mode if ( !this.vimState.isCurrentlyPerformingRemapping && - (!isOperatorCombination || this.vimState.currentMode !== ModeName.Normal) && - (withinTimeout || this.vimState.recordedState.commandList.length === 1) + (!isOperatorCombination || this.vimState.currentMode !== ModeName.Normal) ) { handled = await this._remappers.sendKey( this.vimState.recordedState.commandList, diff --git a/test/configuration/remapper.test.ts b/test/configuration/remapper.test.ts index 87c2f75bb9d9..8945afd6dfd3 100644 --- a/test/configuration/remapper.test.ts +++ b/test/configuration/remapper.test.ts @@ -22,6 +22,10 @@ suite('Remapper', () => { before: ['j', 'j'], after: [''], }, + { + before: [''], + after: [''], + }, ]; const defaultNormalModeKeyBindings: IKeyRemapping[] = [ { @@ -77,7 +81,7 @@ suite('Remapper', () => { inputtedKeys: string[], currentMode: ModeName ) { - return TestRemapper.findMatchingRemap(userDefinedRemappings, inputtedKeys, currentMode); + return super.findMatchingRemap(userDefinedRemappings, inputtedKeys, currentMode); } public getRemappedKeySequenceLengthRange( @@ -297,6 +301,43 @@ suite('Remapper', () => { assert.equal(vscode.window.visibleTextEditors.length, 0); }); + test(' -> in insert mode should go to normal mode', async () => { + const expectedDocumentContent = 'lorem ipsum'; + + // setup + await setupWithBindings({ + insertModeKeyBindings: defaultInsertModeKeyBindings, + normalModeKeyBindings: defaultNormalModeKeyBindings, + visualModeKeyBindings: defaultVisualModeKeyBindings, + }); + + let remapper = new Remappers(); + + const edit = new vscode.WorkspaceEdit(); + edit.insert( + vscode.window.activeTextEditor!.document.uri, + new vscode.Position(0, 0), + expectedDocumentContent + ); + vscode.workspace.applyEdit(edit); + + await modeHandler.handleKeyEvent('i'); + assertEqual(modeHandler.currentMode.name, ModeName.Insert); + + // act + let actual = false; + try { + actual = await remapper.sendKey([''], modeHandler, modeHandler.vimState); + } catch (e) { + assert.fail(e); + } + + // assert + assert.equal(actual, true); + assertEqual(modeHandler.currentMode.name, ModeName.Normal); + assert.equal(vscode.window.activeTextEditor!.document.getText(), expectedDocumentContent); + }); + test('leader, w -> closeActiveEditor in normal mode through modehandler', async () => { // setup await setupWithBindings({