Repository: nix-community/nixvim
Pull request #3203: modules/lsp: init
Author: login: GaetanLepage
Created: 2025-04-22T14:31:04Z | Merged: 2025-04-28T16:17:21Z

Introduce a top-level `lsp` Nixvim module to wrap Neovim's `vim.lsp` API.

Example:
```nix
lsp.servers = {
  luals.enable = true;
  clangd = {
    enable = true;
    config = {
      cmd = [
        "clangd"
        "--background-index"
      ];
      root_markers = [
        "compile_commands.json"
        "compile_flags.txt"
      ];
      filetypes = [
        "c"
        "cpp"
      ];
    };
  };
};
```

References:
- https://gpanders.com/blog/whats-new-in-neovim-0-11/#lsp
- https://neovim.io/doc/user/lsp.html#lsp-quickstart

Note: this is distinct from our `plugins.lsp` module which is backed by [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig).



Diff:
diff --git a/modules/default.nix b/modules/default.nix
index b0fa8f547a..085eaf5db3 100644
--- a/modules/default.nix
+++ b/modules/default.nix
@@ -18,6 +18,7 @@
     ./highlights.nix
     ./keymaps.nix
     ./lazyload.nix
+    ./lsp.nix
     ./lua-loader.nix
     ./opts.nix
     ./output.nix
