Zed: Difference between revisions

Home-manager description: Extension oddities, grammar, formatting.
m fix typo in the option's name
 
(12 intermediate revisions by 7 users not shown)
Line 1: Line 1:
<languages/>
{{infobox application
  |name=Zed
  |image=Zed_Editor_Logo.png
  |type=Source-code Editor
  |developer=Zed Industries
  |predecessor=Atom
  |status=Active
  |license=[https://www.gnu.org/licenses/agpl-3.0.html AGPL], [https://www.gnu.org/licenses/gpl-3.0.html GPL], [https://www.apache.org/licenses/LICENSE-2.0 Apache License]
  |os=Cross-platform (Linux, macOS, Windows)
  |platform=Desktop
  |programmingLanguage=Rust
  |website=[https://zed.dev zed.dev]
  |github=zed-industries/zed
  |bugTracker=[https://github.com/zed-industries/zed/issues GitHub Issues]
  |documentation=[https://zed.dev/docs Zed Documentation]
}}
<translate>
<!--T:1-->
[https://zed.dev Zed]<ref>Zed Industries, "Zed", Official Website, Accessed October 2025. https://zed.dev</ref> is a collaborative, GPU-accelerated text editor developed by Zed Industries. It combines fast local editing with real-time multiplayer features and ships with batteries-included tooling for popular programming languages.


[https://zed.dev/ Zed] is a graphical text editor focusing on speed and collaborative editing.
<!--T:2-->
The editor provides native builds for Linux, including Nixpkgs packages and a reproducible flake. Hardware acceleration requires a GPU with Vulkan support; systems without Vulkan can fall back to emulation via tools such as [https://github.com/nix-community/nixGL nixGL].<ref>Zed Industries, "Linux", Zed Documentation, Accessed October 2025. https://zed.dev/docs/linux</ref>


Its Linux support is fairly recent, the NixOS support even more so.
<!--T:5-->
{{Nixpkg|pkgs/by-name/ze/zed-editor|zed-editor}} is available in Nixpkgs since 24.11; However, Zed provides [https://github.com/zed-industries/zed/blob/main/flake.nix an official nix flake] which might be useful if you need features that have not yet reached unstable Nixpkgs.


== Installation ==
== Installation == <!--T:3-->
The package zed-editor is available only from channel 24.11 onward.


CLI support is installed and aliased to <code>zeditor</code>
<!--T:6-->
The package installs both desktop launchers and a CLI entry point aliased to <code>zeditor</code>, mirroring the upstream binary name


== LSP Support ==
=== Imperative === <!--T:4-->
By default, Zed will try to download pre-built LSP servers in <code>~/.local/share/zed/languages/</code>. This does not work for NixOS.


There's sadly no way to inject those from <code>$PATH</code> for now. Worse, the way to point to the language server is language-specific, there is no global configuration flag for now.
==== Zed's Flake ==== <!--T:45-->
</translate>


The following sections contain some language-specific setup working on NixOS.
<syntaxhighlight lang="console">
nix run github:zed-industries/zed
</syntaxhighlight>


=== rust-analyzer ===
==== Nixpkgs ====
Here, we'll assume rust-analyzer is globally installed in your system profile at <code>/run/current-system/sw/bin/rust-analyzer</code>. You may want to adapt this path in the following code snippet to something more relevant to your use case.


Add the following snippet to your zed configuration file:<syntaxhighlight lang="json">
<syntaxhighlight lang="console">
"lsp": {
nix run nixpkgs#zed-editor
  "rust-analyzer": {
    "binary": {
      "path": "/run/current-system/sw/bin/rust-analyzer",
    },
  }
}
</syntaxhighlight>
</syntaxhighlight>


== Remote Server ==
<translate>
When connecting to a remote server running NixOS, Zed will either automatically download a static server binary matching its version from the upstream Zed website onto the remote host, or will locally obtain then upload a static server binary (if <code>"upload_binary_over_ssh": true</code>) to the remote host, before connecting to it :
=== Declarative === <!--T:7-->
</translate>
 
==== NixOS ====


:<syntaxhighlight lang="console">
<syntaxhighlight lang="nix">
➜  ~ ls .zed_server
environment.systemPackages = [
zed-remote-server-stable-0.169.2
  pkgs.zed-editor
➜  ~ file .zed_server/zed-remote-server-stable-0.169.2
];
.zed_server/zed-remote-server-stable-0.169.2: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), static-pie linked, BuildID[sha1]=b1ac2c83127e7a9a072840cd7c24d7d125c1b655, not stripped
➜  ~ ldd .zed_server/zed-remote-server-stable-0.169.2
statically linked
</syntaxhighlight>
</syntaxhighlight>


In either case, the binary is placed in the <code>~/.zed_server</code> folder on the remote system, and must match the client Zed version exactly or the connection will fail.  This same <code>~/.zed_server</code> location on the client system is also where locally downloaded binaries are stored before being uploaded via SSH to a remote system. 
{{Evaluate}}
==== Home Manager ====


While the default prebuilt binaries from the upstream Zed website work fine with the default nixpkgs definition of <code>zed-editor</code>, you may want to provide your own, whether for reasons related to custom patching or purely mistrust of prebuilt upstream binaries.
<syntaxhighlight lang="nix">
home.packages = [
  pkgs.zed-editor
];
</syntaxhighlight>


The <code>zed-editor</code> provides an additional output, <code>remote_server</code>, for the server binary matching the client version.  To make use of it you need the binary in <code>"${pkgs.zed-editor.remote_server}/bin"</code> placed/symlinked into your <code>~/.zed_server</code> folder (and <code>"upload_binary_over_ssh": true</code> included in the Zed client settings that specify each remote system). 
<syntaxhighlight lang="console">
home-manager switch
</syntaxhighlight>


In a simple case, this can be setup with home-manager as follows:
<translate>
==== Zed's Flake ==== <!--T:9-->
</translate>


:<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
{ pkgs, ... }:
{
{
   home.file.".zed_server" = {
   inputs.zed.url = "github:zed-industries/zed";
     source = "${pkgs.zed-editor.remote_server}/bin";
 
     # keeps the folder writable, but symlinks the binaries into it
  outputs =
    recursive = true;
    {
  };
      self,
      nixpkgs,
      zed,
      ...
     }@inputs:
    let
      system = "x86_64-linux";
      pkgs = import nixpkgs { inherit system; };
    in
    {
      packages.${system}.zed-latest = zed.packages.${system}.default;
     };
}
}
</syntaxhighlight>
</syntaxhighlight>


Because the <code>~/.zed_server</code> folder is also used for external clients connecting to the current system as a remote, it's necessary to use the <code>recursive = true;</code> setting so only individual binaries are symlinked from the nix store into the folder, and the folder itself remains writable. When a client connects using a version that doesn't already have a matching server binary available, it will use its configured method to add the missing server binary.  This same configuration may be set more easily via home-manager if you're managing your entire Zed config thru home-manager by setting <code>programs.zed-editor.installRemoteServer = true;</code> (the <code>programs.zed-editor.enable = true</code> must also be enabled for this to take effect, see below for further details about using the home-manager <code>programs.zed-editor</code>).
<translate>
<!--T:11-->
Build the flake package with <code>nix build .#zed-latest</code> or expose it in your configuration with the appropriate overlay.


If you want to prohibit clients from managing the Zed server binaries on your system when connecting to it as a remote, you can make the whole <code>~/.zed_server</code> folder read-only by symlinking the whole folder to the nix store. This will restrict clients to only be able to use one Zed server binary provided from your nixpkgs <code>zed-editor</code>, which has the indirect effect of also limiting to a single Zed client version that's allowed to connect. To do this, simply remove the <code>recursive = true;</code> or explicitly set it to <code>recursive = false;</code> (the default when not specified) on <code>home.file.".zed_server"</code>.
<!--T:12-->
Be aware however that Zed will refuse to connect to a remote if there is no remote server binary matching the exact Zed client version and it's unable to populate a matching one!
{{Warning|Zed requires hardware-accelerated Vulkan. On systems without supported drivers, use <code>nixGL</code> or home-manager's <code>nixGL.vulkan.enable {{=}} true;</code> to provide the necessary libraries.
Providing Vulkan through <code>nixGL</code> can be more consistent than relying on host distribution packages, especially on non-NixOS systems where Wayland and X11 stacks differ in their Vulkan capabilities.}}


== Home manager support ==
== Configuration == <!--T:13-->
Zed is supported by home-manager, which allows you to make a reproducible initial Zed setup. 
However, due to the method Zed uses for installing, running, and managing Extensions, only the initial set of preinstalled extensions can currently be defined in home-manager. It's possible to install additional extensions from the Zed GUI on any system without including it in the home-manager config. This is partially a technical limitation of the format of extensions (which follow a similar model to VSCode and require external binaries to be downloaded and used at run-time}, and a Zed extensions management design (that doesn't track the installed extensions in the settings but does allow a list to be manually added that will be auto-installed if not already present). 


The <code>userSettings</code> and <code>userkeyMaps</code> options will be translated directly into JSON
<!--T:14-->
The <code>extensions</code> currently just defines the <code>userSettings.auto_install_extensions</code> (see Zed documentation).
Zed stores its configuration in JSON files under <code>~/.config/zed</code>. Home Manager can manage these settings declaratively.
The <code>extraPackages</code> includes extra nixpkgs in the environment Zed executes in (an FHS), so extra LSP server packages (e.g. <code>pkgs.nixd</code>) or optional tools for LSP servers (e.g. <code>pkgs.shellcheck</code> for the "Basher" LSP) should be included.


Note that home-manager configuration produces a <b>read only <code>settings.json</code></b>, but Zed <b>assumes/requires the <code>settings.json</code> to be writable</b>. Using home-manager configuration will prevent you from changing most settings in the GUI since Zed will be unable to modify the read-only <code>settings.json</code> accordingly.  This includes the current/default AI engine to use, or even switching the model of the AI engine (as well as most non-AI settings).
==== Basic ==== <!--T:15-->
</translate>


You can see an example of the home-manager configuration:
<syntaxhighlight lang="nix">
programs.zed-editor = {
:<syntaxhighlight lang="nix">
  enable = true;
{pkgs, lib, ... }:
  extensions = [ "nix" "toml" "rust" ];
  userSettings = {
    theme = {
      mode = "system";
      dark = "One Dark";
      light = "One Light";
    };
    hour_format = "hour24";
    vim_mode = true;
  };
};
</syntaxhighlight>


{
<translate>
    programs.zed-editor = {
<!--T:16-->
        enable = true;
The configuration above enables Zed via Home Manager, installs a small set of extensions, and synchronises the theme with the desktop appearance.


        ## This populates the userSettings "auto_install_extensions"
==== Advanced ==== <!--T:17-->
        extensions = ["nix" "toml" "elixir" "make"];
</translate>


        ## everything inside of these brackets are Zed options.
<syntaxhighlight lang="nix">
        userSettings = {
programs.zed-editor = {
  enable = true;


            assistant = {
  # This populates the userSettings "auto_install_extensions"
                enabled = true;
  extensions = [ "nix" "toml" "elixir" "make" ];
                version = "2";
                default_open_ai_model = null;
                ### PROVIDER OPTIONS
                ### zed.dev models { claude-3-5-sonnet-latest } requires github connected
                ### anthropic models { claude-3-5-sonnet-latest claude-3-haiku-latest claude-3-opus-latest  } requires API_KEY
                ### copilot_chat models { gpt-4o gpt-4 gpt-3.5-turbo o1-preview } requires github connected
                default_model = {
                    provider = "zed.dev";
                    model = "claude-3-5-sonnet-latest";
                };


                #               inline_alternatives = [
  # Everything inside of these brackets are Zed options
                #                    {
  userSettings = {
                #                        provider = "copilot_chat";
    assistant = {
                #                        model = "gpt-3.5-turbo";
      enabled = true;
                #                    }
      version = "2";
                #                ];
      default_open_ai_model = null;
            };


            node = {
      # Provider options:
                path = lib.getExe pkgs.nodejs;
      # - zed.dev models (claude-3-5-sonnet-latest) requires GitHub connected
                npm_path = lib.getExe' pkgs.nodejs "npm";
      # - anthropic models (claude-3-5-sonnet-latest, claude-3-haiku-latest, claude-3-opus-latest) requires API_KEY
            };
      # - copilot_chat models (gpt-4o, gpt-4, gpt-3.5-turbo, o1-preview) requires GitHub connected
      default_model = {
        provider = "zed.dev";
        model = "claude-3-5-sonnet-latest";
      };


            hour_format = "hour24";
      # inline_alternatives = [
            auto_update = false;
      #  {
            terminal = {
      #    provider = "copilot_chat";
                alternate_scroll = "off";
      #     model = "gpt-3.5-turbo";
                blinking = "off";
      #   }
                copy_on_select = false;
      # ];
                dock = "bottom";
    };
                detect_venv = {
                    on = {
                        directories = [".env" "env" ".venv" "venv"];
                        activate_script = "default";
                    };
                };
                env = {
                    TERM = "alacritty";
                };
                font_family = "FiraCode Nerd Font";
                font_features = null;
                font_size = null;
                line_height = "comfortable";
                option_as_meta = false;
                button = false;
                shell = "system";
                #{
                #                   program = "zsh";
                #};
                toolbar = {
                    title = true;
                };
                working_directory = "current_project_directory";
            };


    node = {
      path = lib.getExe pkgs.nodejs;
      npm_path = lib.getExe' pkgs.nodejs "npm";
    };


    hour_format = "hour24";
    auto_update = false;


            lsp = {
    terminal = {
                rust-analyzer = {
      alternate_scroll = "off";
      blinking = "off";
      copy_on_select = false;
      dock = "bottom";
      detect_venv = {
        on = {
          directories = [ ".env" "env" ".venv" "venv" ];
          activate_script = "default";
        };
      };
      env = {
        TERM = "alacritty";
      };
      font_family = "FiraCode Nerd Font";
      font_features = null;
      font_size = null;
      line_height = "comfortable";
      option_as_meta = false;
      button = false;
      shell = "system";
      # shell = {
      #  program = "zsh";
      # };
      toolbar = {
        title = true;
      };
      working_directory = "current_project_directory";
    };


                    binary = {
    lsp = {
                        #                       path = lib.getExe pkgs.rust-analyzer;
      rust-analyzer = {
                        path_lookup = true;
        binary = {
                    };
          # path = lib.getExe pkgs.rust-analyzer;
                };
          path_lookup = true;
                nix = {
        };
                    binary = {
      };
                        path_lookup = true;  
                    };  
                };


                elixir-ls = {
      nix = {
                    binary = {
        binary = {
                        path_lookup = true;  
          path_lookup = true;
                    };
        };
                    settings = {
      };
                        dialyzerEnabled = true;
 
                    };
      elixir-ls = {
                };
        binary = {
            };
          path_lookup = true;
        };
        settings = {
          dialyzerEnabled = true;
        };
      };
    };


    languages = {
      "Elixir" = {
        language_servers = [ "!lexical" "elixir-ls" "!next-ls" ];
        format_on_save = {
          external = {
            command = "mix";
            arguments = [ "format" "--stdin-filename" "{buffer_path}" "-" ];
          };
        };
      };


            languages = {
      "HEEX" = {
                "Elixir" = {
        language_servers = [ "!lexical" "elixir-ls" "!next-ls" ];
                    language_servers = ["!lexical" "elixir-ls" "!next-ls"];
        format_on_save = {
                    format_on_save = {
          external = {
                        external = {
            command = "mix";
                            command = "mix";
            arguments = [ "format" "--stdin-filename" "{buffer_path}" "-" ];
                            arguments = ["format" "--stdin-filename" "{buffer_path}" "-"];
          };
                        };
        };
                    };
      };
                };
    };
                "HEEX" = {
                    language_servers = ["!lexical" "elixir-ls" "!next-ls"];
                    format_on_save = {
                        external = {
                            command = "mix";
                            arguments = ["format" "--stdin-filename" "{buffer_path}" "-"];
                        };
                    };
                };
            };


            vim_mode = true;
    vim_mode = true;
            ## tell zed to use direnv and direnv can use a flake.nix enviroment.
            load_direnv = "shell_hook";
            base_keymap = "VSCode";
            theme = {
                mode = "system";
                light = "One Light";
                dark = "One Dark";
            };
            show_whitespaces = "all" ;
            ui_font_size = 16;
            buffer_font_size = 16;


        };
    # Tell Zed to use direnv and direnv can use a flake.nix environment
    load_direnv = "shell_hook";
    base_keymap = "VSCode";


    theme = {
      mode = "system";
      light = "One Light";
      dark = "One Dark";
     };
     };
    show_whitespaces = "all";
    ui_font_size = 16;
    buffer_font_size = 16;
  };
};
</syntaxhighlight>
<translate>
<!--T:18-->
This example adds language servers to the FHS sandbox, enables the bundled assistant, configures the terminal, and ensures remote server binaries are provided declaratively.
<!--T:19-->
The <code>userSettings</code> and <code>userKeymaps</code> options translate directly into JSON. The <code>extraPackages</code> option includes additional Nixpkgs in the FHS environment, useful for LSP servers (e.g., <code>pkgs.nixd</code>) or optional tools (e.g., <code>pkgs.shellcheck</code> for the Basher LSP).
<!--T:20-->
Home Manager renders <code>settings.json</code> as read-only, which prevents Zed's GUI from saving most preference changes, including AI provider selection. Plan to manage long-term settings declaratively or temporarily disable the module when editing interactively.
<!--T:21-->
Only the initial extension list can be defined declaratively; additional extensions installed through the GUI are stored within Zed's writable data directories and do not appear in <code>userSettings</code>. This follows a similar model to VSCode, where extensions require external binaries downloaded at runtime.
</translate>
== LSP support ==
<translate>
<!--T:22-->
Zed downloads LSP servers into <code>~/.local/share/zed/languages/</code>. These binaries most likely will not work because of linking issues. You can workaround this with the following methods or bring your own LSP servers.
</translate>
<translate>
<!--T:23-->
Check <strong>LSP Logs → Server Info</strong> to confirm which binaries are running.
</translate>
==== Nix-ld (recommended) ====
<translate>
<!--T:24-->
Enable [[FAQ#I've downloaded a binary, but I can't run it, what can I do?|<code>programs.nix-ld</code>]] so language servers downloaded by Zed can resolve dynamic libraries without wrapping and work out of the box.
</translate>
==== FHS wrapper ====
<translate>
<!--T:25-->
Use <code>pkgs.zed-editor.fhsWithPackages</code> to extend the FHS environment with additional system libraries when a language server requires them.
</translate>
<syntaxhighlight lang="nix">
pkgs.zed-editor.fhsWithPackages(
  pkgs: with pkgs; [
    openssl
    zlib
  ]
)
</syntaxhighlight>
<translate>
<!--T:26-->
Remember that language servers started inside the wrapper do not inherit tools and libraries from nix shell. This is usually not desired because LSP may need project-level dependencies provided by nix-shell to compile and analyze the code.
</translate>
==== Bring your own LSP servers ====
<translate>
<!--T:27-->
Newer versions of Zed can detect LSP servers installed in <code>PATH</code> and prefer local versions over automatically downloaded ones. Install LSP servers globally or provide them through nix shell.
</translate>
<translate>
<!--T:28-->
Nixpkgs versions of tools may be required for development on NixOS, especially for the C/C++ ecosystem. Check <strong>LSP Logs → Server Info</strong> to see which binaries are running.
</translate>
<translate>
<!--T:29-->
If automatic detection doesn't work, specify the path manually in your Zed configuration:
</translate>
<syntaxhighlight lang="json">
{
  "lsp": {
    "rust-analyzer": {
      "binary": {
        "path": "/run/current-system/sw/bin/rust-analyzer"
      }
    }
  }
}
}
</syntaxhighlight>
<translate>
<!--T:30-->
Adjust the path for each language server you manage. The example above assumes rust-analyzer is installed in your system profile.
</translate>
== Remote server ==
<translate>
<!--T:31-->
Zed uploads a versioned remote server binary to <code>~/.zed_server</code> on the target host. The Nixpkgs package exposes the matching binary via the <code>remote_server</code> output.
</translate>
<translate>
<!--T:32-->
When you connect to a remote machine, the client either downloads a matching server binary from upstream or pushes a local copy if <code>"upload_binary_over_ssh": true</code> is enabled. Connections fail if the versions diverge.
</translate>
<syntaxhighlight lang="nix">
home.file.".zed_server" = {
  source = "${pkgs.zed-editor.remote_server}/bin";
  recursive = true;
};
</syntaxhighlight>
<translate>
<!--T:33-->
The <code>recursive = true;</code> setting keeps the directory writable while symlinking individual binaries, allowing Zed to add new versions when needed. This is necessary because the <code>~/.zed_server</code> folder is also used when external clients connect to the current system as a remote.
</translate>
<translate>
<!--T:34-->
Alternatively, use Home Manager's built-in option for simpler setup:
</translate>
<syntaxhighlight lang="nix">programs.zed-editor = {
  enable = true;
  installRemoteServer = true;
};</syntaxhighlight>
<translate>
<!--T:35-->
To restrict remote clients to a specific server version, set <code>recursive = false;</code> to make the entire folder read-only. Zed refuses to connect if it cannot provision the required binary, so document the restriction for collaborators.
</translate>
== Tips and tricks ==
==== Vulkan diagnostics ====
{{code|lang=bash|line=no|1=$ vulkaninfo --summary}}
<translate>
<!--T:36-->
Run the command above to check your Vulkan support before launching Zed. Install <code>vulkan-tools</code> if the command is not available. On non-NixOS systems, you may need to wrap Zed with <code>nixGLVulkan</code> from the nixGL package.
</translate>


==== Preinstall extensions ====
<syntaxhighlight lang="json">
{
  "extensions": [
    "nix",
    "toml",
    "elixir"
  ]
}
</syntaxhighlight>
</syntaxhighlight>
<translate>
<!--T:37-->
Declaratively listing extensions ensures they are installed automatically on new systems while still allowing additional extensions to be added interactively.
</translate>
==== Synchronise settings across machines ====
<translate>
<!--T:38-->
Store the entire <code>~/.config/zed</code> directory in a version-controlled dotfiles repo or manage it with Home Manager to keep settings consistent across hosts.
</translate>
== Troubleshooting ==
==== Zed fails to start without Vulkan ====
<translate>
<!--T:39-->
If the GUI refuses to launch, confirm that <code>vulkan-tools</code> reports a working ICD. Use <code>nixGL</code> or vendor packages that supply Vulkan drivers for your GPU.
</translate>
==== Remote collaboration disconnects ====
<translate>
<!--T:40-->
Ensure the remote server binary matches the client version. Re-run <code>home-manager switch</code> or update the symlink in <code>~/.zed_server</code> if the versions diverge after an update.
</translate>
==== Language server missing binaries ====
<translate>
<!--T:41-->
When Zed reports missing tools, add the required executables to <code>programs.zed-editor.extraPackages</code> or wrap the server using <code>pkgs.writeShellApplication</code>.
</translate>
== See also ==
* <translate>
<!--T:42-->
[[Home Manager]] – Manage Zed configuration declaratively
</translate>
* <translate>
<!--T:43-->
[[Graphics#Vulkan]] – Set up Vulkan on NixOS systems
</translate>
* <translate>
<!--T:44-->
[https://search.nixos.org/options?query=zed-editor NixOS options search for Zed]
</translate>
== References ==
<references/>


[[Category:Applications]]
[[Category:Applications]]
[[Category:Text Editor]]
[[Category:Text Editor]]