Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8bbf915
Implement 'keymodel' and shifted special keys
berknam Dec 7, 2020
8530b91
Add count handling to visual, visual block modes
berknam Dec 7, 2020
1e0984b
Fix mouse selection not selecting last character of line
berknam Dec 7, 2020
c696f65
Implement '<LeftMouse>'
berknam Dec 7, 2020
f101d00
Fix go to insertMode actions not updating start position
berknam Dec 7, 2020
7f62254
Fix 'reselectVisual' moving left on all modes when it should do so on…
berknam Dec 7, 2020
f19d681
Fix IncrementDecrementNumberAction
berknam Dec 7, 2020
2b4e6cf
Implement SelectMode
berknam Dec 7, 2020
0bcf777
Fix recordedState.count not being cleared when it should
berknam Dec 7, 2020
1a541db
Allow 'vim.selectmode' to accept empty string
berknam Dec 9, 2020
8604d97
Apply code reviews
berknam Jan 16, 2021
c174f24
Merge branch 'master' into keyboardSelections
J-Fields Feb 19, 2021
6712628
Merge upstream/master into berknam's keyboardSelections (#5842)
AlexanderFarkas May 1, 2026
8f5da3a
Reconcile #5842 with current master and address review feedback
AlexanderFarkas May 1, 2026
a7cbc08
review: drop scope creep + restore master Insert-mode behavior
AlexanderFarkas May 1, 2026
5109d87
fix: shouldWrapKey on <S-arrow>; handleSelectionChange Keyboard-kind …
AlexanderFarkas May 1, 2026
0830b41
test: add 60-test suite for keymodel / selectmode / Select-mode / #2224
AlexanderFarkas May 1, 2026
7f4f99d
refactor: drop Select mode (Vim's Select-mode behavior was never wired)
AlexanderFarkas May 1, 2026
7533e1b
fix: mouse drag must update cursor.stop, not just cursor.start
AlexanderFarkas May 1, 2026
50ae6a9
test/comment cleanup: drop internal labels, line refs, null assertion
AlexanderFarkas May 1, 2026
177b9d3
test: add operator-after-promotion and count-prefix coverage
AlexanderFarkas May 1, 2026
3e08eab
review: tighten types around <C-o> / pseudo-Visual return state
AlexanderFarkas May 2, 2026
3276578
review: drop allVisualModeKeyBindings — redundant after Select-mode r…
AlexanderFarkas May 2, 2026
24e2e1b
feat: implement <PageUp>/<PageDown>, Insert <C-BS>/<S-BS>, test <Left…
AlexanderFarkas May 2, 2026
ee35a64
test: cover Insert/Replace VisualLine/VisualBlock pseudo-modes
AlexanderFarkas May 2, 2026
25ba81f
review: address self-review findings (PageUp guard, <C-BS> word-back,…
AlexanderFarkas May 2, 2026
96dd1d7
review: fix <C-Right>/<C-Left> WORD/word, <S-Up>/<S-Down> no-op, R<C-o>
AlexanderFarkas May 2, 2026
66be5d3
review: fix <PageUp>/<PageDown> in Insert; default keymodel=startsel,…
AlexanderFarkas May 2, 2026
0f25836
review: restore mouse RTL-drag inclusive edge + add review-coverage t…
AlexanderFarkas May 2, 2026
05e080c
feat: bind macOS option+arrow as word-jump / word-select alias
AlexanderFarkas May 2, 2026
e82c66c
refactor: route option+arrow on mac via existing ctrl+arrow entry
AlexanderFarkas May 2, 2026
52de7ca
review: <C-BS> small-word fix; doc Replace/Insert asymmetry; doc Move…
AlexanderFarkas May 2, 2026
f9f6090
fix: dispatch <LeftMouse> for Insert click past EOL
AlexanderFarkas May 2, 2026
767d694
test: expand mouse-selection coverage 4 → 29
AlexanderFarkas May 2, 2026
e4fd52a
fix: track external selection updates while already in Visual
AlexanderFarkas May 2, 2026
41abca6
fix: drop post-remap updateView that clobbered command-set selection
AlexanderFarkas May 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 119 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
],
"keybindings": [
{
"key": "Escape",
"key": "escape",
"command": "extension.vim_escape",
"when": "editorTextFocus && vim.active && !inDebugRepl"
},
Expand All @@ -82,33 +82,33 @@
"command": "extension.vim_home",
"when": "editorTextFocus && vim.active && !inDebugRepl && vim.mode != 'Insert'"
},
{
"key": "ctrl+home",
"command": "extension.vim_ctrl+home",
"when": "editorTextFocus && vim.active && !inDebugRepl && vim.mode != 'Insert'"
},
{
"key": "End",
"command": "extension.vim_end",
"when": "editorTextFocus && vim.active && !inDebugRepl && vim.mode != 'Insert'"
},
{
"key": "ctrl+end",
"command": "extension.vim_ctrl+end",
"when": "editorTextFocus && vim.active && !inDebugRepl && vim.mode != 'Insert'"
"key": "PageUp",
"command": "extension.vim_pageup",
"when": "editorTextFocus && vim.active && !inDebugRepl && !suggestWidgetVisible && !parameterHintsVisible"
},
{
"key": "PageDown",
"command": "extension.vim_pagedown",
"when": "editorTextFocus && vim.active && !inDebugRepl && !suggestWidgetVisible && !parameterHintsVisible"
},
{
"key": "Insert",
"command": "extension.vim_insert",
"when": "editorTextFocus && vim.active && !inDebugRepl"
},
{
"key": "Backspace",
"key": "backspace",
"command": "extension.vim_backspace",
"when": "editorTextFocus && vim.active && !inDebugRepl"
},
{
"key": "Delete",
"key": "delete",
"command": "extension.vim_delete",
"when": "editorTextFocus && vim.active && !inDebugRepl"
},
Expand Down Expand Up @@ -142,6 +142,100 @@
"command": "extension.vim_down",
"when": "editorTextFocus && vim.active && !inDebugRepl && !suggestWidgetVisible && !parameterHintsVisible"
},
{
"key": "shift+left",
"command": "extension.vim_shift+left",
"when": "editorTextFocus && vim.active && !inDebugRepl"
},
{
"key": "shift+right",
"command": "extension.vim_shift+right",
"when": "editorTextFocus && vim.active && !inDebugRepl"
},
{
"key": "shift+up",
"command": "extension.vim_shift+up",
"when": "editorTextFocus && vim.active && !inDebugRepl && !suggestWidgetVisible && !parameterHintsVisible"
},
{
"key": "shift+down",
"command": "extension.vim_shift+down",
"when": "editorTextFocus && vim.active && !inDebugRepl && !suggestWidgetVisible && !parameterHintsVisible"
},
{
"key": "shift+home",
"command": "extension.vim_shift+home",
"when": "editorTextFocus && vim.active && !inDebugRepl"
},
{
"key": "shift+end",
"command": "extension.vim_shift+end",
"when": "editorTextFocus && vim.active && !inDebugRepl"
},
{
"key": "shift+pageup",
"command": "extension.vim_shift+pageup",
"when": "editorTextFocus && vim.active && !inDebugRepl && !suggestWidgetVisible && !parameterHintsVisible"
},
{
"key": "shift+pagedown",
"command": "extension.vim_shift+pagedown",
"when": "editorTextFocus && vim.active && !inDebugRepl && !suggestWidgetVisible && !parameterHintsVisible"
},
{
"key": "ctrl+left",
"mac": "alt+left",
"command": "extension.vim_ctrl+left",
"when": "editorTextFocus && vim.active && vim.mode != 'Insert' && !inDebugRepl"
},
{
"key": "ctrl+right",
"mac": "alt+right",
"command": "extension.vim_ctrl+right",
"when": "editorTextFocus && vim.active && vim.mode != 'Insert' && !inDebugRepl"
},
{
"key": "ctrl+shift+left",
"mac": "alt+shift+left",
"command": "extension.vim_ctrl+shift+left",
"when": "editorTextFocus && vim.active && !inDebugRepl"
},
{
"key": "ctrl+shift+right",
"mac": "alt+shift+right",
"command": "extension.vim_ctrl+shift+right",
"when": "editorTextFocus && vim.active && !inDebugRepl"
},
{
"key": "ctrl+shift+up",
"command": "extension.vim_ctrl+shift+up",
"when": "editorTextFocus && vim.active && !inDebugRepl && !suggestWidgetVisible && !parameterHintsVisible"
},
{
"key": "ctrl+shift+down",
"command": "extension.vim_ctrl+shift+down",
"when": "editorTextFocus && vim.active && !inDebugRepl && !suggestWidgetVisible && !parameterHintsVisible"
},
{
"key": "ctrl+home",
"command": "extension.vim_ctrl+home",
"when": "editorTextFocus && vim.active && vim.mode != 'Insert' && !inDebugRepl"
},
{
"key": "ctrl+end",
"command": "extension.vim_ctrl+end",
"when": "editorTextFocus && vim.active && vim.mode != 'Insert' && !inDebugRepl"
},
{
"key": "ctrl+shift+home",
"command": "extension.vim_ctrl+shift+home",
"when": "editorTextFocus && vim.active && !inDebugRepl"
},
{
"key": "ctrl+shift+end",
"command": "extension.vim_ctrl+shift+end",
"when": "editorTextFocus && vim.active && !inDebugRepl"
},
{
"key": "g g",
"command": "list.focusFirst",
Expand Down Expand Up @@ -352,16 +446,6 @@
"command": "extension.vim_ctrl+down",
"when": "editorTextFocus && vim.active && vim.mode != 'Insert' && !inDebugRepl"
},
{
"key": "ctrl+left",
"command": "extension.vim_ctrl+left",
"when": "editorTextFocus && vim.active && vim.mode != 'Insert' && !inDebugRepl"
},
{
"key": "ctrl+right",
"command": "extension.vim_ctrl+right",
"when": "editorTextFocus && vim.active && vim.mode != 'Insert' && !inDebugRepl"
},
{
"key": "ctrl+pagedown",
"command": "extension.vim_ctrl+pagedown",
Expand All @@ -385,12 +469,12 @@
{
"key": "ctrl+backspace",
"command": "extension.vim_ctrl+backspace",
"when": "editorTextFocus && vim.active && vim.use<C-BS> && vim.mode != 'Insert' && !inDebugRepl"
"when": "editorTextFocus && vim.active && vim.use<C-BS> && !inDebugRepl"
},
{
"key": "shift+backspace",
"command": "extension.vim_shift+backspace",
"when": "editorTextFocus && vim.active && vim.use<S-BS> && vim.mode != 'Insert' && !inDebugRepl"
"when": "editorTextFocus && vim.active && vim.use<S-BS> && !inDebugRepl"
},
{
"key": "cmd+left",
Expand Down Expand Up @@ -975,9 +1059,20 @@
},
"vim.mouseSelectionGoesIntoVisualMode": {
"type": "boolean",
"description": "If enabled, dragging with the mouse activates Visual mode.",
"markdownDescription": "If enabled, dragging with the mouse activates Visual mode. If disabled, mouse drag stays in the current mode (Normal/Insert/Replace) without selection.",
"default": true
},
"vim.keymodel": {
"type": "string",
"markdownDescription": "Defines the behavior of shifted arrow keys (and `<Home>`/`<End>`/`<PageUp>`/`<PageDown>`). Comma-separated tokens:\n- `startsel` -> using a shifted special key starts/extends a Visual selection.\n- `stopsel` -> using an unshifted special key stops a Visual selection (returns to Normal, or to Insert/Replace if Visual was entered from there via `<S-arrow>` or `<C-o>`).",
"enum": [
"",
"startsel",
"stopsel",
"startsel,stopsel"
],
"default": "startsel,stopsel"
},
"vim.disableExtension": {
"type": "boolean",
"description": "Disables the VSCodeVim extension. The extension will continue to be loaded and activated, but Vim functionality will be disabled.",
Expand Down
3 changes: 2 additions & 1 deletion src/actions/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ export abstract class BaseCommand extends BaseAction {

/**
* If isCompleteAction is true, then triggering this command is a complete action -
* that means that we'll go and try to run it.
* that means that after the command is run we can clear recordedState.count because
* it will have been handled already.
*/
public isCompleteAction = true;

Expand Down
29 changes: 24 additions & 5 deletions src/actions/commands/insert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ export class ExitInsertMode extends BaseCommand {
void vscode.commands.executeCommand('editor.action.inlineSuggest.hide');

vimState.cursors = vimState.cursors.map((x) => x.withNewStop(x.stop.getLeft()));
if (vimState.returnToInsertAfterCommand && position.character !== 0) {
if (vimState.modeToReturnToAfterNormalCommand != null && position.character !== 0) {
vimState.cursors = vimState.cursors.map((x) => x.withNewStop(x.stop.getRight()));
}

Expand Down Expand Up @@ -408,7 +408,7 @@ class DecreaseIndent extends IndentCommand {
@RegisterAction
export class BackspaceInInsertMode extends BaseCommand {
modes = [Mode.Insert];
keys = [['<BS>'], ['<C-h>']];
keys = [['<BS>'], ['<C-h>'], ['<S-BS>']];

override runsOnceForEveryCursor() {
return false;
Expand All @@ -419,6 +419,20 @@ export class BackspaceInInsertMode extends BaseCommand {
}
}

@RegisterAction
class CtrlBackspaceInInsertMode extends BaseCommand {
modes = [Mode.Insert];
keys = ['<C-BS>'];

override runsOnceForEveryCursor() {
return false;
}

public override async exec(position: Position, vimState: VimState): Promise<void> {
vimState.recordedState.transformer.vscodeCommand('deleteWordLeft');
}
}

@RegisterAction
class DeleteInInsertMode extends BaseCommand {
modes = [Mode.Insert];
Expand Down Expand Up @@ -572,13 +586,18 @@ class InsertRegisterContent extends BaseCommand {

@RegisterAction
class ExecuteOneNormalCommandInInsertMode extends BaseCommand {
modes = [Mode.Insert];
modes = [Mode.Insert, Mode.Replace];
keys = ['<C-o>'];

public override async exec(position: Position, vimState: VimState): Promise<void> {
vimState.returnToInsertAfterCommand = true;
const sourceMode = vimState.currentMode === Mode.Replace ? Mode.Replace : Mode.Insert;
vimState.modeToReturnToAfterNormalCommand = sourceMode;
vimState.actionCount = 0;
await new ExitInsertMode().exec(position, vimState);
if (sourceMode === Mode.Insert) {
await new ExitInsertMode().exec(position, vimState);
} else {
await vimState.setCurrentMode(Mode.Normal);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/actions/commands/put.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ abstract class BasePutCommand extends BaseCommand {

const newCursorPosition = this.getCursorPosition({
document: vimState.document,
returnToInsertAfterCommand: vimState.returnToInsertAfterCommand,
returnToInsertAfterCommand: vimState.modeToReturnToAfterNormalCommand != null,
mode,
replaceRange,
registerMode,
Expand Down
3 changes: 3 additions & 0 deletions src/actions/commands/replace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ class ReplaceModeToInsertMode extends BaseCommand {
@RegisterAction
class BackspaceInReplaceMode extends BaseCommand {
modes = [Mode.Replace];
// `<C-BS>` aliases `<BS>` here (one char restore), unlike Insert where it
// word-deletes. Replace's invariant requires every char be tracked and
// restored — skipping past chars via word-back would lose the originals.
keys = [['<BS>'], ['<S-BS>'], ['<C-BS>'], ['<C-h>']];

public override async exec(position: Position, vimState: VimState): Promise<void> {
Expand Down
Loading