diff --git a/modules/lsp.nix b/modules/lsp.nix
new file mode 100644
index 0000000000..49291ddc25
--- /dev/null
+++ b/modules/lsp.nix
@@ -0,0 +1,151 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+  inherit (lib.nixvim) toLuaObject;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      activate = lib.mkOption {
+        type = types.bool;
+        description = ''
+          Whether to call `vim.lsp.enable()` for this server.
+        '';
+        default = true;
+        example = false;
+      };
+
+      package = lib.mkOption {
+        type = with types; nullOr package;
+        default = null;
+        description = ''
+          Package to use for this language server.
+
+          Alternatively, the language server should be available on your `$PATH`.
+        '';
+      };
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    luaConfig = lib.mkOption {
+      type = lib.types.pluginLuaConfig;
+      default = { };
+      description = ''
+        Lua code configuring LSP.
+      '';
+    };
+
+    inlayHints = {
+      enable = lib.mkEnableOption "inlay hints globally";
+    };
+
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        LSP servers to enable and/or configure.
+
+        This option is implemented using neovim's `vim.lsp` lua API.
+        If you prefer to use [nvim-lspconfig], see [`plugins.lsp`].
+
+        [nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig
+        [`plugins.lsp`]: https://nix-community.github.io/nixvim/plugins/lsp/index.html
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.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.filterAttrs (_: v: v.enable) cfg.servers;
+    in
+    {
+      extraPackages = lib.pipe enabledServers [
+        builtins.attrValues
+        (builtins.catAttrs "package")
+      ];
+
+      lsp.luaConfig.content =
+        let
+          mkServerConfig =
+            name: props:
+            let
+              luaName = toLuaObject name;
+            in
+            ''
+              vim.lsp.config(${luaName}, ${toLuaObject props.config})
+            ''
+            + lib.optionalString props.activate ''
+              vim.lsp.enable(${luaName})
+            '';
+        in
+        lib.mkMerge (
+          lib.optional cfg.inlayHints.enable "vim.lsp.inlay_hint.enable(true)"
+          ++ lib.mapAttrsToList mkServerConfig enabledServers
+        );
+
+      extraConfigLua = lib.mkIf (cfg.luaConfig.content != "") ''
+        -- LSP {{{
+        do
+          ${cfg.luaConfig.content}
+        end
+        -- }}}
+      '';
+    };
+}
diff --git a/plugins/lsp/default.nix b/plugins/lsp/default.nix
index ed45b5dab6..30573b1f04 100644
--- a/plugins/lsp/default.nix
+++ b/plugins/lsp/default.nix
@@ -180,7 +180,7 @@ lib.nixvim.plugins.mkNeovimPlugin {
           if wrappers == [ ] then s else (builtins.head wrappers) (runWrappers (builtins.tail wrappers) s);
       in
       ''
-        -- LSP {{{
+        -- nvim-lspconfig {{{
         do
           ${cfg.preConfig}
 
diff --git a/tests/test-sources/modules/lsp.nix b/tests/test-sources/modules/lsp.nix
new file mode 100644
index 0000000000..9b645bdcc3
--- /dev/null
+++ b/tests/test-sources/modules/lsp.nix
@@ -0,0 +1,30 @@
+{
+  example = {
+    lsp.servers = {
+      "*".config = {
+        root_markers = [ ".git" ];
+        capabilities.textDocument.semanticTokens = {
+          multilineTokenSupport = true;
+        };
+      };
+      luals.enable = true;
+      clangd = {
+        enable = true;
+        config = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+}


Reviews:
user: login: HeitorAugustoLN
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T20:53:26Z
user: login: GaetanLepage
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T21:10:59Z
user: login: MattSturgeon
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T21:47:19Z
user: login: MattSturgeon
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T21:49:37Z
user: login: GaetanLepage
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T21:49:59Z
user: login: GaetanLepage
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T21:51:09Z
user: login: HeitorAugustoLN
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T21:59:56Z
user: login: MattSturgeon
body: Looks good. Minor nits:
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T22:01:37Z
user: login: khaneliman
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T00:17:38Z
user: login: khaneliman
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T00:19:44Z
user: login: khaneliman
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T00:24:09Z
user: login: HeitorAugustoLN
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T00:46:28Z
user: login: MattSturgeon
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T00:52:55Z
user: login: MattSturgeon
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T01:23:27Z
user: login: GaetanLepage
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T07:21:06Z
user: login: GaetanLepage
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T07:21:16Z
user: login: GaetanLepage
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T07:45:29Z
user: login: MattSturgeon
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T07:49:11Z
user: login: GaetanLepage
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T08:01:02Z
user: login: MattSturgeon
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-26T20:24:40Z
user: login: MattSturgeon
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-26T20:44:00Z
user: login: MattSturgeon
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-26T21:17:13Z
user: login: MattSturgeon
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-26T21:21:21Z
user: login: HeitorAugustoLN
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-26T21:46:00Z
user: login: MattSturgeon
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-26T21:55:03Z
user: login: GaetanLepage
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-28T14:09:10Z
user: login: MattSturgeon
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-28T14:44:58Z
user: login: MattSturgeon
body: LGTM. Do you want to rebase or just queue?
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-28T16:08:13Z

Review comments:
diff_hunk: @@ -0,0 +1,103 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = mkOption {
+        type = types.bool;
+        description = ''
+
+        '';
path: modules/lsp.nix
user: login: HeitorAugustoLN
body: Looks like it is missing a description
created_at: 2025-04-22T20:48:17Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,103 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = mkOption {
+        type = types.bool;
+        description = ''
+
+        '';
path: modules/lsp.nix
user: login: GaetanLepage
body: Thanks! Fixed.
created_at: 2025-04-22T21:10:59Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
path: modules/lsp.nix
user: login: MattSturgeon
body: Perhaps going forward we should consider using `(pkgs.formats.lua { }).type`?

This is a bit more restrictive than `anything`: https://github.com/NixOS/nixpkgs/blob/55024d64cdf0cd2a0a2764857962a1da28a6ee23/pkgs/pkgs-lib/formats.nix#L722-L740

```nix
nullOr (oneOf [
  bool
  float
  int
  path
  str
  luaInline
  (attrsOf valueType)
  (listOf valueType)
])
```

That said, it only supports our non-standard "raw lua" by accident; `attrsOf str` _happens_ to match `{ __raw = ""; }`...

We should probably have this discussion outside of this PR, as using `attrsOf anything` is consistent with our current approach throughout nixvim.
created_at: 2025-04-22T21:47:19Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
path: modules/lsp.nix
user: login: MattSturgeon
body: Q: what other options do you see being introduced in the `lsp.*` namespace?

Would it ever make sense to rename `lsp.servers` to simply `lsp`?
created_at: 2025-04-22T21:49:36Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
path: modules/lsp.nix
user: login: GaetanLepage
body: > Perhaps going forward we should consider using `(pkgs.formats.lua { }).type`?
> 
> This is a bit more restrictive than `anything`: https://github.com/NixOS/nixpkgs/blob/55024d64cdf0cd2a0a2764857962a1da28a6ee23/pkgs/pkgs-lib/formats.nix#L722-L740
> 
> ```nix
> nullOr (oneOf [
>   bool
>   float
>   int
>   path
>   str
>   luaInline
>   (attrsOf valueType)
>   (listOf valueType)
> ])
> ```
> 
> That said, it only supports our non-standard "raw lua" by accident; `attrsOf str` _happens_ to match `{ __raw = ""; }`...
> 
> We should probably have this discussion outside of this PR, as using `attrsOf anything` is consistent with our current approach throughout nixvim.

I agree, this is definitely worth discussing. But this PR is not the right place.
created_at: 2025-04-22T21:49:59Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
path: modules/lsp.nix
user: login: GaetanLepage
body: I am wondering about this too.
Maybe we would want a global `enable` toggle? Although I don't think it is really necessary for now.
I am not certain about how this API will evolve.
created_at: 2025-04-22T21:51:09Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        Wrapper of the `vim.lsp` API.
path: modules/lsp.nix
user: login: MattSturgeon
body: This feels overly technical; we could/should include this as a note within the description, but ideally the description should first describe the option; what it is for; etc

```suggestion
        LSP servers to enable and/or configure.

        This option is implemented using neovim's `vim.lsp` lua API.
        If you prefer to use [nvim-lspconfig], see [`plugins.lsp`].

        [nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig
        [`plugins.lsp`]: https://nix-community.github.io/nixvim/plugins/lsp/index.html
```

Maybe the description should also mention whether or not the attr-name is significant. It translates to `vim.lsp.config[${name}]`, however I can't tell how significant that is to the `vim.lsp.config` api.

EDIT: it seems it is _at least_ significant for compatibility with nvim-lspconfig, since you need to use the same server names that they do in order to benefit from their config defaults.
created_at: 2025-04-22T21:54:55Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
path: modules/lsp.nix
user: login: HeitorAugustoLN
body: inlayHints, keymaps, etc. could be in there too
created_at: 2025-04-22T21:59:56Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        Wrapper of the `vim.lsp` API.
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.enable = true;
+        clangd = {
+          enable = true;
+          config = {
+            cmd = [
+              "clangd"
+              "--background-index"
+            ];
+            root_markers = [
+              "compile_commands.json"
+              "compile_flags.txt"
+            ];
+            filetypes = [
+              "c"
+              "cpp"
+            ];
+          };
+        };
+      };
+    };
+  };
+
+  config = {
+    extraConfigLua =
+      let
+        serversToEnable = lib.pipe cfg.servers [
+          (lib.filterAttrs (_: props: props.enable))
+          (lib.mapAttrsToList (serverName: _: serverName))
path: modules/lsp.nix
user: login: MattSturgeon
body: ```suggestion
          builtins.attrNames
```
created_at: 2025-04-22T22:00:57Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        Wrapper of the `vim.lsp` API.
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.enable = true;
+        clangd = {
+          enable = true;
+          config = {
+            cmd = [
+              "clangd"
+              "--background-index"
+            ];
+            root_markers = [
+              "compile_commands.json"
+              "compile_flags.txt"
+            ];
+            filetypes = [
+              "c"
+              "cpp"
+            ];
+          };
+        };
+      };
+    };
+  };
+
+  config = {
+    extraConfigLua =
+      let
+        serversToEnable = lib.pipe cfg.servers [
+          (lib.filterAttrs (_: props: props.enable))
+          (lib.mapAttrsToList (serverName: _: serverName))
+        ];
+      in
+      ''
+        do
+          local __nixvimLspServersConfigs = ${lib.nixvim.toLuaObject cfg.servers}
+
+          for server_name, server_props in ipairs(__nixvimLspServersConfigs) do
path: modules/lsp.nix
user: login: khaneliman
body: ```suggestion
          for server_name, server_props in pairs(__nixvimLspServersConfigs) do
```
this fixes the config being passed for me 
created_at: 2025-04-23T00:17:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        Wrapper of the `vim.lsp` API.
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.enable = true;
+        clangd = {
+          enable = true;
+          config = {
+            cmd = [
+              "clangd"
+              "--background-index"
+            ];
+            root_markers = [
+              "compile_commands.json"
+              "compile_flags.txt"
+            ];
+            filetypes = [
+              "c"
+              "cpp"
+            ];
+          };
+        };
+      };
+    };
+  };
+
+  config = {
+    extraConfigLua =
+      let
+        serversToEnable = lib.pipe cfg.servers [
+          (lib.filterAttrs (_: props: props.enable))
+          (lib.mapAttrsToList (serverName: _: serverName))
+        ];
+      in
+      ''
+        do
+          local __nixvimLspServersConfigs = ${lib.nixvim.toLuaObject cfg.servers}
+
+          for server_name, server_props in ipairs(__nixvimLspServersConfigs) do
path: modules/lsp.nix
user: login: khaneliman
body: But seems to barf when i don't have a config... lol 
created_at: 2025-04-23T00:19:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        Wrapper of the `vim.lsp` API.
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.enable = true;
+        clangd = {
+          enable = true;
+          config = {
+            cmd = [
+              "clangd"
+              "--background-index"
+            ];
+            root_markers = [
+              "compile_commands.json"
+              "compile_flags.txt"
+            ];
+            filetypes = [
+              "c"
+              "cpp"
+            ];
+          };
+        };
+      };
+    };
+  };
+
+  config = {
+    extraConfigLua =
+      let
+        serversToEnable = lib.pipe cfg.servers [
+          (lib.filterAttrs (_: props: props.enable))
+          (lib.mapAttrsToList (serverName: _: serverName))
+        ];
+      in
+      ''
+        do
+          local __nixvimLspServersConfigs = ${lib.nixvim.toLuaObject cfg.servers}
+
+          for server_name, server_props in ipairs(__nixvimLspServersConfigs) do
+            vim.lsp.config[server_name] = server_props.config
+          end
path: modules/lsp.nix
user: login: khaneliman
body: ```suggestion
         for server_name, server_props in pairs(__nixvimLspServersConfigs) do
            if server_props.config then
              vim.lsp.config[server_name] = server_props.config
            end
          end
```
This fixes my configuration issues. It will setup up custom configuration, when provided. Otherwise, will just enable with defaults.
created_at: 2025-04-23T00:24:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        Wrapper of the `vim.lsp` API.
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.enable = true;
+        clangd = {
+          enable = true;
+          config = {
+            cmd = [
+              "clangd"
+              "--background-index"
+            ];
+            root_markers = [
+              "compile_commands.json"
+              "compile_flags.txt"
+            ];
+            filetypes = [
+              "c"
+              "cpp"
+            ];
+          };
+        };
+      };
+    };
+  };
+
+  config = {
+    extraConfigLua =
+      let
+        serversToEnable = lib.pipe cfg.servers [
+          (lib.filterAttrs (_: props: props.enable))
+          (lib.mapAttrsToList (serverName: _: serverName))
+        ];
+      in
+      ''
+        do
+          local __nixvimLspServersConfigs = ${lib.nixvim.toLuaObject cfg.servers}
+
+          for server_name, server_props in ipairs(__nixvimLspServersConfigs) do
+            vim.lsp.config[server_name] = server_props.config
+          end
path: modules/lsp.nix
user: login: HeitorAugustoLN
body: Instead of using a table assignment, it should instead use a function call. Table assignment within `vim.lsp.config` will override the entire configuration, when configuring it through a function call, it will merge all the configuration that have been passed.

https://neovim.io/doc/user/lsp.html#vim.lsp.config()
created_at: 2025-04-23T00:46:28Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        Wrapper of the `vim.lsp` API.
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.enable = true;
+        clangd = {
+          enable = true;
+          config = {
+            cmd = [
+              "clangd"
+              "--background-index"
+            ];
+            root_markers = [
+              "compile_commands.json"
+              "compile_flags.txt"
+            ];
+            filetypes = [
+              "c"
+              "cpp"
+            ];
+          };
+        };
+      };
+    };
+  };
+
+  config = {
+    extraConfigLua =
+      let
+        serversToEnable = lib.pipe cfg.servers [
+          (lib.filterAttrs (_: props: props.enable))
+          (lib.mapAttrsToList (serverName: _: serverName))
+        ];
+      in
+      ''
+        do
+          local __nixvimLspServersConfigs = ${lib.nixvim.toLuaObject cfg.servers}
+
+          for server_name, server_props in ipairs(__nixvimLspServersConfigs) do
+            vim.lsp.config[server_name] = server_props.config
+          end
path: modules/lsp.nix
user: login: MattSturgeon
body: ```suggestion
          for server_name, server_props in ipairs(__nixvimLspServersConfigs) do
            vim.lsp.config(server_name, server_props.config)
          end
```

IMO `__nixvimLspServersConfigs` should also be `mapAttrs (_: props: props.config)`, so that we don't end up needlessly serialising the unrelated non-config props (like `enable`).

I also wonder if it makes sense that we are setting the config for _all_ defined servers, even ones with `enable = false`?

I know we're using `enable` later when deciding which servers to, well, _enable_; I just wonder if it's confusing that disabled servers still get configured?
created_at: 2025-04-23T00:52:55Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,102 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        LSP servers to enable and/or configure.
+
+        This option is implemented using neovim's `vim.lsp` lua API.
+        If you prefer to use [nvim-lspconfig], see [`plugins.lsp`].
+
+        [nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig
+        [`plugins.lsp`]: https://nix-community.github.io/nixvim/plugins/lsp/index.html
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.enable = true;
+        clangd = {
+          enable = true;
+          config = {
+            cmd = [
+              "clangd"
+              "--background-index"
+            ];
+            root_markers = [
+              "compile_commands.json"
+              "compile_flags.txt"
+            ];
+            filetypes = [
+              "c"
+              "cpp"
+            ];
+          };
+        };
+      };
+    };
+  };
+
+  config = {
+    extraConfigLua =
+      let
+        serversToEnable = lib.pipe cfg.servers [
+          (lib.filterAttrs (_: props: props.enable))
+          builtins.attrNames
+        ];
+      in
+      ''
+        do
+          local __nixvimLspServersConfigs = ${lib.nixvim.toLuaObject cfg.servers}
+
+          for server_name, server_props in ipairs(__nixvimLspServersConfigs) do
+            vim.lsp.config(server_name, server_props.config)
+          end
+
+          vim.lsp.enable(${lib.nixvim.toLuaObject serversToEnable})
+        end
+      '';
path: modules/lsp.nix
user: login: MattSturgeon
body: Since we're serialising the whole of `cfg.servers` anyway, we may as well move the `enable` into the loop too:

```suggestion
    extraConfigLua = ''
      do
        local __nixvimLspServers = ${lib.nixvim.toLuaObject cfg.servers}

        for server_name, server_props in ipairs(__nixvimLspServers) do
          if server_props.config then
            vim.lsp.config(server_name, server_props.config)
          end

          if server_props.enable then
            vim.lsp.enable(server_name)
          end
        end

      end
    '';
```


created_at: 2025-04-23T01:23:27Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        Wrapper of the `vim.lsp` API.
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.enable = true;
+        clangd = {
+          enable = true;
+          config = {
+            cmd = [
+              "clangd"
+              "--background-index"
+            ];
+            root_markers = [
+              "compile_commands.json"
+              "compile_flags.txt"
+            ];
+            filetypes = [
+              "c"
+              "cpp"
+            ];
+          };
+        };
+      };
+    };
+  };
+
+  config = {
+    extraConfigLua =
+      let
+        serversToEnable = lib.pipe cfg.servers [
+          (lib.filterAttrs (_: props: props.enable))
+          (lib.mapAttrsToList (serverName: _: serverName))
path: modules/lsp.nix
user: login: GaetanLepage
body: Done
created_at: 2025-04-23T07:21:06Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        Wrapper of the `vim.lsp` API.
path: modules/lsp.nix
user: login: GaetanLepage
body: Done
created_at: 2025-04-23T07:21:16Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        Wrapper of the `vim.lsp` API.
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.enable = true;
+        clangd = {
+          enable = true;
+          config = {
+            cmd = [
+              "clangd"
+              "--background-index"
+            ];
+            root_markers = [
+              "compile_commands.json"
+              "compile_flags.txt"
+            ];
+            filetypes = [
+              "c"
+              "cpp"
+            ];
+          };
+        };
+      };
+    };
+  };
+
+  config = {
+    extraConfigLua =
+      let
+        serversToEnable = lib.pipe cfg.servers [
+          (lib.filterAttrs (_: props: props.enable))
+          (lib.mapAttrsToList (serverName: _: serverName))
+        ];
+      in
+      ''
+        do
+          local __nixvimLspServersConfigs = ${lib.nixvim.toLuaObject cfg.servers}
+
+          for server_name, server_props in ipairs(__nixvimLspServersConfigs) do
+            vim.lsp.config[server_name] = server_props.config
+          end
path: modules/lsp.nix
user: login: GaetanLepage
body: > IMO `__nixvimLspServersConfigs` should also be `mapAttrs (_: props: props.config)`, so that we don't end up needlessly serialising the unrelated non-config props (like `enable`).

But, how do you get `server_name` then?

> I also wonder if it makes sense that we are setting the config for _all_ defined servers, even ones with `enable = false`?
> 
> I know we're using `enable` later when deciding which servers to, well, _enable_; I just wonder if it's confusing that disabled servers still get configured?

The user could want to enable the server somewhere else (in an autocmd for instance) but still have it configured through `lsp.servers.foo.config`.
As the API itself differentiates configuring and enabling servers, I think that our wrapping should do the same.
I.e., if I set `lsp.servers.foo.config = {...}`, I expect it to translate to lua without having to do something extra.
More generally, I think that the closer we are to the underlying API the better.
created_at: 2025-04-23T07:45:28Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,96 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        Wrapper of the `vim.lsp` API.
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.enable = true;
+        clangd = {
+          enable = true;
+          config = {
+            cmd = [
+              "clangd"
+              "--background-index"
+            ];
+            root_markers = [
+              "compile_commands.json"
+              "compile_flags.txt"
+            ];
+            filetypes = [
+              "c"
+              "cpp"
+            ];
+          };
+        };
+      };
+    };
+  };
+
+  config = {
+    extraConfigLua =
+      let
+        serversToEnable = lib.pipe cfg.servers [
+          (lib.filterAttrs (_: props: props.enable))
+          (lib.mapAttrsToList (serverName: _: serverName))
+        ];
+      in
+      ''
+        do
+          local __nixvimLspServersConfigs = ${lib.nixvim.toLuaObject cfg.servers}
+
+          for server_name, server_props in ipairs(__nixvimLspServersConfigs) do
+            vim.lsp.config[server_name] = server_props.config
+          end
path: modules/lsp.nix
user: login: MattSturgeon
body: > > IMO `__nixvimLspServersConfigs` should also be `mapAttrs (_: props: props.config)`, so that we don't end up needlessly serialising the unrelated non-config props (like `enable`).
> 
> But, how do you get `server_name` then?

The name would be unchanged (that's the `_` in the `mapAttrs`). However I personally think https://github.com/nix-community/nixvim/pull/3203#discussion_r2055109527 is a better idea than this one.

> The user could want to enable the server somewhere else (in an autocmd for instance) but still have it configured through `lsp.servers.foo.config`.
> As the API itself differentiates configuring and enabling servers, I think that our wrapping should do the same.

Yeah, I was on the fence regarding the same argument. IMO this could be a justification for a global `lsp.enable` option, even if it is default-true, just to allow users to disable (e.g. conditionally) us configuring stuff for them
created_at: 2025-04-23T07:49:11Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,102 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        LSP servers to enable and/or configure.
+
+        This option is implemented using neovim's `vim.lsp` lua API.
+        If you prefer to use [nvim-lspconfig], see [`plugins.lsp`].
+
+        [nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig
+        [`plugins.lsp`]: https://nix-community.github.io/nixvim/plugins/lsp/index.html
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.enable = true;
+        clangd = {
+          enable = true;
+          config = {
+            cmd = [
+              "clangd"
+              "--background-index"
+            ];
+            root_markers = [
+              "compile_commands.json"
+              "compile_flags.txt"
+            ];
+            filetypes = [
+              "c"
+              "cpp"
+            ];
+          };
+        };
+      };
+    };
+  };
+
+  config = {
+    extraConfigLua =
+      let
+        serversToEnable = lib.pipe cfg.servers [
+          (lib.filterAttrs (_: props: props.enable))
+          builtins.attrNames
+        ];
+      in
+      ''
+        do
+          local __nixvimLspServersConfigs = ${lib.nixvim.toLuaObject cfg.servers}
+
+          for server_name, server_props in ipairs(__nixvimLspServersConfigs) do
+            vim.lsp.config(server_name, server_props.config)
+          end
+
+          vim.lsp.enable(${lib.nixvim.toLuaObject serversToEnable})
+        end
+      '';
path: modules/lsp.nix
user: login: GaetanLepage
body: Sure, I have no strong feelings on this.
created_at: 2025-04-23T08:01:02Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,162 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+  inherit (lib.nixvim) toLuaObject;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      activate = lib.mkOption {
+        type = types.bool;
+        description = ''
+          Whether to call `vim.lsp.enable()` for this server.
+        '';
+        default = true;
+        example = false;
+      };
+
+      package = lib.mkOption {
+        type = with types; nullOr package;
+        default = null;
+        description = ''
+          Package to use for this language server.
+
+          Alternatively, the language server should be available on your `$PATH`.
+        '';
+      };
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    preConfig = mkOption {
+      type = types.lines;
+      description = "Code to be run before configuring the LSP. Useful for requiring plugins";
+      default = "";
+    };
+
+    postConfig = mkOption {
+      type = types.lines;
+      description = "Code to be run after configuring the LSP. Useful for requiring plugins";
+      default = "";
+    };
path: modules/lsp.nix
user: login: MattSturgeon
body: Let's use a `pluginLuaConfig` type?
created_at: 2025-04-26T20:24:40Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,159 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+  inherit (lib.nixvim) toLuaObject;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      activate = lib.mkOption {
+        type = types.bool;
+        description = ''
+          Whether to call `vim.lsp.enable()` for this server.
+        '';
+        default = true;
+        example = false;
+      };
+
+      package = lib.mkOption {
+        type = with types; nullOr package;
+        default = null;
+        description = ''
+          Package to use for this language server.
+
+          Alternatively, the language server should be available on your `$PATH`.
+        '';
+      };
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    luaConfig = lib.mkOption {
+      type = lib.types.pluginLuaConfig;
+      default = { };
+      description = ''
+        Lua code configuring LSP.
+      '';
+    };
+
+    inlay_hint = {
+      enable = lib.mkEnableOption "inlay hints globally";
+    };
+
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        LSP servers to enable and/or configure.
+
+        This option is implemented using neovim's `vim.lsp` lua API.
+        If you prefer to use [nvim-lspconfig], see [`plugins.lsp`].
path: modules/lsp.nix
user: login: MattSturgeon
body: At some point, we should tweak this wording to suggest users use _both_ `lsp` and `plugins.lsp` together.
created_at: 2025-04-26T20:44:00Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,159 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+  inherit (lib.nixvim) toLuaObject;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      activate = lib.mkOption {
+        type = types.bool;
+        description = ''
+          Whether to call `vim.lsp.enable()` for this server.
+        '';
+        default = true;
+        example = false;
+      };
+
+      package = lib.mkOption {
+        type = with types; nullOr package;
+        default = null;
+        description = ''
+          Package to use for this language server.
+
+          Alternatively, the language server should be available on your `$PATH`.
+        '';
+      };
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    luaConfig = lib.mkOption {
+      type = lib.types.pluginLuaConfig;
+      default = { };
+      description = ''
+        Lua code configuring LSP.
+      '';
+    };
+
+    inlay_hint = {
+      enable = lib.mkEnableOption "inlay hints globally";
+    };
+
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        LSP servers to enable and/or configure.
+
+        This option is implemented using neovim's `vim.lsp` lua API.
+        If you prefer to use [nvim-lspconfig], see [`plugins.lsp`].
+
+        [nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig
+        [`plugins.lsp`]: https://nix-community.github.io/nixvim/plugins/lsp/index.html
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.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.filterAttrs (_: v: v.enable) cfg.servers;
path: modules/lsp.nix
user: login: MattSturgeon
body: If we introduce a `name` option (similar to other submodule sets like `extraFiles`), then we can simplify this to `filter (v: v.enable) (attrValues cfg.servers)`

```nix
submodule (
  { name, ... }:
  {
    options = {
      name = lib.mkOption {
        type = lib.types.str;
        description = "The name of the language server, used as the first argument to `vim.lsp.enable` and `vim.lsp.config`.";
        default = name;
      };
      # ...
    };
  }
)
```

This would also allow users to configure the same server in different ways, using different attr-names, and decide which to use via the `enable` option.
created_at: 2025-04-26T21:17:13Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,159 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+  inherit (lib.nixvim) toLuaObject;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      activate = lib.mkOption {
+        type = types.bool;
+        description = ''
+          Whether to call `vim.lsp.enable()` for this server.
+        '';
+        default = true;
+        example = false;
+      };
+
+      package = lib.mkOption {
+        type = with types; nullOr package;
+        default = null;
+        description = ''
+          Package to use for this language server.
+
+          Alternatively, the language server should be available on your `$PATH`.
+        '';
+      };
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    luaConfig = lib.mkOption {
+      type = lib.types.pluginLuaConfig;
+      default = { };
+      description = ''
+        Lua code configuring LSP.
+      '';
+    };
+
+    inlay_hint = {
+      enable = lib.mkEnableOption "inlay hints globally";
+    };
+
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        LSP servers to enable and/or configure.
+
+        This option is implemented using neovim's `vim.lsp` lua API.
+        If you prefer to use [nvim-lspconfig], see [`plugins.lsp`].
+
+        [nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig
+        [`plugins.lsp`]: https://nix-community.github.io/nixvim/plugins/lsp/index.html
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.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.filterAttrs (_: v: v.enable) cfg.servers;
+    in
+    {
+      extraPackages = lib.pipe enabledServers [
+        builtins.attrValues
+        (builtins.catAttrs "package")
+      ];
+
+      lsp.luaConfig.content =
+        let
+          mkServerConfig =
+            name: props:
+            let
+              luaName = toLuaObject name;
+            in
+            ''
+              vim.lsp.config(${luaName}, ${toLuaObject props.config})
+            ''
+            + lib.optionalString props.activate ''
+              vim.lsp.enable(${luaName})
+            '';
+          serverConfig = lib.concatMapAttrsStringSep "\n" mkServerConfig enabledServers;
+
+        in
+        lib.concatStringsSep "\n" (
+          [
+            cfg.preConfig
+          ]
+          ++ lib.optional cfg.inlay_hint.enable "vim.lsp.inlay_hint.enable(true)"
+          ++ [
+            serverConfig
+            cfg.postConfig
+          ]
+        );
path: modules/lsp.nix
user: login: MattSturgeon
body: > error: attribute 'preConfig' missing

```suggestion
        in
        lib.mkMerge (
          lib.optional cfg.inlay_hint.enable "vim.lsp.inlay_hint.enable(true)"
          ++ lib.mapAttrsToList mkServerConfig enabledServers
        );
```
created_at: 2025-04-26T21:21:21Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,159 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+  inherit (lib.nixvim) toLuaObject;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      activate = lib.mkOption {
+        type = types.bool;
+        description = ''
+          Whether to call `vim.lsp.enable()` for this server.
+        '';
+        default = true;
+        example = false;
+      };
+
+      package = lib.mkOption {
+        type = with types; nullOr package;
+        default = null;
+        description = ''
+          Package to use for this language server.
+
+          Alternatively, the language server should be available on your `$PATH`.
+        '';
+      };
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    luaConfig = lib.mkOption {
+      type = lib.types.pluginLuaConfig;
+      default = { };
+      description = ''
+        Lua code configuring LSP.
+      '';
+    };
+
+    inlay_hint = {
path: modules/lsp.nix
user: login: HeitorAugustoLN
body: Why this doesn't follow the regular camelCase naming?
created_at: 2025-04-26T21:46:00Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,159 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+  inherit (lib.nixvim) toLuaObject;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      activate = lib.mkOption {
+        type = types.bool;
+        description = ''
+          Whether to call `vim.lsp.enable()` for this server.
+        '';
+        default = true;
+        example = false;
+      };
+
+      package = lib.mkOption {
+        type = with types; nullOr package;
+        default = null;
+        description = ''
+          Package to use for this language server.
+
+          Alternatively, the language server should be available on your `$PATH`.
+        '';
+      };
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    luaConfig = lib.mkOption {
+      type = lib.types.pluginLuaConfig;
+      default = { };
+      description = ''
+        Lua code configuring LSP.
+      '';
+    };
+
+    inlay_hint = {
path: modules/lsp.nix
user: login: MattSturgeon
body: @GaetanLepage's argument was that it is directly representing the underlying `vim.lsp.inlay_hint` API.

Personally, I don't feel the representation is all that "direct", so I'd also be in favour of:

```suggestion
    inlayHints = {
```
created_at: 2025-04-26T21:55:03Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,159 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+  inherit (lib.nixvim) toLuaObject;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      activate = lib.mkOption {
+        type = types.bool;
+        description = ''
+          Whether to call `vim.lsp.enable()` for this server.
+        '';
+        default = true;
+        example = false;
+      };
+
+      package = lib.mkOption {
+        type = with types; nullOr package;
+        default = null;
+        description = ''
+          Package to use for this language server.
+
+          Alternatively, the language server should be available on your `$PATH`.
+        '';
+      };
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    luaConfig = lib.mkOption {
+      type = lib.types.pluginLuaConfig;
+      default = { };
+      description = ''
+        Lua code configuring LSP.
+      '';
+    };
+
+    inlay_hint = {
path: modules/lsp.nix
user: login: GaetanLepage
body: Sure, done.
created_at: 2025-04-28T14:09:10Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,151 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+  inherit (lib.nixvim) toLuaObject;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      activate = lib.mkOption {
+        type = types.bool;
+        description = ''
+          Whether to call `vim.lsp.enable()` for this server.
+        '';
+        default = true;
+        example = false;
+      };
+
+      package = lib.mkOption {
+        type = with types; nullOr package;
+        default = null;
+        description = ''
+          Package to use for this language server.
+
+          Alternatively, the language server should be available on your `$PATH`.
+        '';
+      };
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    luaConfig = lib.mkOption {
+      type = lib.types.pluginLuaConfig;
+      default = { };
+      description = ''
+        Lua code configuring LSP.
+      '';
+    };
+
+    inlayHints = {
+      enable = lib.mkEnableOption "inlay hints globally";
+    };
+
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        LSP servers to enable and/or configure.
+
+        This option is implemented using neovim's `vim.lsp` lua API.
+        If you prefer to use [nvim-lspconfig], see [`plugins.lsp`].
+
+        [nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig
+        [`plugins.lsp`]: https://nix-community.github.io/nixvim/plugins/lsp/index.html
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.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.filterAttrs (_: v: v.enable) cfg.servers;
+    in
+    {
+      extraPackages = lib.pipe enabledServers [
+        builtins.attrValues
+        (builtins.catAttrs "package")
+      ];
+
+      lsp.luaConfig.content =
+        let
+          mkServerConfig =
+            name: props:
+            let
+              luaName = toLuaObject name;
+            in
+            ''
+              vim.lsp.config(${luaName}, ${toLuaObject props.config})
+            ''
+            + lib.optionalString props.activate ''
+              vim.lsp.enable(${luaName})
+            '';
+        in
+        lib.mkMerge (
+          lib.optional cfg.inlayHints.enable "vim.lsp.inlay_hint.enable(true)"
+          ++ lib.mapAttrsToList mkServerConfig enabledServers
+        );
+
+      extraConfigLua = ''
+        -- LSP {{{
+        do
+          ${cfg.luaConfig.content}
+        end
+        -- }}}
+      '';
path: modules/lsp.nix
user: login: MattSturgeon
body: This unconditional definition is triggering this test: https://github.com/nix-community/nixvim/blob/6415ae4a9779286b4248a2dd2ed742d96f74bbbf/tests/test-sources/modules/output.nix#L81-L101

The test exists to ensure we don't add things to init.lua unless the user has actually configured something.

We want to add a `mkIf` here, not sure what the best condition should be... maybe `enabledServers != [ ]` or `luaConfig.content != ""`?

```suggestion
      extraConfigLua = lib.mkIf (cfg.luaConfig.content != "") ''
        -- LSP {{{
        do
          ${cfg.luaConfig.content}
        end
        -- }}}
      '';
```
created_at: 2025-04-28T14:44:57Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,159 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+  inherit (lib.nixvim) toLuaObject;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      activate = lib.mkOption {
+        type = types.bool;
+        description = ''
+          Whether to call `vim.lsp.enable()` for this server.
+        '';
+        default = true;
+        example = false;
+      };
+
+      package = lib.mkOption {
+        type = with types; nullOr package;
+        default = null;
+        description = ''
+          Package to use for this language server.
+
+          Alternatively, the language server should be available on your `$PATH`.
+        '';
+      };
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    luaConfig = lib.mkOption {
+      type = lib.types.pluginLuaConfig;
+      default = { };
+      description = ''
+        Lua code configuring LSP.
+      '';
+    };
+
+    inlay_hint = {
+      enable = lib.mkEnableOption "inlay hints globally";
+    };
+
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        LSP servers to enable and/or configure.
+
+        This option is implemented using neovim's `vim.lsp` lua API.
+        If you prefer to use [nvim-lspconfig], see [`plugins.lsp`].
path: modules/lsp.nix
user: login: MattSturgeon
body: This will be updated in https://github.com/nix-community/nixvim/pull/3234 to reference `plugins.lspconfig` instead.
created_at: 2025-04-28T16:07:01Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,159 @@
+{
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) mkOption types;
+  inherit (lib.nixvim) toLuaObject;
+
+  cfg = config.lsp;
+
+  serverType = types.submodule {
+    options = {
+      enable = lib.mkEnableOption "the language server";
+
+      activate = lib.mkOption {
+        type = types.bool;
+        description = ''
+          Whether to call `vim.lsp.enable()` for this server.
+        '';
+        default = true;
+        example = false;
+      };
+
+      package = lib.mkOption {
+        type = with types; nullOr package;
+        default = null;
+        description = ''
+          Package to use for this language server.
+
+          Alternatively, the language server should be available on your `$PATH`.
+        '';
+      };
+
+      config = mkOption {
+        type = with types; attrsOf anything;
+        description = ''
+          Configurations for each language server.
+        '';
+        default = { };
+        example = {
+          cmd = [
+            "clangd"
+            "--background-index"
+          ];
+          root_markers = [
+            "compile_commands.json"
+            "compile_flags.txt"
+          ];
+          filetypes = [
+            "c"
+            "cpp"
+          ];
+        };
+      };
+    };
+  };
+in
+{
+  options.lsp = {
+    luaConfig = lib.mkOption {
+      type = lib.types.pluginLuaConfig;
+      default = { };
+      description = ''
+        Lua code configuring LSP.
+      '';
+    };
+
+    inlay_hint = {
+      enable = lib.mkEnableOption "inlay hints globally";
+    };
+
+    servers = mkOption {
+      type = types.attrsOf serverType;
+      description = ''
+        LSP servers to enable and/or configure.
+
+        This option is implemented using neovim's `vim.lsp` lua API.
+        If you prefer to use [nvim-lspconfig], see [`plugins.lsp`].
+
+        [nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig
+        [`plugins.lsp`]: https://nix-community.github.io/nixvim/plugins/lsp/index.html
+      '';
+      default = { };
+      example = {
+        "*".config = {
+          root_markers = [ ".git" ];
+          capabilities.textDocument.semanticTokens = {
+            multilineTokenSupport = true;
+          };
+        };
+        luals.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.filterAttrs (_: v: v.enable) cfg.servers;
path: modules/lsp.nix
user: login: MattSturgeon
body: I'll look at this in a followup PR

EDIT: https://github.com/nix-community/nixvim/pull/3239
created_at: 2025-04-28T16:07:33Z
author_association: MEMBER
path: tests/test-sources/modules/lsp.nix
user: login: MattSturgeon
body: We should really have more extensive tests here. Follow up PR
created_at: 2025-04-28T16:07:58Z
author_association: MEMBER

PR comments:
user: login: MattSturgeon
created_at: 2025-04-22T21:38:43Z
body: > Note: this is distinct from our `plugins.lsp` module which is backed by [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig?rgh-link-date=2025-04-22T14%3A31%3A04Z).

Perhaps one day (when the top-level lsp module is more mature) we'll consider renaming `plugins.lsp` to something like `plugins.nvim-lspconfig` for clarity and to de-emphasize it?
author_association: MEMBER
user: login: MattSturgeon
created_at: 2025-04-22T23:24:35Z
body: @mergifyio rebase
author_association: MEMBER
user: login: mergify[bot]
created_at: 2025-04-22T23:24:43Z
body: > rebase

#### ✅ Branch has been successfully rebased



<!---
DO NOT EDIT
-*- Mergify Payload -*-
{"command": "rebase", "conclusion": "success", "action_is_running": true}
-*- Mergify Payload End -*-
-->
author_association: CONTRIBUTOR
user: login: khaneliman
created_at: 2025-04-22T23:57:42Z
body: We'll want to update the warning in `plugins.clangd-extensions` to know about this module. 
author_association: CONTRIBUTOR
user: login: zowoq
created_at: 2025-04-26T22:50:38Z
body: Any ideas why this PR has been causing multi hour darwin CI jobs on buildbot? 
author_association: CONTRIBUTOR
user: login: GaetanLepage
created_at: 2025-04-26T23:05:14Z
body: > Any ideas why this PR has been causing multi hour darwin CI jobs on buildbot?

LSP is pretty central to Nixvim, so changing it causes many rebuilds in our CI test suite.
Feel free to kill these jobs if they are affecting other projects.
This PR is not ready to be merged yet, so running the full CI is not too important.
author_association: MEMBER
user: login: zowoq
created_at: 2025-04-26T23:23:21Z
body: If the jobs aren't important I'd appreciate it if you killed the builds immediately instead of leaving it to me to notice.

Darwin CI usage by this repo isn't a problem yet but it is approaching that point.

Unfortunately improving CI scheduling isn't simple and may not happen anytime soon.

Disabling x86_64-darwin and only building aarch64-darwin is probably going to be the most workable option. 
author_association: CONTRIBUTOR
user: login: MattSturgeon
created_at: 2025-04-26T23:35:03Z
body: > LSP is pretty central to Nixvim, so changing it causes many rebuilds in our CI test suite.

That really shouldn't be the case; this PR doesn't modify anything used elsewhere in nixvim. It only introduces a new module that is (as of yet) unused.

The only impact it should have is adding _one_ derivation to the test suite (the `example` test). Even this test doesn't install any LSP server packages, or any vim plugins.

Further, most of the CI runs that were problematic were failing with fairly simple eval errors (e.g., typos in function names).

I really don't see any reason why any builds would've continued beyond a few minutes 🤔

> If the jobs aren't important I'd appreciate it if you killed the builds immediately instead of leaving it to me to notice.

Apologies; we usually try to cancel jobs whenever they aren't important. I guess we were distracted this time.

> Darwin CI usage by this repo isn't a problem yet but it is approaching that point.

We appreciate being able to test our plugins on all platforms. 

However there are a handful of tests that aren't affected by platform; they are still declared via flake.parts `perSystem` due to the nature of how flake outputs work, and to allow them to be run locally on any system.

(IDK if those platform-independant checks are actually causing any significant load though)

If we had a way to configure which tests are run by CI (other than simply omitting those tests from the `checks` flake output) we would certainly make use of it.

author_association: MEMBER
user: login: mathjiajia
created_at: 2025-04-27T00:36:30Z
body: ```
error: attribute 'preConfig' missing
at /nix/store/58faqxc9sbjxqq0vwpf9mlax6nhnf0gx-source/modules/lsp.nix:142:13:
    141|           [
    142|             cfg.preConfig
       |             ^
    143|           ]
```

https://github.com/nix-community/nixvim/pull/3203#discussion_r2061623725
author_association: CONTRIBUTOR
user: login: zowoq
created_at: 2025-04-27T00:58:11Z
body: > If we had a way to configure which tests are run by CI (other than simply omitting those tests from the `checks` flake output) we would certainly make use of it.

Unfortunately I think this project is the only one that would really benefit from this at the moment, same applies to the CI scheduling improvements.
author_association: CONTRIBUTOR
user: login: MattSturgeon
created_at: 2025-04-28T14:28:07Z
body: We still have a CI failure: https://buildbot.nix-community.org/#/builders/2852/builds/4316/steps/1/logs/stdio

```
building '/nix/store/gbh75vkssahaifsvm802gk6b7vx3v4vx-files-default-empty.drv'...
files-default-empty> Failed 1 expectation:
files-default-empty> - Expected length to be 0 but found 2.
files-default-empty> For assertions:
files-default-empty> - Default content of test.lua file is expected to be empty.
files-default-empty> - Default content of test.vim file is expected to be empty.
error: build of '/nix/store/gbh75vkssahaifsvm802gk6b7vx3v4vx-files-default-empty.drv' on 'ssh-ng://nix@build04.nix-community.org' failed: builder for '/nix/store/gbh75vkssahaifsvm802gk6b7vx3v4vx-files-default-empty.drv' failed with exit code 1;
```

EDIT: https://github.com/nix-community/nixvim/pull/3203#discussion_r2063823171

---

Oddly, some jobs are showing as "cancelled (parent build was interrupted)" even though the [build as a whole](https://buildbot.nix-community.org/#/builders/26/builds/4935) is still "building".

EDIT: It was still "building" an hour later; I've clicked cancel now, but I'm not sure whether it's actually cancelled it or not.
EDIT2: I've also tried clicking "cancel whole queue"; not _seeing_ any effect.
author_association: MEMBER
user: login: MattSturgeon
created_at: 2025-04-28T16:14:15Z
body: @mergifyio queue
author_association: MEMBER
user: login: mergify[bot]
created_at: 2025-04-28T16:14:22Z
body: > queue

#### ✅ The pull request has been merged automatically

<details>

The pull request has been merged automatically at *70c9b3b890adc745227c51c287520dd5f8febd3d*

</details>


<!---
DO NOT EDIT
-*- Mergify Payload -*-
{"command": "queue", "conclusion": "success", "action_is_running": true}
-*- Mergify Payload End -*-
-->
author_association: CONTRIBUTOR
user: login: mergify[bot]
created_at: 2025-04-28T16:17:21Z
body: This pull request, with head sha `70c9b3b890adc745227c51c287520dd5f8febd3d`, has been successfully merged with fast-forward by Mergify.

<details>
  <summary>This pull request will be automatically closed by GitHub.</summary>

As soon as GitHub detects that the sha `70c9b3b890adc745227c51c287520dd5f8febd3d` is part of the `main` branch, it will mark this pull request as merged.

It is possible for this pull request to remain open if this detection does not happen, this usually happens when a force-push is done on this branch `lsp`, this means GitHub will fail to detect the merge.
</details>

author_association: CONTRIBUTOR