{ lib, config, options, pkgs, ... }: let inherit (lib) types; inherit (lib.nixvim) toLuaObject; cfg = config.lsp; oldCfg = config.plugins.lsp; # 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.submoduleWith { # Server modules have a `config` option, so we must use # shorthandOnlyDefinesConfig to avoid confusing the module system. shorthandOnlyDefinesConfig = true; modules = [ (lib.modules.importApply ./server.nix args) { _module.args.pkgs = pkgs; } ]; }; # 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 the default package (opts: opts.package.default or null) # The default throws if mkPackageOption can't find the package # E.g. mismatched nixpkgs revision (package: (builtins.tryEval package).value) # Get package's homepage (package: package.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 = { servers = lib.mkOption { type = types.submodule [ { freeformType = types.attrsOf (mkServerType { }); } { options = builtins.mapAttrs mkServerOption serverPackages; } { # `*` is effectively a meta server, where shared config & defaults can be set. # It shouldn't have options like `activate` or `package` which relate to "real" servers. # Therefore, we use a bespoke `global-server.nix`, which is inspired by the full `server.nix` module. options."*" = lib.mkOption { description = '' Global configuration applied to all language servers. ''; type = types.submodule ./global-server.nix; default = { }; }; } ]; description = '' LSP servers to enable and/or configure. This option is implemented using neovim's `vim.lsp` lua API. You may also want to use [nvim-lspconfig] to install _default configs_ for many language servers. 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 ''; default = { }; example = { "*".config = { root_markers = [ ".git" ]; capabilities.textDocument.semanticTokens = { multilineTokenSupport = true; }; }; lua_ls.enable = true; clangd = { enable = true; config = { cmd = [ "clangd" "--background-index" ]; root_markers = [ "compile_commands.json" "compile_flags.txt" ]; filetypes = [ "c" "cpp" ]; }; }; }; }; }; config = let enabledServers = lib.pipe cfg.servers [ builtins.attrValues (builtins.filter (server: server.enable)) ]; # Collect per-server warnings serverWarnings = lib.pipe cfg.servers [ builtins.attrValues (builtins.catAttrs "warnings") builtins.concatLists ]; packages = lib.pipe enabledServers [ (builtins.catAttrs "packages") (builtins.zipAttrsWith (_: builtins.concatLists)) ]; in { extraPackages = lib.mkIf (packages.prefix or [ ] != [ ]) packages.prefix; extraPackagesAfter = lib.mkIf (packages.suffix or [ ] != [ ]) packages.suffix; lsp.luaConfig.content = let mkServerConfig = server: let luaName = toLuaObject server.name; luaConfig = toLuaObject server.config; wrap = server.__configWrapper or lib.id; in [ (lib.mkIf (server.config != { }) "vim.lsp.config(${luaName}, ${wrap luaConfig})") (lib.mkIf (server.activate or false) "vim.lsp.enable(${luaName})") ]; in lib.mkMerge ( # Implement the legacy settings wrapping and capabilities mutation when `plugins.lsp` is enabled lib.optional oldCfg.enable '' local __lspCapabilities = function() local capabilities = vim.lsp.protocol.make_client_capabilities() ${oldCfg.capabilities} return capabilities end local __setup = ${lib.foldr lib.id "{ capabilities = __lspCapabilities() }" oldCfg.setupWrappers} local __wrapConfig = function(cfg) if cfg == nil then cfg = __setup else cfg = vim.tbl_extend("keep", cfg, __setup) end return cfg end '' ++ builtins.concatMap mkServerConfig enabledServers ); # Propagate per-server warnings warnings = lib.mkIf (serverWarnings != [ ]) serverWarnings; }; }