28 February 2021
Use Emacs key bindings in VS Code
I’ve been working in Visual Studio Code a lot these days. I used to write code entirely in Emacs, except when working with Java and the need for a heavier IDE would present itself. However, once I started writing a lot of TypeScript all the checking, completion, refactoring, etc. were too alluring to ignore.
I really like how a lot of the Emacs key bindings just work on Mac OSX. It makes editing text in any old random text box (such as TextEdit or a web browser) that much quicker, e.g.,
ctrl + e
- move to end of linectrl + a
- move to beginning of linectrl + p
- move to previous linectrl + n
- move to next linectrl + k
- kill the rest of the current linectrl + y
- paste what was last “killed”
This is a great head start to using those key bindings in another IDE, but it’s not enough. Naturally, there are extensions to solve this problem.
I looked for something that would be simple and would give me all the core commands I wanted while not getting in the way of the other useful things VSCode has to offer. I landed on Awesome Emacs Keymap (emacs-mcx).
After installing it, almost everything seemed right, but—like any Emacs user who is used to customizing things—I was running into a couple design decisions by the extension that I wanted to change. My main issue was with the find widget closing when I didn’t it want to. I achieved these changes with the following steps:
-
In VS Code go to Code > Preferences > Keyboard Shortcuts [⌘K ⌘S]
-
Switch to JSON view (flip doc icon)
-
Take a look at the keybindings.json file from the emacs-mcx repo to see the rules it applies. As seen below, any commands that include “meta” in this file should be “alt” in your keyboard shortcuts JSON file.
-
Enter your overrides! In my examples below, I’m removing key binding rules from the extension by putting a “-” in front of the command name.
Here are the overrides that I have applied in my environment:
// Place your key bindings in this file to override the defaults
[
// allow arrow keys to work in the find widget
{
"key": "right",
"command": "-emacs-mcx.isearchExit"
},
{
"key": "left",
"command": "-emacs-mcx.isearchExit"
},
{
"key": "up",
"command": "-emacs-mcx.isearchExit"
},
{
"key": "down",
"command": "-emacs-mcx.isearchExit"
},
// allow ctrl+f to find next in the find widget
{
"key": "ctrl+f",
"command": "-emacs-mcx.isearchExit",
"when": "editorFocus && findWidgetVisible"
},
// allow other stuff to functional normally in the find widget
{
"key": "ctrl+b",
"command": "-emacs-mcx.isearchExit",
"when": "editorFocus && findWidgetVisible"
},
{
"key": "ctrl+p",
"command": "-emacs-mcx.isearchExit",
"when": "editorFocus && findWidgetVisible"
},
{
"key": "ctrl+n",
"command": "-emacs-mcx.isearchExit",
"when": "editorFocus && findWidgetVisible"
},
{
"key": "ctrl+a",
"command": "-emacs-mcx.isearchExit",
"when": "editorFocus && findWidgetVisible"
},
{
"key": "ctrl+e",
"command": "-emacs-mcx.isearchExit",
"when": "editorFocus && findWidgetVisible"
},
{
"key": "enter",
"command": "-emacs-mcx.isearchExit"
},
// allow curly quotes and ellipses characters on mac
{
"key": "alt+shift+[",
"command": "-emacs-mcx.backwardParagraph"
},
{
"key": "alt+shift+]",
"command": "-emacs-mcx.forwardParagraph"
},
{
"key": "alt+;",
"command": "-editor.action.blockComment",
"when": "editorTextFocus && !config.emacs-mcx.useMetaPrefixMacCmd && !editorReadonly"
},
{
"key": "alt+;",
"command": "-emacs-mcx.executeCommands",
"when": "editorFocus && findWidgetVisible && !config.emacs-mcx.useMetaPrefixMacCmd"
},
// stop backward kill word from adding to clipboard
{
"key": "alt+backspace",
"command": "-emacs-mcx.backwardKillWord",
"when": "editorTextFocus && !config.emacs-mcx.useMetaPrefixMacCmd && !editorReadonly"
}
]
The only snag I’ve encountered is that the extension has updated their commands a couple times since I first set this up, but when that happens I just check back to the keybindings.json file in their repo and updated my command names accordingly (last updated in this post on July 20, 2021).