Zed editor configuration

#| file: modules/home-manager/zed/default.nix
{
  pkgs,
  lib,
  config,
  ...
}:
let
  cfg = config.mjm.zed;
in
{
  options.mjm.zed = {
    enable = lib.mkOption {
      type = lib.types.bool;
      default = config.mjm.desktop.enable;
    };
  };

  config = lib.mkIf cfg.enable {
    <<config>>
  };

  _class = "homeManager";
}

Zed is the text editor I'm using most of the time these days. While they are bigger on AI stuff than I'd generally prefer, it's still a pretty solid editor with those things disabled. Unless I'm stuck in the terminal or doing a really quick change of something, I'm probably working in Zed.

programs.zed-editor.enable = true;
programs.zed-editor.mutableUserSettings = false;
programs.zed-editor.userSettings = {
  <<user-settings>>
};

While it's a bit inconvenient to not be able to alter settings in the UI, I prefer to not have it be possible for my configs to get out of sync between machines, so my user settings for Zed are immutable.

programs.zed-editor.extraPackages = lib.attrValues {
  inherit (pkgs)
    rustup
    gcc
    nixd
    nixfmt
    gopls
    ;
};
programs.zed-editor.extensions = [
  "dracula"
  "grafana-alloy"
  "ini"
  "kdl"
  "nix"
  "scheme"
  "toml"
];

I preinstall several extensions for things I need to use regularly, and also add some packages to Zed's environment to support them. The packages don't need to include every single tool that a project might need, just ones that should always be available.

#| id: user-settings
disable_ai = true;
toolbar.agent_review = false;

I'm not interested in using any of the AI features in Zed, so I disable those.

#| id: user-settings
auto_update = false;
telemetry = {
  diagnostics = false;
  metrics = false;
};

This is disabling things that phone home. Auto updates aren't needed because the version is tracked by Nixpkgs.

#| id: user-settings
theme = {
  mode = "system";
  light = "Dracula Light (Alucard";
  dark = "Dracula";
};
ui_font_size = 14;
ui_font_family = "Ubuntu Sans";
buffer_font_size = 13;
buffer_font_family = "PragmataPro Liga";
buffer_line_height = "standard";
colorize_brackets = true;
preferred_line_length = 92;

These are settings that affect the appearance of Zed, both in the chrome and the editor. I use the Dracula theme like I do with many other things.

#| id: user-settings
vim_mode = true;
helix_mode = true;

Zed has a Helix mode built-in, which is why I can deal with using it. It's not a perfect recreation, but it's good enough. The Helix mode is implemented on top of the Vim mode, so that must be enabled too.

#| id: user-settings
autosave = "on_focus_change";
format_on_save = "on";

I like to have my editors save aggressively, and also automatically format files when they are saved.

#| id: user-settings
toolbar.code_actions = true;
diagnostics.inline.enabled = true;
inlay_hints.enabled = true;
code_lens = "on";
show_signature_help_after_edits = true;
auto_signature_help = true;

These are editor features that I want enabled that are not by default.

#| id: user-settings
git.inline_blame.enabled = false;
title_bar.show_sign_in = false;
title_bar.show_branch_status_icon = false;
title_bar.show_branch_name = false;
title_bar.show_worktree_name = false;
collaboration_panel.button = false;
terminal.button = false;

These are editor features that are enabled by default but that I don't want to use or have shown. Inline blame for Git is too noisy for my taste. The Git information in the title bar is not generally useful for me since I use Jujutsu. I never want to sign in to Zed, so having the button for that in the title bar is annoying. And I don't use the collaboration features or the built-in terminal, so I'd rather not have those buttons shown.

#| id: user-settings
cli_default_open_behavior = "existing_window";
load_direnv = "shell_hook";

By default, I want the CLI command for Zed to open things in the existing window for the workspace the file belongs to.

I rely on direnv for most projects to set up the environment with the right tools in a Nix shell. This often includes LSPs and compilers/interpreters. Zed can load the direnv environment itself, so that the right tools are available even when you don't launch Zed from a CLI with that environment already set up.

#| id: user-settings
lsp.rust-analyzer.initialization_options = {
  assist.preferSelf = true;
  cargo.targetDir = true;
  check.command = "clippy";
};

I have a little bit of custom config for rust-analyzer. "assist.preferSelf" makes it use "Self" instead of the actual type name for completions where appropriate. "cargo.targetDir" makes rust-analyzer use its own target directory instead of sharing the default with normal cargo commands, which is important to avoid the editor needing to wait on a build happening outside the editor or vice versa. Usually this makes things more responsive, even if it means some duplicated work. And finally, I set the check command to clippy, as I want clippy warnings/errors to show up in the editor.

#| id: user-settings
languages.Nix.language_servers = [
  "nixd"
  "!nil"
];

For Nix, I use nixd as the language server. Zed defaults to using nil, so I need to add nixd to the list and remove nil.

xdg.configFile."zed/snippets/gemini.json".source =
  let
    json = pkgs.formats.json { };
  in
  {
    "Code block" = {
      prefix = "cc";
      body = [
        "``` {.\${1:nix}$2}"
        "$0"
        "```"
      ];
      description = "Code block";
    };
  }
  |> json.generate "gemini-snippets.json";

The home-manager module for Zed doesn't include an option for snippets, but it's not hard to generate the necessary JSON file and link it into place. I have one snippet for Gemini files, which is used to generate the code blocks for literate programming.

Proxy Information
Original URL
gemini://midna.dev/homelab/modules/home-manager/zed/
Status Code
Success (20)
Meta
text/gemini;lang=en-US
Capsule Response Time
191.187591 milliseconds
Gemini-to-HTML Time
0.323544 milliseconds

This content has been proxied by September (UNKNO).