1
0
Fork 0
mirror of https://github.com/nix-community/nixvim.git synced 2025-12-07 17:41:04 +01:00

plugins/lsp: use the new lsp module under the hood

- Re-implement setup wrapping and capabilities to preserve existing
  behaviour
- Alias `package` options to new lsp module
- Alias `packageFallback` options to new lsp module
- Alias `preConfig` and `postConfig` to `lsp.luaConfig`
This commit is contained in:
Matt Sturgeon 2025-09-28 15:46:36 +01:00 committed by Gaétan Lepage
parent cb3653a1a8
commit 94331cc50d
4 changed files with 148 additions and 135 deletions

View file

@ -10,6 +10,7 @@ let
inherit (lib.nixvim) toLuaObject; inherit (lib.nixvim) toLuaObject;
cfg = config.lsp; cfg = config.lsp;
oldCfg = config.plugins.lsp;
# Import `server.nix` and apply args # Import `server.nix` and apply args
# For convenience, we set a default here for args.pkgs # For convenience, we set a default here for args.pkgs
@ -156,13 +157,37 @@ in
let let
luaName = toLuaObject server.name; luaName = toLuaObject server.name;
luaSettings = toLuaObject server.settings; luaSettings = toLuaObject server.settings;
wrap = server.__settingsWrapper or lib.id;
in in
[ [
(lib.mkIf (server.settings != { }) "vim.lsp.config(${luaName}, ${luaSettings})") (lib.mkIf (server.settings != { }) "vim.lsp.config(${luaName}, ${wrap luaSettings})")
(lib.mkIf (server.activate or false) "vim.lsp.enable(${luaName})") (lib.mkIf (server.activate or false) "vim.lsp.enable(${luaName})")
]; ];
in in
lib.mkMerge (builtins.concatMap mkServerConfig enabledServers); 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 __wrapSettings = function(settings)
if settings == nil then
settings = __setup
else
settings = vim.tbl_extend("keep", settings, __setup)
end
return settings
end
''
++ builtins.concatMap mkServerConfig enabledServers
);
# Propagate per-server warnings # Propagate per-server warnings
warnings = lib.mkIf (serverWarnings != [ ]) serverWarnings; warnings = lib.mkIf (serverWarnings != [ ]) serverWarnings;

View file

@ -1,4 +1,9 @@
{ lib, config, ... }: {
lib,
config,
options,
...
}:
let let
inherit (lib) mkOption types; inherit (lib) mkOption types;
in in
@ -11,7 +16,10 @@ lib.nixvim.plugins.mkNeovimPlugin {
maintainers = [ lib.maintainers.HeitorAugustoLN ]; maintainers = [ lib.maintainers.HeitorAugustoLN ];
imports = [ ./language-servers ]; imports = [
./language-servers
(lib.mkRemovedOptionModule [ "plugins" "lsp" "enabledServers" ] "Internal option has been removed.")
];
extraOptions = { extraOptions = {
keymaps = { keymaps = {
@ -77,28 +85,6 @@ lib.nixvim.plugins.mkNeovimPlugin {
}; };
}; };
enabledServers = mkOption {
type =
with types;
listOf (submodule {
options = {
name = mkOption {
type = str;
description = "The server's name";
};
extraOptions = mkOption {
type = attrsOf anything;
description = "Extra options for the server";
};
};
});
description = "A list of enabled LSP servers. Don't use this directly.";
default = [ ];
internal = true;
visible = false;
};
inlayHints = mkOption { inlayHints = mkOption {
type = types.bool; type = types.bool;
default = false; default = false;
@ -136,13 +122,33 @@ lib.nixvim.plugins.mkNeovimPlugin {
preConfig = mkOption { preConfig = mkOption {
type = types.lines; type = types.lines;
description = "Code to be run before loading the LSP. Useful for requiring plugins"; description = "Code to be run before loading the LSP. Useful for requiring plugins";
default = ""; # When `plugins.lsp` is enabled, definitions are aliased to `lsp.luaConfig.pre`; so read that final value here.
# This is slightly complicated because `lsp.luaConfig.pre` is nullable, unlike this option.
# The other half of this two-way alias is below in `extraConfig`.
apply =
value:
if config.plugins.lsp.enable then
if config.lsp.luaConfig.pre == null then "" else config.lsp.luaConfig.pre
else if options.plugins.lsp.preConfig.isDefined then
value
else
"";
}; };
postConfig = mkOption { postConfig = mkOption {
type = types.lines; type = types.lines;
description = "Code to be run after loading the LSP. This is an internal option"; description = "Code to be run after loading the LSP. This is an internal option";
default = ""; # When `plugins.lsp` is enabled, definitions are aliased to `lsp.luaConfig.post`; so read that final value here.
# This is slightly complicated because `lsp.luaConfig.post` is nullable, unlike this option.
# The other half of this two-way alias is below in `extraConfig`.
apply =
value:
if config.plugins.lsp.enable then
if config.lsp.luaConfig.post == null then "" else config.lsp.luaConfig.post
else if options.plugins.lsp.postConfig.isDefined then
value
else
"";
}; };
}; };
@ -195,46 +201,8 @@ lib.nixvim.plugins.mkNeovimPlugin {
lsp = { lsp = {
onAttach = lib.modules.mkAliasAndWrapDefsWithPriority lib.id opts.onAttach; onAttach = lib.modules.mkAliasAndWrapDefsWithPriority lib.id opts.onAttach;
inlayHints.enable = lib.modules.mkAliasAndWrapDefsWithPriority lib.id opts.inlayHints; inlayHints.enable = lib.modules.mkAliasAndWrapDefsWithPriority lib.id opts.inlayHints;
luaConfig.pre = lib.modules.mkAliasAndWrapDefsWithPriority lib.id opts.preConfig;
luaConfig.post = lib.modules.mkAliasAndWrapDefsWithPriority lib.id opts.postConfig;
}; };
plugins.lsp.luaConfig.content =
let
runWrappers =
wrappers: s:
if wrappers == [ ] then s else (builtins.head wrappers) (runWrappers (builtins.tail wrappers) s);
in
''
-- nvim-lspconfig {{{
do
${cfg.preConfig}
local __lspServers = ${lib.nixvim.toLuaObject cfg.enabledServers}
local __lspCapabilities = function()
capabilities = vim.lsp.protocol.make_client_capabilities()
${cfg.capabilities}
return capabilities
end
local __setup = ${runWrappers cfg.setupWrappers "{ capabilities = __lspCapabilities() }"}
for i, server in ipairs(__lspServers) do
local options = ${runWrappers cfg.setupWrappers "server.extraOptions"}
if options == nil then
options = __setup
else
options = vim.tbl_extend("keep", options, __setup)
end
require("lspconfig")[server.name].setup(options)
end
${cfg.postConfig}
end
-- }}}
'';
}; };
} }

