Zed

From NixOS Wiki
Revision as of 14:06, 17 January 2025 by Niklas Korz (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Zed is a graphical text editor focusing on speed and collaborative editing.

Its Linux support is fairly recent, the NixOS support even more so.

Installation

The package zed-editor is available only from channel 24.11 onward.

CLI support is installed and aliased to zeditor

LSP Support

By default, Zed will try to download pre-built LSP servers in ~/.local/share/zed/languages/. This does not work for NixOS.

There's sadly no way to inject those from $PATH for now. Worse, the way to point to the language server is language-specific, there is no global configuration flag for now.

The following sections contain some language-specific setup working on NixOS.

rust-analyzer

Here, we'll assume rust-analyzer is globally installed in your system profile at /run/current-system/sw/bin/rust-analyzer. 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:

 "lsp": {
   "rust-analyzer": {
     "binary": {
       "path": "/run/current-system/sw/bin/rust-analyzer",
     },
   }
 }

Remote Server

When connecting to a remote server running NixOS, Zed will automatically upload a static server binary matching its version (provided and built by upstream Zed) and connect to it:

➜  ~ ls .zed_server
zed-remote-server-stable-0.169.2
➜  ~ 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

While this static binary works fine on NixOS, you may want to consider using the nixpkgs provided server instead if you have custom patches to apply or mistrust whatever binary upstream Zed is downloading to your server.

The binary name of the remote_server output of the zed-editor package matches what Zed expects on the remote, meaning you can symlink "${pkgs.zed-editor.remote_server}/bin" to ~/.zed_server on the remote host to make use of it. As an end user, you have to ensure the remote uses the same version of the zed-editor package as the local client.

In home-manager, this can be used as follows:

{ pkgs, ... }:
{
  home.file.".zed_server".source = "${pkgs.zed-editor.remote_server}/bin";
}

If the version does not match, Zed will try to upload the binary itself (as the existing remote binary has a version-specific name), and in this particular example fail to do so because we symlinked .zed_server to a read-only directory in the Nix store.

Alternatively, you may choose to only symlink the binary and not the directory, so Zed will successfully fall back to its own binary upload if the remote package version does not match:

{ pkgs, ... }:
let
  inherit (pkgs.zed-editor) version remote_server;
  binary_name = "zed-remote-server-stable-${version}";
in
{
  home.file.".zed_server/${binary_name}".source = "${remote_server}/bin/${binary_name}";
}


Home manager support

Zed is supported by home-manager, this way you are able to make a reproducible Zed setup.

userSettings option will be translated directly to json file.

note that home-manager configuration produces a read only `settings.json` this means some features which requires to write this file might not work. For example change model at runtime. Changing AI model at runtime tries to write in the configuration.

you can see an example of the home-manager configuration.

{pkgs, lib, ... }:

{
    programs.zed-editor = {
        enable = true;
        extensions = ["nix" "toml" "elixir" "make"];

        ## everything inside of these brackets are Zed options.
        userSettings = {

            assistant = {
                enabled = true;
                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 = [
                #                    {
                #                        provider = "copilot_chat";
                #                        model = "gpt-3.5-turbo";
                #                    }
                #                ];
            };

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

            hour_format = "hour24";
            auto_update = false;
            terminal = {
                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"; 
                #{
                #                    program = "zsh";
                #};
                toolbar = {
                    title = true;
                };
                working_directory = "current_project_directory";
            };



            lsp = {
                rust-analyzer = {

                    binary = {
                        #                        path = lib.getExe pkgs.rust-analyzer;
                        path_lookup = true;
                    };
                };
                nix = { 
                    binary = { 
                        path_lookup = 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}" "-"];
                        };
                    };
                };
                "HEEX" = {
                    language_servers = ["!lexical" "elixir-ls" "!next-ls"];
                    format_on_save = {
                        external = {
                            command = "mix";
                            arguments = ["format" "--stdin-filename" "{buffer_path}" "-"];
                        };
                    };
                };
            };

            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;

        };

    };
}