neovim: nix-slice LSP + completion + wl-copy fix (#19) #21

Merged
rpickler merged 3 commits from issue-19-nixvim-nix-slice into main 2026-10-15 04:13:47 +00:00
Member

First testable slice from #19 — nix-focused, everything verified against the nixvim rev pinned in flake.lock (84e901ee).

Included

  • LSP foundation: plugins.lsp + lsp.inlayHints, on-attach keymaps (gd/gr/gi/gt/K/<leader>rn/<leader>ca/<leader>cf/<leader>ds, [d/]d/<leader>k diagnostics), silent
  • Nix language stack: nixd (completion, goto-def, hover, rename, formatting via nixpkgs-fmt) + statix antipattern linter. Flake-relative option-docs completion left as a commented follow-up in plugins/lsp.nix
  • Completion: nvim-cmp (nvim_lsp/luasnip/path/buffer sources, preset insert mappings) + luasnip + friendly-snippets + lspkind icons
  • wl-copy fix: clipboard.providers.wl-copy.enable only puts wl-copy/wl-paste on nvim PATH — nvim never had a clipboard register, so y/p stayed on the internal register. The old AstroNvim config set clipboard=unnamedplus implicitly. Added clipboard.register = "unnamedplus".

Not in this slice (deferred per issue)

which-key, gitsigns, nvim-ufo, oil, direnv + nix-develop — follow-up PR.

⚠️ Not build-verified (no nix available where this was drafted); option names cross-checked against the locked nixvim module sources. nix flake check / rebuild on your machine is the test.

First testable slice from #19 — nix-focused, everything verified against the nixvim rev pinned in flake.lock (84e901ee). ## Included - **LSP foundation**: `plugins.lsp` + `lsp.inlayHints`, on-attach keymaps (`gd`/`gr`/`gi`/`gt`/`K`/`<leader>rn`/`<leader>ca`/`<leader>cf`/`<leader>ds`, `[d`/`]d`/`<leader>k` diagnostics), silent - **Nix language stack**: `nixd` (completion, goto-def, hover, rename, formatting via nixpkgs-fmt) + `statix` antipattern linter. Flake-relative option-docs completion left as a commented follow-up in `plugins/lsp.nix` - **Completion**: `nvim-cmp` (nvim_lsp/luasnip/path/buffer sources, preset insert mappings) + `luasnip` + `friendly-snippets` + `lspkind` icons - **wl-copy fix**: `clipboard.providers.wl-copy.enable` only puts `wl-copy`/`wl-paste` on nvim PATH — nvim never had a **clipboard register**, so `y`/`p` stayed on the internal register. The old AstroNvim config set `clipboard=unnamedplus` implicitly. Added `clipboard.register = "unnamedplus"`. ## Not in this slice (deferred per issue) which-key, gitsigns, nvim-ufo, oil, direnv + nix-develop — follow-up PR. ⚠️ Not build-verified (no nix available where this was drafted); option names cross-checked against the locked nixvim module sources. `nix flake check` / rebuild on your machine is the test.
Adds the first testable slice from issue #19:
- plugins.lsp with on-attach keymaps, inlay hints, nixd + statix
- nvim-cmp + luasnip + friendly-snippets + lspkind completion
- clipboard.register = unnamedplus: fixes system clipboard under
  Hyprland (wl-clipboard binary was installed but nvim never had a
  clipboard register set; the old AstroNvim config set it implicitly)

Remaining plugins from the issue (which-key, gitsigns, nvim-ufo, oil,
direnv/nix-develop) intentionally deferred to a follow-up.
Owner
Lua callback: [string "vim/keymap"]:60: rhs: expected string|function, got nil
stack traceback:
        [C]: in function 'error'
        vim/_core/shared:1315: in function 'validate'
        [string "vim/keymap"]:60: in function 'set'
        /home/rpickler/.config/nvim/init.lua:462: in function </home/rpickler/.config/nvim/init.lua:377>
        [C]: in function 'nvim_exec_autocmds'
        ...wrapped-0.12.5/share/nvim/runtime/lua/vim/lsp/client.lua:1173: in function 'on_attach'
        ...wrapped-0.12.5/share/nvim/runtime/lua/vim/lsp/client.lua:614: in function 'fn'
        [string "vim/_core/editor"]:273: in function <[string "vim/_core/editor"]:272>
Press ENTER or type command to continue

This appears to come from this generated code:

            callback = function(args)
                do
                    local __nixvim_binds = {
                        {
                            action = vim.diagnostic.open_float,
                            key = "<leader>k",
                            mode = "n",
                            options = { desc = "Lsp diagnostic open_float", silent = true },
                        },
                        {
                            action = vim.diagnostic.goto_prev,
                            key = "[d",
                            mode = "n",
                            options = { desc = "Lsp diagnostic goto_prev", silent = true },
                        },
                        {
                            action = vim.diagnostic.goto_next,
                            key = "]d",
                            mode = "n",
                            options = { desc = "Lsp diagnostic goto_next", silent = true },
                        },
                        {
                            action = vim.lsp.buf.code_action,
                            key = "<leader>ca",
                            mode = "n",
                            options = { desc = "Lsp buf code_action", silent = true },
                        },
                        {
                            action = vim.lsp.buf.format,
                            key = "<leader>cf",
                            mode = "n",
                            options = { desc = "Lsp buf format", silent = true },
                        },
                        {
                            action = vim.lsp.buf.document_symbol,
                            key = "<leader>ds",
                            mode = "n",
                            options = { desc = "Lsp buf document_symbol", silent = true },
                        },
                        {
                            action = vim.lsp.buf.rename,
                            key = "<leader>rn",
                            mode = "n",
                            options = { desc = "Lsp buf rename", silent = true },
                        },
                        {
                            action = vim.lsp.buf.hover,
                            key = "K",
                            mode = "n",
                            options = { desc = "Lsp buf hover", silent = true },
                        },
                        {
                            action = vim.lsp.buf.declarations,
                            key = "gD",
                            mode = "n",
                            options = { desc = "Lsp buf declarations", silent = true },
                        },
                        {
                            action = vim.lsp.buf.definition,
                            key = "gd",
                            mode = "n",
                            options = { desc = "Lsp buf definition", silent = true },
                        },
                        {
                            action = vim.lsp.buf.implementation,
                            key = "gi",
                            mode = "n",
                            options = { desc = "Lsp buf implementation", silent = true },
                        },
                        {
                            action = vim.lsp.buf.references,
                            key = "gr",
                            mode = "n",
                            options = { desc = "Lsp buf references", silent = true },
                        },
                        {
                            action = vim.lsp.buf.type_definition,
                            key = "gt",
                            mode = "n",
                            options = { desc = "Lsp buf type_definition", silent = true },
                        },
                    }

                    for i, map in ipairs(__nixvim_binds) do
                        local options = vim.tbl_extend("keep", map.options or {}, { buffer = args.buf })
                        vim.keymap.set(map.mode, map.key, map.action, options)
                    end

I don't see anything wrong with those binds though. I'm guessing one of the actions is wrong?

  programs.nixvim = {
    plugins.lsp = {
      enable = true;

      keymaps = {
        silent = true;
        diagnostic = {
          "[d" = "goto_prev";
          "]d" = "goto_next";
          "<leader>k" = "open_float";
        };
        lspBuf = {
          gd = "definition";
          #gD = "declarations";
          gr = "references";
          gi = "implementation";
          gt = "type_definition";
          K = "hover";
          "<leader>rn" = "rename";
          "<leader>ca" = "code_action";
          "<leader>cf" = "format";
          "<leader>ds" = "document_symbol";
        };
      };
    };

    lsp.inlayHints.enable = true;

    # nixd — modern Nix language server (completion, goto-def, rename, formatting
    # via nixpkgs-fmt). Default settings; NixOS/HM option-docs completion from
    # your own flake can be added later with (nixd wraps this under `nixd.options`):
    #   plugins.lsp.servers.nixd.settings.options = {
    #     nixos.expr = "(builtins.getFlake (builtins.toString ./.)).nixosConfigurations.<host>.options";
    #   };
    plugins.lsp.servers.nixd.enable = true;

    # statix — Nix antipattern linter as LSP diagnostics.
    plugins.lsp.servers.statix.enable = true;
  };

commenting out this bit in lsp.nix fixed it, so it's that one specific keybind. Perhaps it doesn't exist?

And I just used copy paste for that, so that's confirmed to work.
Completions also confirmed to work. I'll drive this for the day and see how it goes. Fix that one line above and I'll merge it.

```Error in LspAttach Autocommands for "*": Lua callback: [string "vim/keymap"]:60: rhs: expected string|function, got nil stack traceback: [C]: in function 'error' vim/_core/shared:1315: in function 'validate' [string "vim/keymap"]:60: in function 'set' /home/rpickler/.config/nvim/init.lua:462: in function </home/rpickler/.config/nvim/init.lua:377> [C]: in function 'nvim_exec_autocmds' ...wrapped-0.12.5/share/nvim/runtime/lua/vim/lsp/client.lua:1173: in function 'on_attach' ...wrapped-0.12.5/share/nvim/runtime/lua/vim/lsp/client.lua:614: in function 'fn' [string "vim/_core/editor"]:273: in function <[string "vim/_core/editor"]:272> Press ENTER or type command to continue ``` This appears to come from this generated code: ``` callback = function(args) do local __nixvim_binds = { { action = vim.diagnostic.open_float, key = "<leader>k", mode = "n", options = { desc = "Lsp diagnostic open_float", silent = true }, }, { action = vim.diagnostic.goto_prev, key = "[d", mode = "n", options = { desc = "Lsp diagnostic goto_prev", silent = true }, }, { action = vim.diagnostic.goto_next, key = "]d", mode = "n", options = { desc = "Lsp diagnostic goto_next", silent = true }, }, { action = vim.lsp.buf.code_action, key = "<leader>ca", mode = "n", options = { desc = "Lsp buf code_action", silent = true }, }, { action = vim.lsp.buf.format, key = "<leader>cf", mode = "n", options = { desc = "Lsp buf format", silent = true }, }, { action = vim.lsp.buf.document_symbol, key = "<leader>ds", mode = "n", options = { desc = "Lsp buf document_symbol", silent = true }, }, { action = vim.lsp.buf.rename, key = "<leader>rn", mode = "n", options = { desc = "Lsp buf rename", silent = true }, }, { action = vim.lsp.buf.hover, key = "K", mode = "n", options = { desc = "Lsp buf hover", silent = true }, }, { action = vim.lsp.buf.declarations, key = "gD", mode = "n", options = { desc = "Lsp buf declarations", silent = true }, }, { action = vim.lsp.buf.definition, key = "gd", mode = "n", options = { desc = "Lsp buf definition", silent = true }, }, { action = vim.lsp.buf.implementation, key = "gi", mode = "n", options = { desc = "Lsp buf implementation", silent = true }, }, { action = vim.lsp.buf.references, key = "gr", mode = "n", options = { desc = "Lsp buf references", silent = true }, }, { action = vim.lsp.buf.type_definition, key = "gt", mode = "n", options = { desc = "Lsp buf type_definition", silent = true }, }, } for i, map in ipairs(__nixvim_binds) do local options = vim.tbl_extend("keep", map.options or {}, { buffer = args.buf }) vim.keymap.set(map.mode, map.key, map.action, options) end ``` I don't see anything wrong with those binds though. I'm guessing one of the actions is wrong? ``` programs.nixvim = { plugins.lsp = { enable = true; keymaps = { silent = true; diagnostic = { "[d" = "goto_prev"; "]d" = "goto_next"; "<leader>k" = "open_float"; }; lspBuf = { gd = "definition"; #gD = "declarations"; gr = "references"; gi = "implementation"; gt = "type_definition"; K = "hover"; "<leader>rn" = "rename"; "<leader>ca" = "code_action"; "<leader>cf" = "format"; "<leader>ds" = "document_symbol"; }; }; }; lsp.inlayHints.enable = true; # nixd — modern Nix language server (completion, goto-def, rename, formatting # via nixpkgs-fmt). Default settings; NixOS/HM option-docs completion from # your own flake can be added later with (nixd wraps this under `nixd.options`): # plugins.lsp.servers.nixd.settings.options = { # nixos.expr = "(builtins.getFlake (builtins.toString ./.)).nixosConfigurations.<host>.options"; # }; plugins.lsp.servers.nixd.enable = true; # statix — Nix antipattern linter as LSP diagnostics. plugins.lsp.servers.statix.enable = true; }; ``` commenting out this bit in lsp.nix fixed it, so it's that one specific keybind. Perhaps it doesn't exist? And I just used copy paste for that, so that's confirmed to work. Completions also confirmed to work. I'll drive this for the day and see how it goes. Fix that one line above and I'll merge it.
Nixvim's keymaps.lspBuf emits vim.lsp.buf.<name> verbatim; 'declarations'
(plural) does not exist in Neovim, so the generated action was nil and
keymap.set errored in the LspAttach autocmd. Use 'declaration' (the
function Neovim's default gD maps to).
rpickler deleted branch issue-19-nixvim-nix-slice 2026-10-15 04:13:47 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
richardpickler.com/nixos!21
No description provided.