Files
nixos/home/modules/zed/default.nix
2026-05-29 19:23:22 +03:00

375 lines
10 KiB
Nix

{
config,
lib,
pkgs,
pkgs-unstable,
inputs,
...
}:
let
cfg = config.programs.zed-editor;
models-dev = inputs.models-dev;
getModelsFromProvider =
provider:
let
providerPath = "${models-dev}/providers/${provider}";
modelsPath = "${providerPath}/models";
providerToml = builtins.fromTOML (builtins.readFile "${providerPath}/provider.toml");
findAllTomlFiles =
path:
let
entries = builtins.readDir path;
files = builtins.filter (name: builtins.match ".*\\.toml$" name != null) (
builtins.attrNames (lib.filterAttrs (_: type: type == "regular") entries)
);
dirs = builtins.attrNames (lib.filterAttrs (_: type: type == "directory") entries);
nestedFiles = builtins.concatMap (
dir:
let
nestedPath = "${path}/${dir}";
nestedTomlFiles = findAllTomlFiles nestedPath;
in
map (file: "${dir}/${file}") nestedTomlFiles
) dirs;
in
files ++ nestedFiles;
modelFiles = findAllTomlFiles modelsPath;
modelEntries = map (
file:
let
filePath = "${modelsPath}/${file}";
parsed = builtins.fromTOML (builtins.readFile filePath);
modelName = builtins.substring 0 (builtins.stringLength file - 5) file;
in
{
name = modelName;
display_name = parsed.name or modelName;
max_tokens = parsed.limit.context or parsed.limit.output or 128000;
}
) modelFiles;
in
{
api_url = providerToml.api;
available_models = builtins.sort (a: b: a.name < b.name) modelEntries;
name = providerToml.name;
};
opencodeProviders = map getModelsFromProvider [
"opencode"
"opencode-go"
"fireworks-ai"
"firepass"
];
openaiCompatibleProviders = builtins.listToAttrs (
map (p: lib.nameValuePair p.name p) opencodeProviders
);
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 = {
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 = "opencode-go";
model = "glm-5";
};
};
agent_servers = {
OpenCode = {
command = "opencode";
args = [ "acp" ];
type = "custom";
};
};
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"
"-"
];
};
};
TypeScript.language_servers = [
"tsgo"
"vtsls"
];
};
lsp = with pkgs; {
nixd.binary.path = lib.getExe nixd;
nil.binary.path = lib.getExe nil;
ruby-lsp = {
initialization_options = {
enabledFeatures = {
codeActions = true;
codeLens = true;
completion = true;
definition = true;
diagnostics = true;
documentHighlights = true;
documentLink = true;
documentSymbols = true;
foldingRanges = true;
formatting = false;
hover = true;
inlayHint = true;
onTypeFormatting = true;
selectionRanges = true;
semanticHighlighting = true;
signatureHelp = true;
typeHierarchy = true;
workspaceSymbol = true;
};
linters = [ "standard" ];
};
settings.use_bundler = true;
};
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 = {
mode = "eager";
provider = "copilot";
};
buffer_font_family = "Iosevka Nerd Font";
buffer_font_size = 16;
ui_font_size = 17;
relative_line_numbers = "enabled";
file_types = {
JSONC = [
"tsconfig.json"
"tsconfig.*.json"
];
};
language_models = {
openai_compatible = openaiCompatibleProviders;
};
};
userKeymaps = [
{
context = "VimControl && !menu";
bindings = {
"space g" = [
"task::Spawn"
{
task_name = "lazygit";
reveal_target = "center";
}
];
"space o" = [
"task::Spawn"
{
task_name = "opencode";
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";
}
{
label = "opencode";
command = "opencode";
reveal = "always";
allow_concurrent_runs = true;
use_new_terminal = false;
hide = "on_success";
}
];
};
home.sessionVariables = lib.mkIf cfg.defaultEditor {
EDITOR = "${lib.getExe cfg.package} -w";
};
};
}