This post is a part of my "Zed is the Future?" series


If your are a keyboard-heavy user, one of the first things you start tweaking in a text editor are keyboard shortcuts.

Coming from Neovim, I came to expect full programmability of my key mappings. However, Zed takes a different approach that steers more towards the zero-configuration philosophy that has become popular lately. Zed's keymap is configured through a collection of JSON files with the list of bindings and contexts where they are active:

[
  {
    "context": "Dock || Terminal || VimControl"
    "bindings": {
      "ctrl-h": ["workspace::ActivatePaneInDirection", "Left"],
      "ctrl-j": ["workspace::ActivatePaneInDirection", "Down"],
      "ctrl-k": ["workspace::ActivatePaneInDirection", "Up"],
      "ctrl-l": ["workspace::ActivatePaneInDirection", "Right"],
    },
  },
  {
    "context": "Editor && VimControl && !VimWaiting && !menu"
    "bindings": {
      ", q": "pane::CloseActiveItem",
      "n": "search::SelectNextMatch",
      "shift-n": "search::SelectPrevMatch"
    },
  }
]

The JSON-based approach offers several advantages:

  • Auto-completion and validation tools can provide instant feedback while editing the config
  • Keybindings are easily shareable as simple JSON snippets
  • Finding specific mappings is straightforward with basic text search
  • Future format migrations can be handled smoothly with automated tooling

However, this comes with several drawbacks:

  • The JSON format can be verbose and repetitive, making keybindings more cumbersome to define
  • As everything is defined in a single file with little organization, it becomes hard to explore and maintain the mappings
  • Customizing default bindings requires copying the existing ones, as there's no native way to extend them systematically

Alternatives

I've been wondering if there are alternatives that would be a middle-ground between the full Turing-complete configuration and the plain JSON files.

So I've built a tool to generate the keymap from a more structured and concise definition written in Jsonnet configuration language.

Here is the excerpt from my keymap:

local lib = import 'keymap.lib.jsonnet';
local ctx = lib.ctx;
local map = lib.map;

local leader = 'space';
local local_leader = ',';

std.flattenArrays([
  // You can split the keymap into multiple files.
  (import 'window.lib.jsonnet')(prefix='space w'),
  // Definitions can take parameters for customization.
  // For example, I copied default Zed Vim 'g' bindings and
  // added ability to replace prefix `g` with any other key.
  (import 'actions.lib.jsonnet')(prefix='g'),
  [
    // Commonly used contexts can be defined in a single place
    // as the library functions.
    ctx.hub({
      'ctrl-h': ['workspace::ActivatePaneInDirection', 'Left'],
      'ctrl-l': ['workspace::ActivatePaneInDirection', 'Right'],
      'ctrl-k': ['workspace::ActivatePaneInDirection', 'Up'],
      'ctrl-j': ['workspace::ActivatePaneInDirection', 'Down'],
    }),

    ctx.vim_normal({
      n: 'search::SelectNextMatch',
      'shift-n': 'search::SelectPrevMatch',
      [local_leader + ' q']: 'pane::CloseActiveItem',
    }),

    // When multiple bindings start with the same key,
    // they can be grouped together with `map.hydra`.
    ctx.vim_normal(map.hydra(leader, {
      '/': 'workspace::NewSearch',
      ',': 'tab_switcher::Toggle',
    })),
  ],

  // You can still use the standard keymap definition syntax.
  {
    context: 'Workspace',
    bindings: {
      'shift-ctrl-r': ['task::Spawn', { task_name: 'Update keymap' }],
    },
  },
])

This config can be fed into jsonnet tool to generate your familiar keymap.json:

jsonnet keymap.jsonnet -o keymap.json

I'm still exploring the ergonomics of this approach, but it does address my configuration needs for now. The tooling for using it still has a few rough edges though - you need to manually invoke the keymap update after you change the Jsonnet file. Also, error reporting could be improved to provide more context when something goes wrong.

I plan to polish it further and share it with the community to gather some feedback.