aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2026-10-02 17:18:58 +0200
committerMartin Fischer <martin@push-f.com>2026-10-31 08:43:08 +0100
commitb6f87c0f1308cd4c3bd7d170970705476ccbf5bc (patch)
tree11d824ebb0fdb0a029365ff8c84fb1655c1043ed
parentb3116a2edaaad9e44baef014749ccec9c75559c7 (diff)
feat(workstation): set up LSP with nvim
-rw-r--r--nixos/profiles/workstation/dev.nix1
-rw-r--r--nixos/profiles/workstation/graphical.nix4
-rw-r--r--nixos/profiles/workstation/pkgs/nvim.nix4
-rw-r--r--user/nvim/init.lua91
4 files changed, 100 insertions, 0 deletions
diff --git a/nixos/profiles/workstation/dev.nix b/nixos/profiles/workstation/dev.nix
index 4c986a1..1b9100b 100644
--- a/nixos/profiles/workstation/dev.nix
+++ b/nixos/profiles/workstation/dev.nix
@@ -29,6 +29,7 @@
# language servers
nixd
rust-analyzer
+ taplo
# documentation
man-pages # Linux development manual pages
diff --git a/nixos/profiles/workstation/graphical.nix b/nixos/profiles/workstation/graphical.nix
index 5c82d49..c633456 100644
--- a/nixos/profiles/workstation/graphical.nix
+++ b/nixos/profiles/workstation/graphical.nix
@@ -52,6 +52,10 @@
[[ "$(tty)" == /dev/tty1 ]] && exec niri-session -l
'';
+ fonts.packages = with pkgs; [
+ nerd-fonts.symbols-only
+ ];
+
environment.systemPackages = with pkgs; [
wmenu
i3bar-river
diff --git a/nixos/profiles/workstation/pkgs/nvim.nix b/nixos/profiles/workstation/pkgs/nvim.nix
index 46a678c..aaedda6 100644
--- a/nixos/profiles/workstation/pkgs/nvim.nix
+++ b/nixos/profiles/workstation/pkgs/nvim.nix
@@ -19,5 +19,9 @@ pkgs.wrapNeovimUnstable pkgs.neovim-unwrapped {
svelte
typescript
]))
+
+ # For LSP. Language servers are installed in ../dev.nix.
+ nvim-lspconfig
+ blink-cmp
];
}
diff --git a/user/nvim/init.lua b/user/nvim/init.lua
index 108f84a..c841a45 100644
--- a/user/nvim/init.lua
+++ b/user/nvim/init.lua
@@ -25,6 +25,90 @@ pick.setup({ source = { show = pick.default_show } })
-- Reuse Neovim's popup-menu selection color for mini.pick's selected item.
vim.api.nvim_set_hl(0, 'MiniPickMatchCurrent', { link = 'PmenuSel' })
+-- Show diagnostics inline at the end of the affected line.
+vim.diagnostic.config({ virtual_text = true })
+
+-- Enable language servers when they are available (for example, from a nix shell).
+local language_servers = {
+ {
+ name = 'gopls', cmd = { 'gopls' },
+ -- nvim-lspconfig's gopls root detection also invokes `go env`.
+ required_commands = { 'go' }
+ },
+ { name = 'lua_ls', cmd = { 'lua-language-server' } },
+ { name = 'nixd', cmd = { 'nixd' } },
+ { name = 'pyright', cmd = { 'pyright-langserver', '--stdio' } },
+ { name = 'rust_analyzer', cmd = { 'rust-analyzer' } },
+ { name = 'svelte', cmd = { 'svelteserver', '--stdio' } },
+ { name = 'taplo', cmd = { 'taplo', 'lsp', 'stdio' } },
+ { name = 'ts_ls', cmd = { 'typescript-language-server', '--stdio' } },
+}
+
+for _, server in ipairs(language_servers) do
+ local command = vim.fn.exepath(server.cmd[1])
+ local available = command ~= ''
+
+ for _, required_command in ipairs(server.required_commands or {}) do
+ if vim.fn.exepath(required_command) == '' then
+ available = false
+ break
+ end
+ end
+
+ if available then
+ server.cmd[1] = command
+ vim.lsp.config(server.name, { cmd = server.cmd })
+ vim.lsp.enable(server.name)
+ end
+end
+
+local function cursor_is_in_comment()
+ local row, col = unpack(vim.api.nvim_win_get_cursor(0))
+ local ok, captures = pcall(
+ vim.treesitter.get_captures_at_pos,
+ 0,
+ row - 1,
+ math.max(col - 1, 0)
+ )
+
+ if not ok then
+ return false
+ end
+
+ return vim.iter(captures):any(function(capture)
+ return capture.capture == 'comment' or vim.startswith(capture.capture, 'comment.')
+ end)
+end
+
+require('blink.cmp').setup({
+ keymap = {
+ preset = 'default',
+ ['<CR>'] = {
+ 'accept',
+ function()
+ if vim.fn.pumvisible() == 1 then
+ return vim.keycode('<C-y>')
+ end
+ end,
+ 'fallback',
+ },
+ },
+ sources = {
+ -- Only show completion items provided by an attached LSP. This disables
+ -- blink.cmp's default buffer/path/snippet sources, which otherwise trigger
+ -- completions in plain text and Markdown files.
+ default = { 'lsp' },
+ transform_items = function(_, items)
+ -- Some LSPs do autocompletion in comments (e.g. nixd and lua_ls).
+ -- Ignore them in comments.
+ return cursor_is_in_comment() and {} or items
+ end,
+ },
+ completion = {
+ documentation = { auto_show = true },
+ },
+})
+
-- Keyboard shortcuts
-- Clear search highlighting with Escape.
@@ -35,6 +119,13 @@ vim.keymap.set('n', '<C-p>', function()
pick.builtin.files({ tool = tool })
end, { desc = 'Pick files' })
+vim.keymap.set('n', 'gd', vim.lsp.buf.definition, {
+ desc = 'LSP: Go to definition',
+})
+vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, {
+ desc = 'LSP: Go to declaration',
+})
+
-- Disable netrw's directory browser; directory buffers are handled below with MiniPick.
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1