From e34eaf8395855bf8b1b1a40b38f4c9e9abe2a38d Mon Sep 17 00:00:00 2001 From: Matt Sturgeon Date: Tue, 29 Apr 2025 02:44:19 +0100 Subject: [PATCH] modules/lsp/server: declare `package` defaults Convert the `attrsOf (servers.nix)` option to a freeform submodule. Declare a `servers.nix` option for each lsp server listed in `lsp-packages.nix` that has a known nixpkgs package. --- modules/lsp/default.nix | 58 ++++++++++++++++++++++- modules/lsp/server.nix | 67 ++++++++++++++++----------- plugins/by-name/lspconfig/default.nix | 2 +- 3 files changed, 98 insertions(+), 29 deletions(-) diff --git a/modules/lsp/default.nix b/modules/lsp/default.nix index 22cb456d..14754e75 100644 --- a/modules/lsp/default.nix +++ b/modules/lsp/default.nix @@ -2,6 +2,7 @@ lib, config, options, + pkgs, ... }: let @@ -9,6 +10,55 @@ let inherit (lib.nixvim) toLuaObject; cfg = config.lsp; + + # Import `server.nix` and apply args + # For convenience, we set a default here for args.pkgs + mkServerModule = args: lib.modules.importApply ./server.nix ({ inherit pkgs; } // args); + + # Create a submodule type from `server.nix` + # Used as the type for both the freeform `lsp.servers.` + # and the explicitly declared `lsp.servers.*` options + mkServerType = args: types.submodule (mkServerModule args); + + # Create a server option + # Used below for the `lsp.servers.*` options + mkServerOption = + name: args: + let + homepage = lib.pipe options.lsp.servers [ + # Get suboptions of `lsp.servers` + (opt: opt.type.getSubOptions opt.loc) + # Get suboptions of `lsp.servers.` + (opts: opts.${name}.type.getSubOptions opts.${name}.loc) + # Get package option's homepage + (opts: opts.package.default.meta.homepage or null) + ]; + + # If there's a known homepage for this language server, + # we'll link to it in the option description + nameLink = if homepage == null then name else "[${name}](${homepage})"; + in + lib.mkOption { + type = mkServerType args; + description = '' + The ${nameLink} language server. + ''; + default = { }; + }; + + # Combine `packages` and `customCmd` sets from `lsp-packages.nix` + # We use this set to generate the package-option defaults + serverPackages = + let + inherit (import ../../plugins/lsp/lsp-packages.nix) + packages + customCmd + ; + in + builtins.mapAttrs (name: v: { + inherit name; + package = v.package or v; + }) (packages // customCmd); in { options.lsp = { @@ -25,7 +75,11 @@ in }; servers = lib.mkOption { - type = types.attrsOf (types.submodule ./server.nix); + type = types.submodule { + freeformType = types.attrsOf (mkServerType { }); + options = builtins.mapAttrs mkServerOption serverPackages; + }; + description = '' LSP servers to enable and/or configure. @@ -35,7 +89,7 @@ in This can be installed using [`${options.plugins.lspconfig.enable}`][`plugins.lspconfig`]. [nvim-lspconfig]: ${options.plugins.lspconfig.package.default.meta.homepage} - [`plugins.lspconfig`]: ../plugins/lspconfig/index.md + [`plugins.lspconfig`]: ../../plugins/lspconfig/index.md ''; default = { }; example = { diff --git a/modules/lsp/server.nix b/modules/lsp/server.nix index 25e8b761..7b812bbf 100644 --- a/modules/lsp/server.nix +++ b/modules/lsp/server.nix @@ -1,59 +1,74 @@ +# Usage: lib.importApply ./server.nix { /*args*/ } +{ + name ? null, + package ? null, + # Avoid naming conflict with the `config` module arg + # TODO: consider renaming the `config` option to something like `settings`? + configOption ? null, + pkgs ? { }, +}@args: { lib, name, ... }: let inherit (lib) types; + displayName = args.name or "the language server"; + packageName = package.name or (lib.strings.removePrefix "the " displayName); in { options = { - enable = lib.mkEnableOption "the language server"; + enable = lib.mkEnableOption displayName; name = lib.mkOption { type = types.maybeRaw types.str; description = '' - The name of the language server, supplied to functions like `vim.lsp.enable()`. + The name to use for ${displayName}. + Supplied to functions like `vim.lsp.enable()`. ''; - default = name; - defaultText = lib.literalMD "the attribute name"; + # Use the supplied attr name, or fallback to the name module-arg + default = args.name or name; + defaultText = args.name or (lib.literalMD "the attribute name"); }; activate = lib.mkOption { type = types.bool; description = '' - Whether to call `vim.lsp.enable()` for this server. + Whether to call `vim.lsp.enable()` for ${displayName}. ''; default = true; example = false; }; - package = lib.mkOption { - type = with types; nullOr package; - default = null; - description = '' - Package to use for this language server. + package = lib.mkPackageOption pkgs packageName { + nullable = true; + default = package.default or package; + example = package.example or null; + extraDescription = '' + ${package.extraDescription or ""} - Alternatively, the language server should be available on your `$PATH`. + Alternatively, ${displayName} should be installed on your `$PATH`. ''; }; config = lib.mkOption { type = with types; attrsOf anything; description = '' - Configurations for each language server. + Configurations for ${displayName}. ${configOption.extraDescription or ""} ''; default = { }; - example = { - cmd = [ - "clangd" - "--background-index" - ]; - root_markers = [ - "compile_commands.json" - "compile_flags.txt" - ]; - filetypes = [ - "c" - "cpp" - ]; - }; + example = + configOption.example or { + cmd = [ + "clangd" + "--background-index" + ]; + root_markers = [ + "compile_commands.json" + "compile_flags.txt" + ]; + filetypes = [ + "c" + "cpp" + ]; + }; }; }; } diff --git a/plugins/by-name/lspconfig/default.nix b/plugins/by-name/lspconfig/default.nix index ee77f8d2..24a35636 100644 --- a/plugins/by-name/lspconfig/default.nix +++ b/plugins/by-name/lspconfig/default.nix @@ -35,7 +35,7 @@ lib.nixvim.plugins.mkNeovimPlugin { > setup that relate to neovim's builtin LSP and are now being moved to the > new [`lsp`] module. - [`lsp`]: ../../lsp/servers.md + [`lsp`]: ../../lsp/servers/index.md [`plugins.lsp`]: ../lsp/index.md [nvim-lspconfig]: ${opts.package.default.meta.homepage} '';