Awesome Emacs Keymap (emacs-mcx)

April 4, 2026 · View on GitHub

Test and Build Post-build

Visual Studio Marketplace Version Visual Studio Marketplace Installs - Azure DevOps Extension Visual Studio Marketplace Rating

Open VSX Version Open VSX Downloads Open VSX Rating

Buy Me A Coffee Support me on Ko-fi GitHub Sponsors

Emacs emulation for Visual Studio Code

  • Emacs-like cursor movements and editing commands
  • Customizable meta key assignment (alt, ESC, ctrl+[, or command on macOS)
  • Shift-selection with movement commands
  • Search behavior like Emacs' isearch
  • Compatibility with VSCode's built-in multi cursor feature
  • Kill-ring integrated with the system clipboard
  • Mark-ring
  • Prefix argument
  • Rectangle commands
  • Text/position/rectangle registers
  • Sexp operations (with ParEdit commands)
  • Available both in Desktop and Web

It's inspired by Emacs Keymap by hiro-sun and its forks such as Emacs Friendly Keymap by Sebastian Zaha, Emacs Keymap Improved by rkwan94, and Emacs Friendly Keybindings (Neon) by NotKyon, but totally revamped and improved for better feature coverage, Emacs emulation, and usability (also getting rid of some bugs).

FAQ/Tips

The cursor cannot be moved on the find widget as the widget closes with movement keys.

It's an intended design that simulates the original Emacs' behavior. You can disable it with emacs-mcx.cursorMoveOnFindWidget option described below. See https://github.com/whitphx/vscode-emacs-mcx/issues/137 for the details about this topic.

i-search (C-s) is initialized with the currently selected string and the previous search is removed.

Setting editor.find.seedSearchStringFromSelection as "never" can change this behavior. It makes the find widget work similarly to Emacs.

Refs:

I want to customize the META key (ESC, ctrl+[, alt, or Command (⌘) key on macOS).

You can configure the key to be used as a META prefix with the following settings.

  • emacs-mcx.useMetaPrefixAlt (true by default)
  • emacs-mcx.useMetaPrefixEscape
  • emacs-mcx.useMetaPrefixCtrlLeftBracket
  • emacs-mcx.useMetaPrefixMacCmd

Read the extension settings section below for more details.

I want to use keyboard macro

Check out Keyboard Macro Beta by tshino (tshino.kb-macro) and its keybindings.

The extension has been broken!

Try the Developer: Reinstall Extension... command from the command palette to reinstall the extension. I fixed a problem by this way somehow.

I find a bug. I want a feature X to be implemented. I have a question.

Post a bug report or a feature request to GitHub Issues.

Extension settings

This extension has some custom settings named with a prefix emacs-mcx. You can configure those settings. (See this page to know how to change the settings.)

Configurable options of this extension are the followings.

emacs-mcx.moveBeginningOfLineBehavior

Determines the behavior of the move-beginning-of-line command. If set as 'vscode', it works as VSCode's native Home key behavior. If set as 'emacs', it works as the original Emacs' move-beginning-of-line.

emacs-mcx.moveEndOfLineBehavior

Determines the behavior of the move-end-of-line command. If set as 'vscode', it works as VSCode's native End key behavior. If set as 'emacs', it works as the original Emacs' move-end-of-line.

emacs-mcx.scrollUpCommandBehavior

Determines the behavior of the scroll-up-command command. If set as 'vscode', it works as VSCode's native Page Down key behavior. If set as 'emacs', it works as the original Emacs' scroll-up-command.

emacs-mcx.scrollDownCommandBehavior

Determines the behavior of the scroll-down-command command. If set as 'vscode', it works as VSCode's native Page Up key behavior. If set as 'emacs', it works as the original Emacs' scroll-down-command.

emacs-mcx.wordNavigationStyle

Determines the behavior of word navigation and word killing commands such as forward-word, backward-word, kill-word, and backward-kill-word. If set as 'vscode' (default), they work as VSCode's native word navigation. If set as 'emacs', they follow Emacs-like word boundary detection.

emacs-mcx.strictEmacsMove (deprecated)

If set as true, all emacs-mcx.moveBeginningOfLineBehavior, emacs-mcx.moveEndOfLineBehavior, emacs-mcx.scrollUpCommandBehavior, and emacs-mcx.scrollDownCommandBehavior are set to 'emacs'.

emacs-mcx.keepCursorInVisibleRange

If set as true, the cursor is kept in the visible range when scrolling up or down like Emacs.

emacs-mcx.emacsLikeTab

If set as true, tab key works as the Emacs' tab-to-tab-stop command.

emacs-mcx.useMetaPrefixEscape

If set as true, Escape key works as the Meta prefix like original Emacs. If set as false, Escape key works as cancel, the VSCode's native behavior. For example, if set as true, M-f (forward-word) can be issued by both alt+f and escape f.

The only exception is the commands which begin with M-g (M-g g, M-g n, M-g p). It is because VSCode can handle only up to two key strokes as the key bindings. So, as the special case, Escape g works as follows.

CommandDesc
Escape gJump to line (command palette)

emacs-mcx.useMetaPrefixCtrlLeftBracket

If set as true, ctrl+[ works as the Meta prefix like original Emacs.

emacs-mcx.useMetaPrefixAlt

If set as true, alt key (option (⌥) key on macOS) works as the Meta prefix.

emacs-mcx.useMetaPrefixMacCmd

If set as true, Command (⌘) key works as the Meta prefix on macOS. This option only works on macOS.

emacs-mcx.killRingMax

Configures the maximum number of kill ring entries. The default is 60.

emacs-mcx.killWholeLine

This simulates the original Emacs' kill-whole-line variable. The default is false.

emacs-mcx.cursorMoveOnFindWidget

If set to true, cursor move commands of this extension such as C-f and C-b are disabled when the find widget is focused, to allow the widget to keep open and the cursor to move on it.

emacs-mcx.enableOverridingTypeCommand

Prefix arguments do not work on character inputs with IMEs by default and you can set this config to true in order to enable it. Note that this config makes use of VSCode API's type command under the hood and can cause problems in some cases.

emacs-mcx.enableDigitArgument

Indicates whether M-<digit> (the emacs-mcx.digitArgument command) is enabled. Set false when M-<digit> conflicts with some other necessary commands. See https://github.com/whitphx/vscode-emacs-mcx/issues/1208 for the background.

emacs-mcx.shiftSelectMode

Toggle Shift Selection (Emacs shift-select-mode). When true, holding Shift while running movement commands starts mark mode to extend the selection; the default is false to avoid ctrl+shift+p conflicts with the Command Palette on Windows/Linux.

emacs-mcx.lineMoveVisual

When true, line-move moves point by visual lines (same as an Emacs variable line-move-visual).

emacs-mcx.paredit.parentheses

Key-value pairs of parentheses to be used in the ParEdit commands like the following example.

{
  "[": "]",
  "(": ")",
  "{": "}"
}

The parentheses pairs are inherited from a default configuration and merged in order: Default → User-defined global config → User-defined per-language. Each finer-grained config can override individual pair definitions from its parent. You can also override the default pairs or disable them by setting null as the value. For example:

"emacs-mcx.paredit.parentheses": {
  "<": ">",  // New pair
  "{": null,  // Override to disable
}
// This will result in the following configuration for files of the language:
// {
//   "[": "]",
//   "(": ")",
//   "<": ">"
// }

emacs-mcx.subwordMode

When true, word-oriented move and edit commands, including M-f, M-b, M-d will recognize subwords (same as the Emacs variable subword-mode). It can also be set as per-language flag, for example:

"[go]": {
  "emacs-mcx.subwordMode": true
}

emacs-mcx.debug.*

Configurations for debugging.

'when' clause context

This extension provides some contexts that you can refer to in "when" clauses of your keybindings.json.

emacs-mcx.inMarkMode

boolean

This indicates whether mark-mode is enabled.

emacs-mcx.acceptingArgument

boolean

This indicates the editor is accepting argument input following C-u.

emacs-mcx.prefixArgumentExists (experimental)

boolean

This indicates if a prefix argument exists. Use this boolean context to check the existence of a prefix argument, instead of using emacs-mcx.prefixArgument with null check.

emacs-mcx.prefixArgument (experimental)

number | undefined

This is a currently input prefix argument.

Keybindings

Alt key is mapped to the Meta prefix (M) by default and you can configure for Escape, ctrl+[, or Command (⌘) key (macOS only) to work as it with the settings above.

Move commands

CommandPrefix argumentDesc
C-fMove forward (forward-char)
C-bMove backward (backward-char)
C-nMove to the next line (next-line)
C-pMove to the previous line (previous-line)
C-aMove to the beginning of line (move-beginning-of-line)
C-eMove to the end of line (move-end-of-line)
M-fMove forward by one word unit (forward-word)
M-bMove backward by one word unit (backward-word)
C-<right>, M-<right>This command (right-word) behaves like M-f
C-<left>, M-<left>This command (left-word) behaves like M-b
M-mMove (forward or back) to the first non-whitespace character on the current line (back-to-indentation)
C-vScroll down by one screen unit (scroll-up-command)
M-vScroll up by one screen unit (scroll-down-command)
M-S-[ (M-{ with US keyboard), C-<up>Move back to previous paragraph beginning (backward-paragraph)
M-S-] (M-} with US keyboard), C-<down>Move forward to next paragraph end (forward-paragraph)
M-S-, (M-< with US keyboard)Move to the top of the buffer (beginning-of-buffer)
M-S-. (M-> with US keyboard)Move to the end of the buffer (end-of-buffer)
M-g g (M-g M-g)Read a number n and move point to the beginning of line number n (goto-line)
M-g n (M-g M-n, C-x ` )Jump to next error
M-g p (M-g M-p)Jump to previous error
C-lCenter screen on current line (recenter-top-bottom)

Search Commands

CommandDesc
C-sIncremental search forward (isearch-forward).
C-rIncremental search backward (isearch-backward).
C-M-sBegin incremental regexp search (isearch-forward-regexp).
C-M-rBegin reverse incremental regexp search (isearch-backward-regexp).
M-S-5 (M-% with US keyboard)Replace (query-replace)
C-M-S-5 (C-M-% with US keyboard)Replace with regexp (query-replace-regexp)
C-M-nAdd selection to next find match
C-M-pAdd selection to previous find match
M-s oOpen Quick Search, which is like Emacs' occur command.
M-cToggles the case sensitivity of the find widget.

Edit commands

CommandPrefix argumentDesc
C-dDelete right (DEL)
C-hDelete left (BACKSPACE)
M-\Delete spaces and tabs around point (delete-horizontal-space).
M-SPCCycle spacing: first reduces whitespace to one space, then deletes all whitespace, then restores original (cycle-spacing). alt+space is ignored by VSCode (issue), so this keybinding only works with other Meta key options (Escape, Ctrl+[, or Cmd on macOS), which need to be enabled via emacs-mcx.useMetaPrefixEscape, emacs-mcx.useMetaPrefixCtrlLeftBracket, or emacs-mcx.useMetaPrefixMacCmd.
C-x C-oDelete blank lines around (delete-blank-lines)
C-tTranspose characters (transpose-chars).
C-x C-tTranspose lines (transpose-lines).
M-S-6 (M-^ with US keyboard)join two lines cleanly (delete-indentation)
M-dKill the next word (kill-word).
M-BkspKill one word backwards (backward-kill-word).
M-zKill up to and including the given character (zap-to-char).
C-kKill rest of line or one or more lines (kill-line).
C-S-BkspKill an entire line at once (kill-whole-line).
C-wKill the region (kill-region).
M-wCopy the region into the kill ring (kill-ring-save).
(unassigned) emacs-mcx.updateKillRingSaveReplace the current kill ring entry with the region (kill-new with REPLACE).
C-yYank the last kill into the buffer, at point (yank).
M-yEither replace the text just yanked with an earlier batch of killed text (yank-pop), or allow selecting from the list of previously-killed batches of text.
C-c yBrowse kill ring
C-oOpen line
C-jNew line
C-mNew line
C-x hSelect All
C-x u, C-/, C-S-- (C-_ with US keyboard)Undo
C-;Toggle line comment in and out
M-;Toggle region comment in and out
C-x C-l (M-l)Convert to lower case (On the Emacs' original behavior, C-x C-l and M-l are assigned to the different functionalities. However, this extension assigns these keys to the same emacs-mcx.transformToLowercase command which calls editor.action.transformToLowercase command internally and works similarly to both the original Emacs' functionalities based on the context. Upper case and title case (below) are same)
C-x C-u (M-u)Convert to upper case
M-cConvert to title case

Mark Commands

CommandDesc
C-SPC, C-S-2 (C-@ with US keyboard)Set the mark at point, and activate it (set-mark-command).
C-SPC C-SPCSet the mark, pushing it onto the mark ring, without activating it.
C-u C-SPCMove point to where the mark was, and restore the mark from the ring of former marks.
C-x C-xSet the mark at point, and activate it; then move point where the mark used to be (exchange-point-and-mark).

See this page and this page about the mark and the mark ring.

Intellisense

CommandDesc
C-M-i, M-TAB, M-/, C-'Trigger suggest. These keybindings call editor.action.triggerSuggest command, alternating Emacs' completion-at-point or dabbrev-expand

Text registers

CommandDesc
C-x r s rCopy region into register r (copy-to-register).
C-x r i rInsert text from register r (insert-register).

See this page about the text registers.

Position registers

CommandDesc
C-x r SPC rRecord the position of point and the current buffer in register r (point-to-register).
C-x r j rJump to the position and buffer saved in register r (jump-to-register).

Rectangles

CommandDesc
C-x r kKill the text of the region-rectangle, saving its contents as the last killed rectangle (kill-rectangle).
C-x r M-wSave the text of the region-rectangle as the last killed rectangle (copy-rectangle-as-kill).
C-x r dDelete the text of the region-rectangle (delete-rectangle).
C-x r yYank the last killed rectangle with its upper left corner at point (yank-rectangle).
C-x r pReplace last kill ring to each line of rectangle if the kill ring top only contains one line.
C-x r oInsert blank space to fill the space of the region-rectangle (open-rectangle).
C-x r cClear the region-rectangle by replacing all of its contents with spaces (clear-rectangle).
C-x r tReplace rectangle contents with string on each line (string-rectangle).
C-x SPCToggle Rectangle Mark mode (rectangle-mark-mode).

Rectangle Registers

CommandDesc
C-x r r rCopy the region-rectangle into register r (copy-rectangle-to-register). With prefix argument, delete it as well.
C-x r i rInsert the rectangle stored in register r (if it contains a rectangle) (insert-register).

Other Commands

CommandDesc
C-g (ESC)Quit
ESC ESC ESCQuit (keyboard-escape-quit, but this extension does not distinguish it from C-g)
C-' (M-/)IntelliSense Suggestion
M-xOpen command palette
C-M-SPCToggle SideBar visibility
C-x zToggle Zen Mode
C-x C-cClose window (save-buffers-kill-terminal)

File Commands

CommandDesc
C-x C-fQuickOpen a file (Tips: This extension assigns C-x C-f to the VSCode's native quick file navigation. If you prefer more Emacs-like behavior, The "File Browser" extension by Bodil Stokke (bodil.file-browser) may help.)
C-x C-sSave
C-x C-wSave as
C-x sSave all files
C-x C-nOpen new window

Tab / Buffer Manipulation Commands

CommandDesc
C-x bSwitch to another open buffer
C-x kClose current tab (buffer)
C-x 0Close the current editor group (delete-window).
C-x 1Close editors in other (split) group.
C-x 2Split editor horizontal
C-x 3Split editor vertical
C-x 4Toggle split layout (vertical to horizontal)
C-x oFocus another editor group (other-window)
C-x LEFTSelect the previous tab (previous-buffer).
C-x RIGHTSelect the next tab (next-buffer).

Prefix argument

See https://www.gnu.org/software/emacs/manual/html_node/emacs/Arguments.html for detail

CommandDesc
C-uuniversal-argument
M-<number>digit-argument
M--negative-argument

sexp

CommandPrefix argumentDesc
C-M-fMove forward over a balanced expression (forward-sexp)
C-M-bMove backward over a balanced expression (backward-sexp)
C-M-S-2 (C-M-@ with US keyboard)Set mark after end of following balanced expression (mark-sexp). This does not move point.
C-M-kKill balanced expression forward (kill-sexp)
C-M-BkspKill balanced expression backward (backward-kill-sexp)
C-S-kKill a line as if with kill-line, but respecting delimiters. (paredit-kill)

This extension makes use of paredit.js to provide sexp functionalities. Thank you for this great library.

Looking up identifiers

CommandDesc
M-.Find definitions of an identifier (xref-find-definitions). It's just bound to VSCode's "Go to Definition" (editor.action.revealDefinition).
M-,Go back to where you previously invoked M-. and friends (xref-go-back). It's just bound to VSCode's "Go Back" (workbench.action.navigateBack).
C-M-,Go forward to where you previously invoked M-, (xref-go-forward). It's just bound to VSCode's "Go Forward" (workbench.action.navigateForward).
M-S-/ (M-? with US keyboard)Find references of an identifier (xref-find-references). It's just bound to VSCode's "References: Find All References" (references-view.findReferences).

Other commands/APIs

emacs-mcx.executeCommandWithPrefixArgument

This command calls another command with the prefix argument. This is mainly for extension developers who want to make the extensions collaborative with this extension's prefix argument. See the issue #1146 for the discussion about it.

For example, if you define the keybinding below,

  • C-x e will call the command foo with the argument {}.
  • C-u C-x e will call the command foo with the argument { prefixArgument: 4 }.
{
  "key": "ctrl+x e",
  "command": "emacs-mcx.executeCommandWithPrefixArgument",
  "args": {
    "command": "foo"
  }
}

You can pass the arguments to the target command as below. In this case,

  • C-x e will call the command foo with the argument { baz: 42 }.
  • C-u C-x e will call the command foo with the argument { prefixArgument: 4, baz: 42 }.
{
  "key": "ctrl+x e",
  "command": "emacs-mcx.executeCommandWithPrefixArgument",
  "args": {
    "command": "foo",
    "args": {
      "baz": 42
    }
  }
}

You can change the key name of the prefix argument.

{
  "key": "ctrl+x e",
  "command": "emacs-mcx.executeCommandWithPrefixArgument",
  "args": {
    "command": "foo",
    "prefixArgumentKey": "repeat"
  }
}
  • C-x e will call the command foo with the argument {}.
  • C-u C-x e will call the command foo with the argument { repeat: 4 }.

Overriding the prefix argument

When you define a keybinding for emacs-mcx.* commands, you can override the prefix argument by specifying the prefixArgument in the keybinding's arguments.

For example:

{
  "key": "alt+shift+y",
  "command": "emacs-mcx.yankPop",
  "args": {
    "prefixArgument": -1
  }
}

With this keybinding, you can call emacs-mcx.yankPop command with a prefix argument -1 by pressing alt+shift+y.

Conflicts with default key bindings

  • ctrl+d: editor.action.addSelectionToNextFindMatch => Use ctrl+alt+n instead;
  • ctrl+g: workbench.action.gotoLine => Use alt+g g instead;
  • ctrl+b: workbench.action.toggleSidebarVisibility => Use ctrl+alt+space instead;
  • ctrl+j: workbench.action.togglePanel => Use ctrl+x j instead;
  • ctrl+space: toggleSuggestionDetails, editor.action.triggerSuggest => Use ctrl+' instead;
  • ctrl+x: editor.action.clipboardCutAction => Use ctrl+w instead;
  • ctrl+v: editor.action.clipboardPasteAction => Use ctrl+y instead;
  • ctrl+k: editor.debug.action.showDebugHover, editor.action.trimTrailingWhitespace, editor.action.showHover, editor.action.removeCommentLine, editor.action.addCommentLine, editor.action.openDeclarationToTheSide;
  • ctrl+k z: workbench.action.toggleZenMode => Use ctrl+x z instead;
  • ctrl+y: redo;
  • ctrl+m: editor.action.toggleTabFocusMode;
  • ctrl+/: editor.action.commentLine => Use ctrl+; instead;
  • ctrl+p & ctrl+e: workbench.action.quickOpen => Use ctrl+x ctrl+f instead;
  • ctrl+o: workbench.action.files.openFileFolder;

Contributions/Development

Your contributions are very welcome!

Please see CONTRIBUTING.md about development of this extension.