Skip to content

Instantly share code, notes, and snippets.

@anthonyjclark
Last active July 18, 2024 23:28
Show Gist options
  • Save anthonyjclark/1de4882c13ffd3c61863bc657557253e to your computer and use it in GitHub Desktop.
Save anthonyjclark/1de4882c13ffd3c61863bc657557253e to your computer and use it in GitHub Desktop.
Custom bindings for DCsunset/vscode-modal-editor
// ----------------------------------------------------------------
// ▗▄▄▄▖ █
// ▐▛▀▀▘ ▐▌ ▀
// ▐▌ ▝█ █▘▐███ ▟█▙ ▐▙██▖▗▟██▖ ██ ▟█▙ ▐▙██▖▗▟██▖
// ▐███ ▐█▌ ▐▌ ▐▙▄▟▌▐▛ ▐▌▐▙▄▖▘ █ ▐▛ ▜▌▐▛ ▐▌▐▙▄▖▘
// ▐▌ ▗█▖ ▐▌ ▐▛▀▀▘▐▌ ▐▌ ▀▀█▖ █ ▐▌ ▐▌▐▌ ▐▌ ▀▀█▖
// ▐▙▄▄▖ ▟▀▙ ▐▙▄ ▝█▄▄▌▐▌ ▐▌▐▄▄▟▌▗▄█▄▖▝█▄█▘▐▌ ▐▌▐▄▄▟▌
// ▝▀▀▀▘▝▀ ▀▘ ▀▀ ▝▀▀ ▝▘ ▝▘ ▀▀▀ ▝▀▀▀▘ ▝▀▘ ▝▘ ▝▘ ▀▀▀
// ----------------------------------------------------------------
// 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",
}
};
@nightscape
Copy link

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?

@anthonyjclark
Copy link
Author

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