Last active
July 18, 2024 23:28
-
-
Save anthonyjclark/1de4882c13ffd3c61863bc657557253e to your computer and use it in GitHub Desktop.
Custom bindings for DCsunset/vscode-modal-editor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ---------------------------------------------------------------- | |
// ▗▄▄▄▖ █ | |
// ▐▛▀▀▘ ▐▌ ▀ | |
// ▐▌ ▝█ █▘▐███ ▟█▙ ▐▙██▖▗▟██▖ ██ ▟█▙ ▐▙██▖▗▟██▖ | |
// ▐███ ▐█▌ ▐▌ ▐▙▄▟▌▐▛ ▐▌▐▙▄▖▘ █ ▐▛ ▜▌▐▛ ▐▌▐▙▄▖▘ | |
// ▐▌ ▗█▖ ▐▌ ▐▛▀▀▘▐▌ ▐▌ ▀▀█▖ █ ▐▌ ▐▌▐▌ ▐▌ ▀▀█▖ | |
// ▐▙▄▄▖ ▟▀▙ ▐▙▄ ▝█▄▄▌▐▌ ▐▌▐▄▄▟▌▗▄█▄▖▝█▄█▘▐▌ ▐▌▐▄▄▟▌ | |
// ▝▀▀▀▘▝▀ ▀▘ ▀▀ ▝▀▀ ▝▘ ▝▘ ▀▀▀ ▝▀▀▀▘ ▝▀▘ ▝▘ ▝▘ ▀▀▀ | |
// ---------------------------------------------------------------- | |
// https://github.com/alefragnani/vscode-bookmarks | |
// https://github.com/AWtnb/vscode-cursor-eraser | |
// https://github.com/dkundel/vscode-new-file | |
// https://github.com/earshinov/vscode-simple-alignment | |
// https://github.com/haberdashPI/vscode-select-by-indent | |
// https://github.com/kaiwood/vscode-center-editor-window | |
// https://github.com/mtbaqer/vscode-better-folding | |
// https://github.com/MurlocCra4ler/vscode-leap | |
// https://github.com/qcz/vscode-text-power-tools | |
// https://github.com/silesky/vscode-toggle-bool | |
// https://github.com/stkb/Rewrap | |
// https://github.com/usernamehw/vscode-find-jump | |
// https://github.com/viablelab/vscode-capitalize | |
// ---------------------------------------------------------------- | |
// ▗▖ ▄▖ ▗▖ █ ▗▖ █ | |
// ▐▌▐▛ ▐▌ ▀ ▐▌ ▀ | |
// ▐▙█ ▟█▙ ▝█ █▌▐▙█▙ ██ ▐▙██▖ ▟█▟▌ ██ ▐▙██▖ ▟█▟▌▗▟██▖ | |
// ▐██ ▐▙▄▟▌ █▖█ ▐▛ ▜▌ █ ▐▛ ▐▌▐▛ ▜▌ █ ▐▛ ▐▌▐▛ ▜▌▐▙▄▖▘ | |
// ▐▌▐▙ ▐▛▀▀▘ ▐█▛ ▐▌ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌ ▀▀█▖ | |
// ▐▌ █▖▝█▄▄▌ █▌ ▐█▄█▘▗▄█▄▖▐▌ ▐▌▝█▄█▌▗▄█▄▖▐▌ ▐▌▝█▄█▌▐▄▄▟▌ | |
// ▝▘ ▝▘ ▝▀▀ █ ▝▘▀▘ ▝▀▀▀▘▝▘ ▝▘ ▝▀▝▘▝▀▀▀▘▝▘ ▝▘ ▞▀▐▌ ▀▀▀ | |
// █▌ ▜█▛▘ | |
// ---------------------------------------------------------------- | |
// Useful keybindings: | |
// - ESCAPE to remove secondary cursors | |
// - UP/DOWN to move lines | |
// - LEFT/RIGHT to accept next word in suggestion | |
// - ALT+ENTER in find widget will "editor.action.selectAllMatches" and respect selection | |
// - CMD+ENTER in find widget will replace all matches in selection | |
// ---------------------------------------------------------------- | |
// ▗▄▖ █ | |
// ▗▛▀▜ ▐▌ ▐▌ ▀ | |
// ▐▙ ▟█▙ ▐███ ▐███ ██ ▐▙██▖ ▟█▟▌▗▟██▖ | |
// ▜█▙ ▐▙▄▟▌ ▐▌ ▐▌ █ ▐▛ ▐▌▐▛ ▜▌▐▙▄▖▘ | |
// ▜▌▐▛▀▀▘ ▐▌ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌ ▀▀█▖ | |
// ▐▄▄▟▘▝█▄▄▌ ▐▙▄ ▐▙▄ ▗▄█▄▖▐▌ ▐▌▝█▄█▌▐▄▄▟▌ | |
// ▀▀▘ ▝▀▀ ▀▀ ▀▀ ▝▀▀▀▘▝▘ ▝▘ ▞▀▐▌ ▀▀▀ | |
// ▜█▛▘ | |
// ---------------------------------------------------------------- | |
// "editor.find.autoFindInSelection": "multiline", | |
// "extensions.experimental.affinity": { "dcsunset.vscode-modal-editor": 1 }, | |
// "remote.extensionKind": { "murloccra4ler.leap": ["ui", "workspace"], ... }, | |
// ---------------------------------------------------------------- | |
// ▄▄▄ ▗▖ | |
// ▀█▀ ▐▌ | |
// █ ▟█▟▌ ▟█▙ ▟██▖▗▟██▖ | |
// █ ▐▛ ▜▌▐▙▄▟▌ ▘▄▟▌▐▙▄▖▘ | |
// █ ▐▌ ▐▌▐▛▀▀▘▗█▀▜▌ ▀▀█▖ | |
// ▄█▄ ▝█▄█▌▝█▄▄▌▐▙▄█▌▐▄▄▟▌ | |
// ▀▀▀ ▝▀▝▘ ▝▀▀ ▀▀▝▘ ▀▀▀ | |
// ---------------------------------------------------------------- | |
// command to create list from paragraph (add - or * to beginning of each line) | |
// Repeated jump is broken when selecting (replay leaves selection mode) | |
// For uiua: join lines in reverse | |
// 1. move line down | |
// 2. move cursor up | |
// 3. join lines | |
// Diff view keybindings | |
// Enter for blank line below | |
// Shift-enter for blank line above | |
// editor.action.copyLinesDownAction | |
// editor.action.deleteLines | |
// Switch over to cursorless for most functionality | |
// https://www.cursorless.org/docs/user/experimental/keyboard/modal/ | |
// 'attribute' | 'attributes' | 'block' | 'blocks' | CALL_TOKEN | 'calls' | 'callee' | 'cell' | |
// | 'callees' | 'cells' | 'chapter' | 'chapters' | CHAR_TOKEN | 'class' | | |
// 'classes' | 'class name' | 'class names' | 'command' | 'commands' | | |
// COMMENT_TOKEN | 'comments' | 'condition' | 'conditions' | 'element' | | |
// 'elements' | 'end tag' | 'end tags' | 'environment' | 'environments' | 'file' | |
// | 'files' | 'funk' | 'funks' | 'funk name' | 'funk names' | 'identifier' | | |
// 'identifiers' | 'if state' | 'if states' | 'instance' | 'instances' | 'item' | |
// | 'items' | 'key' | 'keys' | 'lambda' | 'lambdas' | 'line' | 'lines' | 'link' | |
// | 'links' | 'list' | 'lists' | 'map' | 'maps' | 'name' | 'names' | 'paint' | | |
// 'paints' | 'paragraph' | 'paragraphs' | 'part' | 'parts' | 'regex' | | |
// 'regexes' | 'section' | 'sections' | 'selector' | 'selectors' | 'sentence' | | |
// 'sentences' | 'short paint' | 'short paints' | 'start tag' | 'start tags' | | |
// 'state' | 'states' | 'subparagraph' | 'subparagraphs' | 'subsection' | | |
// 'subsections' | 'subsubsection' | 'subsubsections' | 'tags' | 'token' | | |
// 'tokens' | 'type' | 'types' | 'unit' | 'units' | 'value' | 'values' | | |
// WORD_TOKEN; | |
// Built-in commands (https://code.visualstudio.com/api/references/commands) | |
// https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-clue.md | |
// New features | |
// - record text object plus deletion (only records deletion) | |
// - record change with count? | |
// - add macros to extension (other extensions don't play nicely) | |
// looks like it can be done similarly to records (save commands to list and replay) | |
// To consider | |
// - folding (editor.fold and arguments; editor.unfold and arguments) | |
// - how to handle fallthrough like hydra? | |
// space-space | |
// debugger (step-in, step-out, continue, stop, reset) | |
// python (run selection, run all, next cell, insert above, insert below, etc.) | |
// jupyter.execSelectionInteractive | |
// Copilot | |
// - generate tests | |
// - explain | |
// - generate code | |
// ---------------------------------------------------------------- | |
// ▗▄▄▄▖ █ | |
// ▐▛▀▀▘ ▐▌ ▀ | |
// ▐▌ ▐▌ ▐▌▐▙██▖ ▟██▖▐███ ██ ▟█▙ ▐▙██▖▗▟██▖ | |
// ▐███ ▐▌ ▐▌▐▛ ▐▌▐▛ ▘ ▐▌ █ ▐▛ ▜▌▐▛ ▐▌▐▙▄▖▘ | |
// ▐▌ ▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌ ▀▀█▖ | |
// ▐▌ ▐▙▄█▌▐▌ ▐▌▝█▄▄▌ ▐▙▄ ▗▄█▄▖▝█▄█▘▐▌ ▐▌▐▄▄▟▌ | |
// ▝▘ ▀▀▝▘▝▘ ▝▘ ▝▀▀ ▀▀ ▝▀▀▀▘ ▝▀▘ ▝▘ ▝▘ ▀▀▀ | |
// ---------------------------------------------------------------- | |
// Enable command repeat | |
function repeatable( command ) { return { command, count: "_ctx.count" }; } | |
// Record command to a register | |
function record( command, reg ) { return { command, record: reg }; } | |
// Record change and motion | |
const recordChange = command => record( command, "change" ); | |
const recordMotion = command => record( command, "motion" ); | |
// Call findText with computer or un-computed args | |
function findText( till, select, backward, regex, text ) { | |
let computedArgs = text.includes( "_ctx" ); | |
let args = computedArgs | |
? `{ text: ${ text }, till: ${ till }, select: ${ select }, backward: ${ backward }, regex: ${ regex } }` | |
: { text: text, till: till, select: select, backward: backward, regex: regex }; | |
return { | |
command: "modalEditor.findText", | |
computedArgs: computedArgs, | |
args: args | |
}; | |
} | |
// Use find widget with arguments | |
function findWidget( caseSensitive, regex, wholeWord ) { | |
return { | |
command: "editor.actions.findWithArgs", | |
args: { | |
// findInSelection: false, | |
isCaseSensitive: caseSensitive, | |
isRegex: regex, | |
matchWholeWord: wholeWord | |
} | |
}; | |
} | |
// Select all text (inner/around) pairs | |
function selectPair( till, left, right ) { | |
right = typeof right !== "undefined" ? right : left; | |
return [ findText( till, false, true, false, left ), findText( till, true, false, false, right ) ]; | |
} | |
// All jumps (find/till/sneak) have the same basic formation | |
function jumpMotion( till, select, backward, numKeys ) { | |
let commandList = []; | |
const left = select ? "cursorLeftSelect" : "cursorLeft"; | |
const right = select ? "cursorRightSelect" : "cursorRight"; | |
// Skip current character when moving backward | |
if ( till && backward ) commandList.push( left ); | |
// Adjust cursor when moving forward | |
else if ( till && !backward ) commandList.push( right ); | |
// Find text based on the previously typed keys | |
commandList.push( findText( till, select, backward, false, `_ctx.keys.slice(-${ numKeys })` ) ); | |
// Move cursor left when motion is forward | |
if ( !backward ) commandList.push( left ); | |
// Return the wildcard command | |
return { "": recordMotion( repeatable( [ commandList ] ) ) }; | |
} | |
function cursorlessSelection( target ) { | |
return { | |
command: "cursorless.command", | |
args: { | |
version: 2, | |
action: { name: "setSelection", args: [] }, | |
targets: [ { type: "primitive", modifiers: [ { type: "containingScope", scopeType: { type: target } } ] } ], | |
} | |
} | |
} | |
module.exports = { | |
// ---------------------------------------------------------------- | |
// ▄▄ ▗▖ ▄▖ | |
// █▀▀▌ ▐▌▐▛ | |
// ▐▛ ▟█▙ ▐█▙█▖▐█▙█▖ ▟█▙ ▐▙██▖ ▐▙█ ▟█▙ ▝█ █▌▐█▙█▖ ▟██▖▐▙█▙ | |
// ▐▌ ▐▛ ▜▌▐▌█▐▌▐▌█▐▌▐▛ ▜▌▐▛ ▐▌ ▐██ ▐▙▄▟▌ █▖█ ▐▌█▐▌ ▘▄▟▌▐▛ ▜▌ | |
// ▐▙ ▐▌ ▐▌▐▌█▐▌▐▌█▐▌▐▌ ▐▌▐▌ ▐▌ ▐▌▐▙ ▐▛▀▀▘ ▐█▛ ▐▌█▐▌▗█▀▜▌▐▌ ▐▌ | |
// █▄▄▌▝█▄█▘▐▌█▐▌▐▌█▐▌▝█▄█▘▐▌ ▐▌ ▐▌ █▖▝█▄▄▌ █▌ ▐▌█▐▌▐▙▄█▌▐█▄█▘ | |
// ▀▀ ▝▀▘ ▝▘▀▝▘▝▘▀▝▘ ▝▀▘ ▝▘ ▝▘ ▝▘ ▝▘ ▝▀▀ █ ▝▘▀▝▘ ▀▀▝▘▐▌▀▘ | |
// █▌ ▐▌ | |
// ---------------------------------------------------------------- | |
"": { | |
// ---------------------------------------------------------------- | |
// ▗▄ ▄▖ ▗▖ █ ▄▄ █ █ | |
// ▐█ █▌ ▐▌ ▀ ▐▛▀ ▀ ▐▌ ▀ | |
// ▐███▌ ▟█▙ ▟█▟▌ ██ ▐███ ██ ▟██▖ ▟██▖▐███ ██ ▟█▙ ▐▙██▖▗▟██▖ | |
// ▐▌█▐▌▐▛ ▜▌▐▛ ▜▌ █ ▐▌ █ ▐▛ ▘ ▘▄▟▌ ▐▌ █ ▐▛ ▜▌▐▛ ▐▌▐▙▄▖▘ | |
// ▐▌▀▐▌▐▌ ▐▌▐▌ ▐▌ █ ▐▌ █ ▐▌ ▗█▀▜▌ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌ ▀▀█▖ | |
// ▐▌ ▐▌▝█▄█▘▝█▄█▌▗▄█▄▖ ▐▌ ▗▄█▄▖▝█▄▄▌▐▙▄█▌ ▐▙▄ ▗▄█▄▖▝█▄█▘▐▌ ▐▌▐▄▄▟▌ | |
// ▝▘ ▝▘ ▝▀▘ ▝▀▝▘▝▀▀▀▘ ▝▘ ▝▀▀▀▘ ▝▀▀ ▀▀▝▘ ▀▀ ▝▀▀▀▘ ▝▀▘ ▝▘ ▝▘ ▀▀▀ | |
// ---------------------------------------------------------------- | |
// Change case | |
"Q": { | |
"c": recordChange( "textPowerTools.changeCaseToCamelCase" ), | |
"d": recordChange( "textPowerTools.changeCaseToConstantCase" ), | |
"f": recordChange( [ | |
"editor.action.transformToLowercase", | |
"cursorLeft", | |
"modalEditor.toUpperCase" | |
] ), | |
"l": recordChange( "modalEditor.toLowerCase" ), | |
"L": recordChange( "editor.action.transformToLowercase" ), | |
"p": recordChange( "textPowerTools.changeCaseToPascalCase" ), | |
"s": recordChange( "textPowerTools.changeCaseToSnakeCase" ), | |
"t": recordChange( "extension.capitalizeTitle" ), | |
"T": recordChange( "editor.action.transformToTitlecase" ), | |
"u": recordChange( "modalEditor.toUpperCase" ), | |
"U": recordChange( "editor.action.transformToUppercase" ), | |
}, | |
// Change selection and to end of line | |
// TODO: swap cursor to beginning of selection | |
"c": recordChange( [ | |
{ command: "deleteRight", when: "_ctx.selection.isEmpty" }, | |
{ command: "modalEditor.cut", when: "!_ctx.selection.isEmpty" }, | |
"modalEditor.setInsertMode" | |
] ), | |
"C": recordChange( [ | |
"modalEditor.clearSelections", | |
"cursorLineEndSelect", | |
"editor.action.clipboardCutAction", | |
"modalEditor.setInsertMode" | |
] ), | |
// Delete selection and to end of line | |
"d": recordChange( [ | |
{ command: "deleteRight", when: "_ctx.selection.isEmpty" }, | |
{ command: "modalEditor.cut", when: "!_ctx.selection.isEmpty" }, | |
"modalEditor.setNormalMode" | |
] ), | |
"D": recordChange( [ | |
"modalEditor.clearSelections", | |
"cursorLineEndSelect", | |
"editor.action.clipboardCutAction", | |
"modalEditor.setNormalMode" | |
] ), | |
// Replace character | |
"r": { | |
// Wildcard character | |
"": recordChange( [ | |
{ | |
command: "modalEditor.transform", | |
computedArgs: true, | |
args: `{ transformer: text => _ctx.keys.charAt(_ctx.keys.length-1).repeat(text.length) }`, | |
when: "!_ctx.selection.isEmpty" | |
}, | |
{ | |
command: [ | |
"cursorRightSelect", | |
{ | |
command: "modalEditor.transform", | |
computedArgs: true, | |
args: `{ transformer: text => _ctx.keys.charAt(_ctx.keys.length-1).repeat(text.length) }`, | |
}, | |
"cursorLeft" | |
], when: "_ctx.selection.isEmpty" | |
}, | |
] ) | |
}, | |
// Built-in quick-fix action | |
"q": "editor.action.quickFix", | |
// Undo and Redo | |
"u": repeatable( "undo" ), | |
"U": repeatable( "redo" ), | |
// Join lines | |
",": recordChange( repeatable( "editor.action.joinLines" ) ), | |
// TODO: move these to proper heading | |
// Select next/prev match of highligh | |
// ">": "editor.action.addSelectionToNextFindMatch", | |
// "<": "editor.action.addSelectionToPreviousFindMatch", | |
"R": "bookmarks.toggle", | |
">": "bookmarks.jumpToNext", | |
"<": "bookmarks.jumpToPrevious", | |
"=": "editor.toggleFold", | |
// Insert at cursor (or at line beginning) | |
"i": recordChange( "modalEditor.setInsertMode" ), | |
"I": recordChange( [ "cursorLineStart", "modalEditor.setInsertMode" ] ), | |
// Insert after cursor (or at line ending) | |
"a": recordChange( [ | |
{ command: "cursorRight", when: "_ctx.pos.character < _ctx.lineAt(_ctx.pos).text.length" }, | |
"modalEditor.setInsertMode" | |
] ), | |
"A": recordChange( [ "cursorLineEnd", "modalEditor.setInsertMode" ] ), | |
// Open line below and above | |
"o": recordChange( [ | |
// { command: ["editor.action.insertLineAfter", "modalEditor.setInsertMode"], when: "_ctx.languageId != 'markdown'" }, | |
// { command: ["editor.action.insertLineAfter", "modalEditor.setInsertMode"], when: "_ctx.languageId != 'markdown'" }, | |
"editor.action.insertLineAfter", "modalEditor.setInsertMode", | |
] ), | |
"O": recordChange( [ "editor.action.insertLineBefore", "modalEditor.setInsertMode" ] ), | |
// Repeat previous edit and jump | |
".": { command: "modalEditor.replayRecord", args: "change" }, | |
"J": { command: "modalEditor.replayRecord", args: "motion" }, | |
// Edit numbers and expressions | |
"-": "editor.emmet.action.decrementNumberByOne", | |
"_": "editor.emmet.action.incrementNumberByOne", | |
"+": "editor.emmet.action.evaluateMathExpression", | |
"!": "extension.toggleBool", | |
// Align selection | |
"&": "simple-alignment.align", | |
// ---------------------------------------------------------------- | |
// ▄ █ | |
// ▐█▌ ▐▌ ▀ | |
// ▐█▌ ▟██▖▐███ ██ ▟█▙ ▐▙██▖▗▟██▖ | |
// █ █ ▐▛ ▘ ▐▌ █ ▐▛ ▜▌▐▛ ▐▌▐▙▄▖▘ | |
// ███ ▐▌ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌ ▀▀█▖ | |
// ▗█ █▖▝█▄▄▌ ▐▙▄ ▗▄█▄▖▝█▄█▘▐▌ ▐▌▐▄▄▟▌ | |
// ▝▘ ▝▘ ▝▀▀ ▀▀ ▝▀▀▀▘ ▝▀▘ ▝▘ ▝▘ ▀▀▀ | |
// ---------------------------------------------------------------- | |
// Go group | |
"g": { | |
"g": { command: "modalEditor.gotoLine", computedArgs: true, args: "_ctx.count || 1" }, | |
"b": "cursorBottom", | |
"d": "editor.action.revealDefinition", | |
"r": "editor.action.goToReferences", | |
"n": "workbench.action.nextEditor", | |
"p": "workbench.action.previousEditor", | |
".": "workbench.action.navigateToLastEditLocation", | |
"f": { command: "workbench.action.quickOpen", args: "@:" }, | |
"u": "editor.action.openLink", | |
"k": recordMotion( { command: "cursorMove", args: { to: "up", by: "line" } } ), | |
"l": recordMotion( { command: "cursorMove", args: { to: "down", by: "line" } } ), | |
"s": recordMotion( findText( false, false, false, true, "\\.|\\?|!" ) ), | |
"S": recordMotion( findText( false, false, true, true, "\\.|\\?|!" ) ), | |
"a": recordMotion( findText( false, false, false, false, " " ) ), | |
"A": recordMotion( findText( false, false, true, true, " |\\(" ) ), | |
}, | |
// Peek group | |
"G": { | |
"k": "editor.action.showHover", | |
"d": "editor.action.revealDefinitionAside", | |
"p": "editor.action.peekDefinition", // Close with shift-esc | |
"l": "editor.action.findReferences", | |
}, | |
// Select inner object group | |
// - rest of indentation (from line down) | |
// - rest of paragraph (and rest of other things) | |
// - value of key-value pair or rhs of assignment | |
// - key of key-value pair or lhs of assignment | |
// - number | |
// - to end of line minus newline | |
// - fenced code block | |
// - js regex (between slashes) | |
// - column of text | |
// - subword (like iw, but treating -, _, and . as word delimiters and only part of camelCase) | |
// - rework the argument one below | |
// - semantic objects: function, class, block, method, loop, etc. | |
// - https://www.cursorless.org/docs/#primitive-targets | |
// - attribute, call, callee, class name, class | |
// - end tag, function name, if statement, lambda | |
// - list, map, name, regex, start tag, tags, type annotation | |
// - unit (px in 100px) | |
// - item, key, value | |
// - lots more (e.g., take head and take tail, take three funk, etc.) | |
// ",": "collectionItem", | |
// "sk": "collectionKey", | |
// ".": "functionCall", | |
// "sc": "class", | |
// "sv": "value", | |
// "sp": "nonWhitespaceSequence", | |
// "ss": "boundedNonWhitespaceSequence", | |
// if state, key, lambda, list, map | |
"e": { | |
"f": cursorlessSelection( "namedFunction" ), | |
"w": [ "cursorWordEndRight", "cursorWordStartLeftSelect" ], | |
"i": [ "vscode-select-by-indent.select-inner", "cursorLeftSelect" ], | |
// "p": [ | |
// { command: "cursorMove", args: { to: "prevBlankLine" } }, | |
// { command: "cursorRight", when: "_ctx.lineAt(_ctx.pos.line).text.length === 0"}, | |
// { command: "cursorMove", args: { to: "nextBlankLine", select: true } }, | |
// { command: "cursorLeftSelect", when: "_ctx.lineAt(_ctx.pos.line).text.length === 0"}, | |
// ], | |
"c": cursorlessSelection( "condition" ), | |
"b": cursorlessSelection( "branch" ), | |
// "k": cursorlessSelection("block"), | |
"p": cursorlessSelection( "paragraph" ), | |
"a": cursorlessSelection( "argumentOrParameter" ), | |
"s": cursorlessSelection( "sentence" ), | |
"u": cursorlessSelection( "url" ), | |
";": cursorlessSelection( "statement" ), | |
"/": cursorlessSelection( "comment" ), | |
'"': selectPair( true, '"' ), | |
" ": selectPair( true, " " ), | |
"'": selectPair( true, "'" ), | |
"`": selectPair( true, "`" ), | |
"*": selectPair( true, "*" ), | |
"_": selectPair( true, "_" ), | |
"~": selectPair( true, "~" ), | |
"<": selectPair( true, "<", ">" ), | |
">": selectPair( true, ">", "<" ), | |
"(": selectPair( true, "(", ")" ), | |
")": selectPair( true, ")", "(" ), | |
"{": selectPair( true, "{", "}" ), | |
"}": selectPair( true, "}", "{" ), | |
"[": selectPair( true, "[", "]" ), | |
"]": selectPair( true, "]", "[" ), | |
}, | |
// Select around object group | |
"E": { | |
"w": [ "cursorWordStartLeft", "cursorWordEndRightSelect" ], | |
"W": [ "editor.action.smartSelect.expand" ], | |
"i": [ "vscode-select-by-indent.select-outer", "cursorLeftSelect" ], | |
"l": [ "expandLineSelection", "cursorLeftSelect" ], | |
"p": [ | |
{ command: "cursorMove", args: { to: "prevBlankLine" } }, | |
{ command: "cursorRight", when: "_ctx.lineAt(_ctx.pos.line).text.length === 0" }, | |
{ command: "cursorMove", args: { to: "nextBlankLine", select: true } }, | |
], | |
"a": [ | |
findText( false, false, true, true, "[\(,]" ), | |
{ command: "cursorRight", when: "_ctx.lineAt(_ctx.pos).text[_ctx.pos.character] == '('" }, | |
findText( true, true, false, true, "[\),]" ), | |
{ command: "cursorLeftSelect", when: "_ctx.lineAt(_ctx.pos).text[_ctx.pos.character] == ')'" }, | |
], | |
"s": [ findText( false, false, true, true, "^$|\\.|\\?|!" ), findText( false, true, false, true, "^$|\\.|\\?|!" ) ], | |
// "b": ["editor.action.selectToBracket", "cursorLeftSelect"], | |
'"': selectPair( false, '"' ), | |
"'": selectPair( false, "'" ), | |
"`": selectPair( false, "`" ), | |
"*": selectPair( false, "*" ), | |
"_": selectPair( false, "_" ), | |
"~": selectPair( false, "~" ), | |
"<": selectPair( false, "<", ">" ), | |
">": selectPair( false, ">", "<" ), | |
"(": selectPair( false, "(", ")" ), | |
")": selectPair( false, ")", "(" ), | |
"{": selectPair( false, "{", "}" ), | |
"}": selectPair( false, "}", "{" ), | |
"[": selectPair( false, "[", "]" ), | |
"]": selectPair( false, "]", "[" ), | |
}, | |
// Leader group | |
" ": { | |
"s": "workbench.action.files.save", | |
"r": "editor.action.rename", | |
"R": "editor.action.refactor", | |
"l": "turboConsoleLog.displayLogMessage", | |
"w": [ "workbench.action.newGroupRight", "workbench.action.quickOpen" ], | |
"e": "workbench.action.toggleEditorWidths", | |
"f": "workbench.action.findInFiles", | |
"n": "extension.advancedNewFile", | |
"q": [ "cSpell.goToPreviousSpellingIssueAndSuggest", "cursorUndo" ], | |
"Q": [ "cSpell.goToNextSpellingIssueAndSuggest", "cursorUndo" ], | |
"h": "editor.action.selectHighlights", | |
"b": { command: "latex-workshop.build", when: "_ctx.languageId == 'latex'" }, | |
"v": { command: "latex-workshop.viewExternal", when: "_ctx.languageId == 'latex'" }, | |
"c": { command: "latex-workshop.clean", when: "_ctx.languageId == 'latex'" }, | |
// Send the current file to the terminal | |
"x": [ | |
"workbench.action.terminal.new", | |
{ command: "workbench.action.terminal.sendSequence", args: { text: "'${file}'" } }, | |
], | |
// maybe sub mode to close things individually ("workbench.action.terminal.toggleTerminal") | |
"z": [ "workbench.action.closeSidebar", "workbench.action.closePanel" ], | |
"t": [ "workbench.action.files.save", "workbench.action.terminal.focus" ], | |
// "/": { command: "editor.actions.findWithArgs", args: { isRegex: true } }, | |
"/": findWidget( false, true, false ), | |
"k": "modalEditor.importPreset", | |
"d": "editor.action.copyLinesDownAction", // debugger mode? :debug | |
"g": "workbench.action.gotoSymbol", | |
// "y": [{ command: "modalEditor.yank", args: { register: "" } }, "modalEditor.setNormalMode"], | |
// "p": { command: "modalEditor.paste", args: { register: "" } }, | |
// "P": { command: "modalEditor.paste", args: { register: "", before: true } }, | |
"?": "workbench.action.showCommands", | |
'"': { command: "editor.action.insertSnippet", args: { snippet: '"${TM_SELECTED_TEXT}"' } }, | |
"'": { command: "editor.action.insertSnippet", args: { snippet: "'${TM_SELECTED_TEXT}'" } }, | |
"`": { command: "editor.action.insertSnippet", args: { snippet: "`${TM_SELECTED_TEXT}`" } }, | |
"*": { command: "editor.action.insertSnippet", args: { snippet: "*${TM_SELECTED_TEXT}*" } }, | |
"_": { command: "editor.action.insertSnippet", args: { snippet: "_${TM_SELECTED_TEXT}_" } }, | |
"~": { command: "editor.action.insertSnippet", args: { snippet: "~${TM_SELECTED_TEXT}~" } }, | |
"<": { command: "editor.action.insertSnippet", args: { snippet: "<${TM_SELECTED_TEXT}>" } }, | |
"(": { command: "editor.action.insertSnippet", args: { snippet: "(${TM_SELECTED_TEXT})" } }, | |
"[": { command: "editor.action.insertSnippet", args: { snippet: "[${TM_SELECTED_TEXT}]" } }, | |
"{": { command: "editor.action.insertSnippet", args: { snippet: "{${TM_SELECTED_TEXT}}" } }, | |
// " ": { | |
// // "r": "jupyter.runcurrentcelladvance", | |
// } | |
}, | |
// View group (only center for now) | |
"z": "center-editor-window.center", | |
// Next and previous diagnostic | |
"[": "editor.action.marker.prev", | |
"]": "editor.action.marker.next", | |
// Navigate forward and backward through locations | |
"{": "workbench.action.navigateBack", | |
"}": "workbench.action.navigateForward", | |
// Copy/yank selection | |
// "y": ["modalEditor.yank", "modalEditor.setNormalMode"], | |
"y": [ "editor.action.clipboardCopyAction", "modalEditor.setNormalMode" ], | |
// "Y": ["modalEditor.clearSelections", "cursorLineEndSelect", "modalEditor.yank", "modalEditor.clearSelections"], | |
"Y": [ "modalEditor.clearSelections", "cursorLineEndSelect", "editor.action.clipboardCopyAction", "modalEditor.clearSelections" ], | |
// Paste after and before | |
// "p": "modalEditor.paste", | |
"p": "editor.action.clipboardPasteAction", | |
// "P": { command: "modalEditor.paste", args: { before: true } }, | |
"P": { command: "editor.action.clipboardPasteAction", args: { before: true } }, | |
// Change mode | |
":": "modalEditor.setCommandMode", | |
"s": "modalEditor.setSelectMode", | |
// Select to end of line | |
"S": "cursorLineEndSelect", | |
// Selections | |
"x": repeatable( "expandLineSelection" ), | |
// Search and replace and search using selection (alternative: "actions.findWithSelection") | |
"/": findWidget( false, false, false ), | |
"?": "editor.action.startFindReplaceAction", | |
"*": "editor.action.nextSelectionMatchFindAction", | |
// Repeat search forward | |
"n": [ | |
// Move right to find the next instead of current | |
{ command: "cursorRight", when: "!_ctx.selection.isEmpty" }, | |
// Find next | |
"editor.action.nextMatchFindAction", | |
// Move left because of inclusive range | |
{ command: "cursorLeftSelect", when: "!_ctx.selection.isEmpty" }, | |
"closeFindWidget" | |
], | |
// Repeat search backward | |
"N": [ | |
// Move right to find the next instead of current | |
{ command: "cursorRight", when: "!_ctx.selection.isEmpty" }, | |
// Find previous | |
"editor.action.previousMatchFindAction", | |
// Move left because of inclusive range | |
{ command: "cursorLeftSelect", when: "!_ctx.selection.isEmpty" }, | |
"closeFindWidget" | |
], | |
// Add cursor at end/beginning of each line in selection | |
// TODO: change to I and A? | |
"|": "editor.action.insertCursorAtEndOfEachLineSelected", | |
"\\": [ "editor.action.insertCursorAtEndOfEachLineSelected", "cursorHome" ], | |
// Add and clear cursors | |
"0": "removeSecondaryCursors", | |
"(": repeatable( "editor.action.insertCursorAbove" ), | |
")": repeatable( "editor.action.insertCursorBelow" ), | |
// Macros | |
"m": "blah", | |
"M": "blah", | |
}, | |
// ---------------------------------------------------------------- | |
// _ _ _ | |
// | \ | | ___ _ __ _ __ ___ __ _| | | |
// | \| |/ _ \| '__| '_ ` _ \ / _` | | | |
// | |\ | (_) | | | | | | | | (_| | | | |
// |_| \_|\___/|_| |_| |_| |_|\__,_|_| | |
// _ __ | |
// | |/ /___ _ _ _ __ ___ __ _ _ __ | |
// | ' // _ \ | | | '_ ` _ \ / _` | '_ \ | |
// | . \ __/ |_| | | | | | | (_| | |_) | | |
// |_|\_\___|\__, |_| |_| |_|\__,_| .__/ | |
// |___/ |_| | |
// ---------------------------------------------------------------- | |
"normal": { | |
// ---------------------------------------------------------------- | |
// _ _ __ __ _ _ | |
// | \ | | | \/ | ___ | |_(_) ___ _ __ ___ | |
// | \| | | |\/| |/ _ \| __| |/ _ \| '_ \/ __| | |
// | |\ |_ | | | | (_) | |_| | (_) | | | \__ \ | |
// |_| \_(_) |_| |_|\___/ \__|_|\___/|_| |_|___/ | |
// ---------------------------------------------------------------- | |
// Sneak forward and backward given two characters | |
// "v": { "": jumpMotion(false, false, false, 2) }, | |
// "V": { "": jumpMotion(false, false, true, 2) }, | |
"v": "leap.find", | |
"V": { "": jumpMotion( false, false, false, 2 ) }, | |
// Jump till next typed character | |
"t": jumpMotion( true, false, false, 1 ), | |
"T": jumpMotion( true, false, true, 1 ), | |
// Jump past next typed character | |
"f": jumpMotion( false, false, false, 1 ), | |
"F": jumpMotion( false, false, true, 1 ), | |
// Forward to start of next word | |
"w": repeatable( [ | |
"modalEditor.clearSelections", | |
"cursorLeft", | |
findText( false, false, false, true, "\\w" ), | |
"editor.action.addSelectionToNextFindMatch", | |
// { command: "cursorLeftSelect", when: "_ctx.lineAt(_ctx.pos).text[_ctx.pos.character] !== ' '" }, | |
] ), | |
"W": repeatable( [ "modalEditor.clearSelections", "cursorWordPartRightSelect" ] ), | |
// Backward to start of previous word | |
"b": repeatable( [ "modalEditor.clearSelections", "cursorWordStartLeftSelect" ] ), | |
"B": repeatable( [ "modalEditor.clearSelections", "cursorWordPartLeftSelect" ] ), | |
// Jump to first character or first column and jump to end | |
"h": "cursorHome", | |
"'": "cursorLineEnd", | |
// Jump to previous and next blank line | |
"H": { command: "cursorMove", args: { to: "prevBlankLine" } }, | |
'"': { command: "cursorMove", args: { to: "nextBlankLine" } }, | |
// Single character motions | |
"j": repeatable( "cursorLeft" ), | |
"k": repeatable( "cursorUp" ), | |
"l": repeatable( "cursorDown" ), | |
";": repeatable( "cursorRight" ), | |
// Jump up/down 10 lines | |
"K": [ { command: "cursorMove", args: { to: "up", value: 10 } }, "center-editor-window.center" ], | |
"L": [ { command: "cursorMove", args: { to: "down", value: 10 } }, "center-editor-window.center" ], | |
// Jump forward/backward to digit | |
"`": [ "cursorWordStartRight", findText( false, false, false, true, "[0-9]" ) ], | |
"~": [ "cursorWordStartLeft", findText( false, false, true, true, "[0-9]" ) ], | |
}, | |
// ---------------------------------------------------------------- | |
// ____ _ _ _ | |
// / ___| ___| | ___ ___| |_(_) ___ _ __ | |
// \___ \ / _ \ |/ _ \/ __| __| |/ _ \| '_ \ | |
// ___) | __/ | __/ (__| |_| | (_) | | | | | |
// |____/ \___|_|\___|\___|\__|_|\___/|_| |_| | |
// _ __ | |
// | |/ /___ _ _ _ __ ___ __ _ _ __ | |
// | ' // _ \ | | | '_ ` _ \ / _` | '_ \ | |
// | . \ __/ |_| | | | | | | (_| | |_) | | |
// |_|\_\___|\__, |_| |_| |_|\__,_| .__/ | |
// |___/ |_| | |
// ---------------------------------------------------------------- | |
"select": { | |
// ---------------------------------------------------------------- | |
// ____ __ __ _ _ | |
// / ___| | \/ | ___ | |_(_) ___ _ __ ___ | |
// \___ \ | |\/| |/ _ \| __| |/ _ \| '_ \/ __| | |
// ___) | | | | | (_) | |_| | (_) | | | \__ \ | |
// |____(_) |_| |_|\___/ \__|_|\___/|_| |_|___/ | |
// ---------------------------------------------------------------- | |
// // Sneak forward and backward given two characters | |
// "v": { "": jumpMotion(false, true, false, 2) }, | |
// "V": { "": jumpMotion(false, true, true, 2) }, | |
"V": { "": jumpMotion( false, true, false, 2 ) }, | |
// Jump till next typed character | |
"t": jumpMotion( true, true, false, 1 ), | |
"T": jumpMotion( true, true, true, 1 ), | |
// Jump past next typed character | |
"f": jumpMotion( false, true, false, 1 ), | |
"F": jumpMotion( false, true, true, 1 ), | |
// Forward to start of next word | |
"w": repeatable( [ | |
findText( false, true, false, true, "\\w" ), | |
"cursorWordStartRightSelect", | |
// { command: "cursorLeftSelect", when: "_ctx.lineAt(_ctx.pos).text[_ctx.pos.character] !== ' '" }, | |
] ), | |
"W": repeatable( [ "modalEditor.clearSelections", "cursorWordPartRightSelect" ] ), | |
// Backward to start of previous word | |
"b": repeatable( "cursorWordStartLeftSelect" ), | |
"B": repeatable( "cursorWordPartLeftSelect" ), | |
// Jump to first character or first column and jump to end | |
"h": "cursorHomeSelect", | |
"'": "cursorLineEndSelect", | |
// Jump to previous and next blank line | |
"H": { command: "cursorMove", args: { to: "prevBlankLine", select: true } }, | |
'"': { command: "cursorMove", args: { to: "nextBlankLine", select: true } }, | |
// Single character motions | |
"j": repeatable( "cursorLeftSelect" ), | |
"k": repeatable( "cursorUpSelect" ), | |
"l": repeatable( "cursorDownSelect" ), | |
";": repeatable( "cursorRightSelect" ), | |
// Jump up/down 10 lines | |
"K": [ { command: "cursorMove", args: { to: "up", select: true, value: 10 } }, "center-editor-window.center" ], | |
"L": [ { command: "cursorMove", args: { to: "down", select: true, value: 10 } }, "center-editor-window.center" ], | |
// Add cursor at end/beginning of each line in selection | |
"\\": [ | |
"modalEditor.setNormalMode", | |
"editor.action.insertCursorAtEndOfEachLineSelected", | |
{ command: "cursor-eraser.erase-match", args: "^$" }, | |
], | |
"|": [ | |
"modalEditor.setNormalMode", | |
"editor.action.insertCursorAtEndOfEachLineSelected", | |
{ command: "cursor-eraser.erase-match", args: "^$" }, | |
"cursorHome", | |
], | |
}, | |
// ---------------------------------------------------------------- | |
// ____ _ | |
// / ___|___ _ __ ___ _ __ ___ __ _ _ __ __| | | |
// | | / _ \| '_ ` _ \| '_ ` _ \ / _` | '_ \ / _` | | |
// | |__| (_) | | | | | | | | | | | (_| | | | | (_| | | |
// \____\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|\__,_| | |
// _ __ | |
// | |/ /___ _ _ _ __ ___ __ _ _ __ | |
// | ' // _ \ | | | '_ ` _ \ / _` | '_ \ | |
// | . \ __/ |_| | | | | | | (_| | |_) | | |
// |_|\_\___|\__, |_| |_| |_|\__,_| .__/ | |
// |___/ |_| | |
// ---------------------------------------------------------------- | |
"command": { | |
"write": "workbench.action.files.save", | |
"w": "workbench.action.files.save", | |
"sort": "editor.action.sortLinesAscending", | |
"sortrev": "editor.action.sortLinesDescending", | |
"reverse": "textPowerTools.reverseLines", | |
"shuffle": "textPowerTools.shuffleLines", | |
"reflow": "rewrap.rewrapComment", | |
"r": "rewrap.rewrapComment", | |
"table": "textPowerTools.formatContentAsTableByPipeWithPadding", | |
"t": "textPowerTools.formatContentAsTableByPipeWithPadding", | |
"numbers": "textPowerTools.insertDecimalNumbers", | |
"n": "textPowerTools.insertDecimalNumbers", | |
"lorem": "textPowerTools.generateLoremIpsumParagraph", | |
"days": "textPowerTools.insertShotEnglishDayNamesSequence", | |
"months": "textPowerTools.insertShotEnglishMonthNamesSequence", | |
"longdays": "textPowerTools.insertLongEnglishDayNamesSequence", | |
"longmonths": "textPowerTools.insertLongEnglishMonthNamesSequence", | |
"fake": "textPowerTools.generateFakeData", | |
"bclear": "bookmarks.clear", | |
} | |
}; |
I just started messing around with Modal Editor a week ago. I am using this as my daily driver (and I expect to do so going forward), but I am also expecting to make quite a few changes For example, I thought I liked having forward ('d') and backward ('s') next to each other. But I am leaning toward switching these back to helix/vim defaults.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi @anthonyjclark, thanks for creating this and posting it to DC Modal!
Are you using this as a daily driver already? How much of Helix'es keybindings are roughly covered?