{ config, lib, pkgs, pkgs-unstable, ... }: let cfg = config.programs.zed-editor; ameba-ls = pkgs.stdenv.mkDerivation rec { pname = "ameba-ls"; version = "0.1.0"; src = let selectSystem = attrs: attrs.${pkgs.stdenv.hostPlatform.system} or (throw "Unsupported system: ${pkgs.stdenv.hostPlatform.system}"); in pkgs.fetchurl (selectSystem { x86_64-linux = { url = "https://github.com/crystal-lang-tools/ameba-ls/releases/download/v${version}/ameba-ls-${version}-x86_64-linux-musl.tar.gz"; hash = "sha256-NtqR8NHytuHT1XIhRVvnp7Lo4Ed9UGbISbTn/BfLGA8="; }; aarch64-linux = { url = "https://github.com/crystal-lang-tools/ameba-ls/releases/download/v${version}/ameba-ls-${version}-aarch64-linux-musl.tar.gz"; hash = "sha256-77iqdaI+Mqivk0/9jkNdGpluDbz297vsY9wzuipcPOU="; }; aarch64-darwin = { url = "https://github.com/crystal-lang-tools/ameba-ls/releases/download/v${version}/ameba-ls-${version}-aarch64-apple-darwin.tar.gz"; hash = "sha256-tDB+ZjgjlGjNg3DjX09z1dJnqTv9h/1sXiauBj7uHNc="; }; }); sourceRoot = "."; installPhase = '' runHook preInstall mkdir -p $out/bin cp ameba-ls $out/bin/ chmod +x $out/bin/ameba-ls runHook postInstall ''; meta = with lib; { description = "Language server for the Ameba linter for Crystal lang"; homepage = "https://github.com/crystal-lang-tools/ameba-ls"; license = licenses.mit; platforms = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ]; mainProgram = "ameba-ls"; }; }; in { options.programs.zed-editor = { userTasks = lib.mkOption { type = lib.types.listOf lib.types.attrs; default = [ ]; }; defaultEditor = lib.mkOption { type = lib.types.bool; default = false; }; }; config = lib.mkIf cfg.enable { programs.zed-editor = { package = pkgs-unstable.zed-editor; extensions = [ "ruby" "crystal" "nix" "svelte" ]; userSettings = { node = { path = lib.getExe pkgs.nodejs_latest; npm_path = lib.getExe' pkgs.nodejs_latest "npm"; }; agent = { enabled = true; default_model = { provider = "zed.dev"; model = "claude-sonnet-4-thinking"; }; }; auto_update = false; telemetry = { diagnostics = false; metrics = false; }; vim_mode = true; languages = { Ruby = { language_servers = [ "ruby-lsp" "rubocop" "!solargraph" ]; formatter.external = { command = pkgs.writeShellScript "rufo" '' bundle exec rufo "$@" if [ $? -eq 1 ]; then exit 1 fi ''; arguments = [ "--filename" "{buffer_path}" ]; }; }; Nix = { formatter.external = { command = lib.getExe pkgs.nixfmt-rfc-style; arguments = [ "-q" ]; }; }; Rust = { formatter.external = { command = lib.getExe pkgs.rustfmt; arguments = [ "--edition" "2018" ]; }; }; Crystal = { language_servers = [ "crystalline" "ameba-ls" ]; formatter.external = { command = lib.getExe pkgs.crystal; arguments = [ "tool" "format" "--no-color" "-" ]; }; }; }; lsp = with pkgs; { nixd.binary.path = lib.getExe nixd; nil.binary.path = lib.getExe nil; ruby-lsp.binary.path = lib.getExe ruby-lsp; rubocop = { binary = { path = "bundle"; arguments = [ "exec" "rubocop" "--lsp" ]; }; }; yaml-language-server.binary = { path = lib.getExe yaml-language-server; arguments = [ "--stdio" ]; }; json-language-server.binary = { path = lib.getExe nodePackages.vscode-json-languageserver; arguments = [ "--stdio" ]; }; package-version-server.binary.path = lib.getExe pkgs-unstable.package-version-server; eslint.settings.onIgnoredFiles = "off"; crystalline.binary.path = lib.getExe crystalline; ameba-ls.binary.path = lib.getExe ameba-ls; rust-analyzer.binary.path = lib.getExe rust-analyzer; }; load_direnv = "shell_hook"; theme = { mode = "system"; light = "One Light"; dark = "One Dark"; }; terminal = { line_height = "standard"; font_family = "IosevkaTerm Nerd Font"; }; edit_predictions = { enabled = true; mode = "eager"; provider = "zed"; }; buffer_font_family = "Iosevka Nerd Font"; buffer_font_size = 16; ui_font_size = 17; relative_line_numbers = true; file_types = { JSONC = [ "tsconfig.json" "tsconfig.*.json" ]; }; context_servers = { browser-tools-context-server = { source = "extension"; settings = { npx_command = "${pkgs.nodejs}/bin/npx"; }; }; }; }; userKeymaps = [ { context = "VimControl && !menu"; bindings = { "space g" = [ "task::Spawn" { task_name = "lazygit"; reveal_target = "center"; } ]; "space r" = "pane::DeploySearch"; "space f" = [ "file_finder::Toggle" { separate_history = true; } ]; "space :" = "task::Spawn"; "g R" = "editor::FindAllReferences"; }; } { context = "Dock || Pane || Editor"; bindings = { "ctrl-h" = "workspace::ActivatePaneLeft"; "ctrl-j" = "workspace::ActivatePaneDown"; "ctrl-k" = "workspace::ActivatePaneUp"; "ctrl-l" = "workspace::ActivatePaneRight"; }; } { context = "Terminal"; bindings = { "ctrl-p" = [ "terminal::SendKeystroke" "ctrl-p" ]; }; } ]; userTasks = [ { label = "lazygit"; command = lib.getExe config.programs.lazygit.package; env = { EDITOR = lib.getExe cfg.package; }; args = [ "-p" "$ZED_WORKTREE_ROOT" ]; reveal = "always"; allow_concurrent_runs = true; use_new_terminal = false; hide = "on_success"; } ]; }; xdg.configFile."zed/tasks.json".text = builtins.toJSON cfg.userTasks; home.sessionVariables = lib.mkIf cfg.defaultEditor { EDITOR = lib.getExe cfg.package; }; }; }