View file

@ -20,7 +20,6 @@
}@args: }@args:
# returns a module # returns a module
{ {
pkgs,
config, config,
options, options,
lib, lib,
@ -51,9 +50,41 @@ in
plugins.lsp.servers.${name} = { plugins.lsp.servers.${name} = {
enable = lib.mkEnableOption description; enable = lib.mkEnableOption description;
# alias to lsp.servers.${name}.package
package = package =
lib.nixvim.mkMaybeUnpackagedOption "plugins.lsp.servers.${name}.package" pkgs name let
package; getSubOptions = opt: opt.type.getSubOptions opt.loc;
serverOpts = getSubOptions options.lsp.servers;
opt = lib.pipe serverOpts [
(lib.getAttr serverName)
getSubOptions
(lib.getAttr "package")
];
pkg = config.lsp.servers.${serverName}.package;
self = options.plugins.lsp.servers.${name}.package;
in
if serverOpts ? ${serverName} then
lib.mkOption (
{
inherit (opt) type default description;
apply =
v:
if enabled then
pkg
else if self.isDefined then
v
else
opt.default or v;
}
// lib.optionalAttrs (opt ? example) { inherit (opt) example; }
// lib.optionalAttrs (opt ? defaultText) { inherit (opt) defaultText; }
)
else
# If there's no explicit option, that means there isn't a known package, so the server uses freeformType
lib.nixvim.mkUnpackagedOption options.plugins.lsp.servers.${name}.package name
// {
apply = v: if enabled then config.lsp.servers.${serverName}.packageFallback else v;
};
packageFallback = mkOption { packageFallback = mkOption {
type = types.bool; type = types.bool;
@ -63,6 +94,7 @@ in
This can be useful if you want local versions of the language server (e.g. from a devshell) to override the nixvim version. This can be useful if you want local versions of the language server (e.g. from a devshell) to override the nixvim version.
''; '';
apply = v: if enabled then config.lsp.servers.${serverName}.packageFallback else v;
}; };
cmd = mkOption { cmd = mkOption {
@ -159,16 +191,40 @@ in
}; };
config = lib.mkIf enabled { config = lib.mkIf enabled {
extraPackages = lib.optional (!cfg.packageFallback) cfg.package; # The server submodule is using `shorthandOnlyDefinesConfig`,
extraPackagesAfter = lib.optional cfg.packageFallback cfg.package; # so define a "longhand" function module.
lsp.servers.${serverName} = _: {
# Top-secret internal option that only exists when the server is enabled via the old API.
# The new API checks if this attr exists and uses it to wrap the server's settings string.
options.__settingsWrapper = lib.mkOption {
type = lib.types.functionTo lib.types.str;
description = ''
This internal option exists to preserve the old `plugins.lsp` behaviour.
plugins.lsp.enabledServers = [ > [!IMPORTANT]
{ > This option should not be used by end-users!
name = serverName; > It will be removed when `plugins.lsp` is dropped.
extraOptions = { '';
inherit (cfg) cmd filetypes autostart; readOnly = true;
root_markers = cfg.rootMarkers; internal = true;
on_attach = lib.nixvim.ifNonNull' cfg.onAttach ( visible = false;
};
# Propagate definitions to the new lsp module
config = {
enable = true;
package = lib.mkIf (opts.package.highestPrio < 1500) (
lib.modules.mkAliasAndWrapDefsWithPriority lib.id opts.package
);
packageFallback = lib.modules.mkAliasAndWrapDefsWithPriority lib.id opts.packageFallback;
__settingsWrapper =
settings: "__wrapSettings(${lib.foldr lib.id settings config.plugins.lsp.setupWrappers})";
settings = {
autostart = lib.mkIf (cfg.autostart != null) cfg.autostart;
cmd = lib.mkIf (cfg.cmd != null) cfg.cmd;
filetypes = lib.mkIf (cfg.filetypes != null) cfg.filetypes;
root_markers = lib.mkIf (cfg.rootMarkers != null) cfg.rootMarkers;
on_attach = lib.mkIf (cfg.onAttach != null) (
lib.nixvim.mkRaw '' lib.nixvim.mkRaw ''
function(client, bufnr) function(client, bufnr)
${lib.optionalString (!cfg.onAttach.override) config.plugins.lsp.onAttach} ${lib.optionalString (!cfg.onAttach.override) config.plugins.lsp.onAttach}
@ -184,8 +240,8 @@ in
}; };
} }
// cfg.extraOptions; // cfg.extraOptions;
} };
]; };
}; };
imports = imports =

View file

@ -173,25 +173,13 @@
"example" "example"
]; ];
}; };
enabledServers = lib.mkAfter [
{
name = "second";
extraOptions.settings = lib.mkIf false {
should.be = "missing";
};
}
{
name = "third";
extraOptions.settings = lib.mkIf true {
should.be = "present";
};
}
];
}; };
}; };
assertions = assertions =
let let
enabledServers = builtins.filter (server: server.enable) (builtins.attrValues config.lsp.servers);
toLua = lib.nixvim.lua.toLua' { toLua = lib.nixvim.lua.toLua' {
removeNullAttrValues = true; removeNullAttrValues = true;
removeEmptyAttrValues = true; removeEmptyAttrValues = true;
@ -200,39 +188,35 @@
multiline = true; multiline = true;
}; };
print = lib.generators.toPretty { serverCount = builtins.length enabledServers;
multiline = true; expectedCount = 2;
};
serverCount = builtins.length config.plugins.lsp.enabledServers; baseServer = builtins.elemAt enabledServers 0;
expectedCount = 3;
nilServer = builtins.head config.plugins.lsp.enabledServers; nilServer = builtins.elemAt enabledServers 1;
nilSettings = toLua nilServer.extraOptions.settings; nilSettings = toLua nilServer.settings.settings;
expectedNilSettings = toLua { expectedNilSettings = toLua {
nil.formatting.command = [ nil.formatting.command = [
"real" "real"
"example" "example"
]; ];
}; };
secondServer = builtins.elemAt config.plugins.lsp.enabledServers 1;
expectedSecondServer = {
name = "second";
extraOptions = { };
};
thirdServer = builtins.elemAt config.plugins.lsp.enabledServers 2;
expectedThirdServer = {
name = "third";
extraOptions.settings.should.be = "present";
};
in in
[ [
{ {
assertion = serverCount == expectedCount; assertion = serverCount == expectedCount;
message = "Expected ${toString expectedCount} enabled LSP server!"; message = "Expected ${toString expectedCount} enabled LSP server!";
} }
{
assertion = baseServer.name == "*";
message = ''
baseServer's `name` does not match expected value.
Expected: "*"
Actual: ${baseServer.name}
'';
}
{ {
assertion = nilSettings == expectedNilSettings; assertion = nilSettings == expectedNilSettings;
message = '' message = ''
@ -243,26 +227,6 @@
Actual: ${nilSettings} Actual: ${nilSettings}
''; '';
} }
{
assertion = secondServer == expectedSecondServer;
message = ''
`secondServer` does not match expected value.
Expected: ${print expectedSecondServer}
Actual: ${print secondServer}
'';
}
{
assertion = secondServer == expectedSecondServer;
message = ''
`thirdServer` does not match expected value.
Expected: ${print expectedThirdServer}
Actual: ${print thirdServer}
'';
}
]; ];
}; };