nvf/modules/plugins/languages/nix.nix
2025-07-01 20:28:04 +02:00

297 lines
7.7 KiB
Nix

{
config,
pkgs,
lib,
inputs,
...
}: let
inherit (builtins) attrNames;
inherit (lib) concatStringsSep mkLuaInline;
inherit (lib.meta) getExe;
inherit (lib.options) mkEnableOption mkOption;
inherit (lib.modules) mkIf mkMerge;
inherit (lib.lists) isList;
inherit (lib.strings) optionalString;
inherit
(lib.types)
anything
attrsOf
enum
either
listOf
nullOr
package
str
;
inherit (lib.nvim.types) mkGrammarOption diagnostics;
inherit (lib.nvim.lua) expToLua toLuaObject;
cfg = config.vim.languages.nix;
useFormat = "on_attach = default_on_attach";
noFormat = "on_attach = attach_keymaps";
defaultServer = "nil";
packageToCmd = package: defaultCmd:
if isList package
then expToLua package
else ''{"${package}/bin/${defaultCmd}"}'';
servers = {
nil = {
package = inputs.nil.packages.${pkgs.stdenv.system}.nil;
internalFormatter = true;
lspConfig = ''lspconfig.nil_ls.setup ${
toLuaObject {
capabilities = mkLuaInline "capabilities";
on_attach =
if cfg.format.enable
then mkLuaInline "default_on_attach"
else "attach_keymaps";
cmd = [(packageToCmd cfg.lsp.package "nil")];
settings.nil =
{
formatting.command =
if cfg.format.enable
then
if cfg.format.type == "alejandra"
then ''{"${cfg.format.package}/bin/alejandra", "--quiet"}''
else if cfg.format.type == "nixfmt"
then ''{"${cfg.format.package}/bin/nixfmt"}''
else null
else null;
}
// cfg.lsp.options;
}
}'';
# lspConfig = ''
# lspconfig.nil_ls.setup{
# capabilities = capabilities,
# ${
# if cfg.format.enable
# then useFormat
# else noFormat
# },
# cmd = ${packageToCmd cfg.lsp.package "nil"},
# ${optionalString cfg.format.enable ''
# settings = {
# ["nil"] = {
# ${optionalString (cfg.format.type == "alejandra") ''
# formatting = {
# command = {"${cfg.format.package}/bin/alejandra", "--quiet"},
# },
# ''}
# ${optionalString (cfg.format.type == "nixfmt") ''
# formatting = {
# command = {"${cfg.format.package}/bin/nixfmt"},
# },
# ''}
# nix = ${toLuaObject cfg.lsp.options},
# },
# },
# ''}
# }
# '';
};
nixd = {
package = pkgs.nixd;
internalFormatter = true;
lspConfig = ''
lspconfig.nixd.setup{
capabilities = capabilities,
${
if cfg.format.enable
then useFormat
else noFormat
},
cmd = ${packageToCmd cfg.lsp.package "nixd"},
${optionalString cfg.format.enable ''
settings = {
nixd = {
${optionalString (cfg.format.type == "alejandra") ''
formatting = {
command = {"${cfg.format.package}/bin/alejandra", "--quiet"},
},
''}
${optionalString (cfg.format.type == "nixfmt") ''
formatting = {
command = {"${cfg.format.package}/bin/nixfmt"},
},
''}
nix = ${toLuaObject cfg.lsp.options},
},
},
''}
}
'';
};
};
defaultFormat = "alejandra";
formats = {
alejandra = {
package = pkgs.alejandra;
};
nixfmt = {
package = pkgs.nixfmt-rfc-style;
};
};
defaultDiagnosticsProvider = [
"statix"
"deadnix"
];
diagnosticsProviders = {
statix = {
package = pkgs.statix;
nullConfig = pkg: ''
table.insert(
ls_sources,
null_ls.builtins.diagnostics.statix.with({
command = "${pkg}/bin/statix",
})
)
'';
};
deadnix = {
package = pkgs.deadnix;
nullConfig = pkg: ''
table.insert(
ls_sources,
null_ls.builtins.diagnostics.deadnix.with({
command = "${pkg}/bin/deadnix",
})
)
'';
};
};
in {
options.vim.languages.nix = {
enable = mkEnableOption "Nix language support";
treesitter = {
enable =
mkEnableOption "Nix treesitter"
// {
default = config.vim.languages.enableTreesitter;
};
package = mkGrammarOption pkgs "nix";
};
lsp = {
enable =
mkEnableOption "Nix LSP support"
// {
default = config.vim.lsp.enable;
};
server = mkOption {
description = "Nix LSP server to use";
type = enum (attrNames servers);
default = defaultServer;
};
package = mkOption {
description = "Nix LSP server package, or the command to run as a list of strings";
example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]'';
type = either package (listOf str);
default = servers.${cfg.lsp.server}.package;
};
options = mkOption {
type = nullOr (attrsOf anything);
default = null;
description = "Options to pass to nixd LSP server";
};
};
format = {
enable =
mkEnableOption "Nix formatting"
// {
default = config.vim.languages.enableFormat;
};
type = mkOption {
description = "Nix formatter to use";
type = enum (attrNames formats);
default = defaultFormat;
};
package = mkOption {
description = "Nix formatter package";
type = package;
default = formats.${cfg.format.type}.package;
};
};
extraDiagnostics = {
enable =
mkEnableOption "extra Nix diagnostics"
// {
default = config.vim.languages.enableExtraDiagnostics;
};
types = diagnostics {
langDesc = "Nix";
inherit diagnosticsProviders;
inherit defaultDiagnosticsProvider;
};
};
};
config = mkIf cfg.enable (mkMerge [
{
assertions = [
{
assertion = cfg.format.type != "nixpkgs-fmt";
message = ''
nixpkgs-fmt has been archived upstream. Please use one of the following available formatters:
${concatStringsSep ", " (attrNames formats)}
'';
}
{
assertion = cfg.lsp.server != "rnix";
message = ''
rnix-lsp has been archived upstream. Please use one of the following available language servers:
${concatStringsSep ", " (attrNames servers)}
'';
}
];
}
(mkIf cfg.treesitter.enable {
vim.treesitter.enable = true;
vim.treesitter.grammars = [cfg.treesitter.package];
})
(mkIf cfg.lsp.enable {
vim.lsp.lspconfig.enable = true;
vim.lsp.lspconfig.sources.nix-lsp = servers.${cfg.lsp.server}.lspConfig;
})
(mkIf (cfg.format.enable && (!cfg.lsp.enable || !servers.${cfg.lsp.server}.internalFormatter)) {
vim.formatter.conform-nvim = {
enable = true;
setupOpts.formatters_by_ft.nix = [cfg.format.type];
setupOpts.formatters.${cfg.format.type} = {
command = getExe cfg.format.package;
};
};
})
(mkIf cfg.extraDiagnostics.enable {
vim.diagnostics.nvim-lint = {
enable = true;
linters_by_ft.nix = cfg.extraDiagnostics.types;
linters = mkMerge (
map (name: {
${name}.cmd = getExe diagnosticsProviders.${name}.package;
})
cfg.extraDiagnostics.types
);
};
})
]);
}