From 61b1e3d2a9f12c24045ddf08fedd765ba50bce0e Mon Sep 17 00:00:00 2001 From: Thomas Naderer Date: Sun, 1 Mar 2026 21:26:55 +0100 Subject: [PATCH] refactor(nvim): modernize plugin stack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed dead weight: - Drop nightfox, rose-pine, kanagawa, everforest colorschemes - Remove fzf/fzf.vim (redundant with telescope) - Remove vim-fugitive and its keymaps (lazygit covers this) - Remove Comment.nvim (built into nvim 0.10+) Replaced: - nvim-cmp → blink.cmp (Rust-powered, faster, ghost text) - none-ls/null-ls → conform.nvim (async, maintained) - cmp-nvim-lsp capabilities → blink.cmp.get_lsp_capabilities() - Copilot suggestions now routed through blink-copilot Added: - lazydev.nvim for proper Lua LSP in nvim config - flash.nvim (s/S jump anywhere on screen) - which-key.nvim (leader key popup with groups) - snacks.nvim (notifier + word highlights) - nvim-treesitter-textobjects (af/if, ac/ic, ]f/[f) Co-Authored-By: Claude Sonnet 4.6 --- nvim/lua/keymaps.lua | 119 +++++++++++------------ nvim/lua/plugins/init.lua | 76 ++++++++++----- nvim/lua/plugins/lsp.lua | 165 ++++++++++++++++---------------- nvim/lua/plugins/lualine.lua | 2 +- nvim/lua/plugins/prettier.lua | 55 +++++------ nvim/lua/plugins/treesitter.lua | 26 +++++ 6 files changed, 252 insertions(+), 191 deletions(-) diff --git a/nvim/lua/keymaps.lua b/nvim/lua/keymaps.lua index a1048de..968e4f7 100644 --- a/nvim/lua/keymaps.lua +++ b/nvim/lua/keymaps.lua @@ -5,7 +5,7 @@ local opt = { noremap = true } local opts = { noremap = true, silent = true } local term_opts = { silent = true } -local keymap = vim.api.nvim_set_keymap +local keymap = vim.keymap.set -- {{{ Leader Key (moved to init.lua) -- vim.g.mapleader = " " -- set as leader key @@ -13,15 +13,15 @@ local keymap = vim.api.nvim_set_keymap -- }}} -- {{{ Basic Mappings -keymap("n", ";", ":", opt) -- remap ; to : -keymap("n", ":", ";", opt) -- remap : to ; +keymap("n", ";", ":", opt) -- remap ; to : +keymap("n", ":", ";", opt) -- remap : to ; -keymap("n", "w", ":w", opts) -- save -keymap("n", "q", ":q", opts) -- quit -keymap("n", "wq", ":wq", opts) -- save and quit -keymap("n", "W", ":w!", opts) -- force save -keymap("n", "Q", ":q!", opts) -- force quit -keymap("n", "WQ", ":wq!", opts) -- force save and quit +keymap("n", "w", ":w", opts) -- save current buffer +keymap("n", "q", ":confirm qall", opts) -- quit Neovim (prompt on unsaved changes) +keymap("n", "wq", ":wall | qa", opts) -- save all writable buffers and quit +keymap("n", "W", ":w!", opts) -- force save current buffer +keymap("n", "Q", ":qall!", opts) -- force quit Neovim +keymap("n", "WQ", ":wall! | qa!", opts) -- force save all and quit -- }}} -- {{{ Navigation @@ -29,125 +29,122 @@ keymap("n", "j", "gj", opts) -- move visually down wrapped lines keymap("n", "k", "gk", opts) -- move visually up wrapped lines -- Keep arrow key navigation (non-standard but you requested it) -keymap("n", "", "h", opts) -- move to left window -keymap("n", "", "l", opts) -- move to right window -keymap("n", "", "k", opts) -- move to upper window -keymap("n", "", "j", opts) -- move to lower window +keymap("n", "", "h", opts) -- move to left window +keymap("n", "", "l", opts) -- move to right window +keymap("n", "", "k", opts) -- move to upper window +keymap("n", "", "j", opts) -- move to lower window -keymap("n", "", ":bnext", opts) -- next buffer +keymap("n", "", ":bnext", opts) -- next buffer keymap("n", "", ":bprevious", opts) -- previous buffer -- }}} -- {{{ Window Splitting keymap("n", "sv", ":vsplit", opts) -- split vertically -keymap("n", "sh", ":split", opts) -- split horizontally -keymap("n", "sc", ":close", opts) -- close current split -keymap("n", "so", ":only", opts) -- close all other splits +keymap("n", "sh", ":split", opts) -- split horizontally +keymap("n", "sc", ":close", opts) -- close current split +keymap("n", "so", ":only", opts) -- close all other splits -- }}} -- {{{ Window Resizing -keymap("n", "+", ":resize +5", opts) -- increase window height -keymap("n", "-", ":resize -5", opts) -- decrease window height +keymap("n", "+", ":resize +5", opts) -- increase window height +keymap("n", "-", ":resize -5", opts) -- decrease window height keymap("n", "<", ":vertical resize -5", opts) -- decrease window width keymap("n", ">", ":vertical resize +5", opts) -- increase window width -keymap("n", "=", "=", opts) -- equalize window sizes +keymap("n", "=", "=", opts) -- equalize window sizes -- }}} -- {{{ Visual Mode / Text, Clipboard -keymap("v", ">", ">gv", opts) -- indent right, stay in visual mode -keymap("v", "<", "", ">gv", opts) -- indent right, stay in visual mode +keymap("v", "<", "", ":move '>+1gv-gv", opts) -- move selected block down -keymap("x", "", ":move '<-2gv-gv", opts) -- move selected block up +keymap("x", "", ":move '>+1gv-gv", opts) -- move selected block down +keymap("x", "", ":move '<-2gv-gv", opts) -- move selected block up -keymap("n", "Q", "gq", opts) -- format paragraph -keymap("x", "Q", "gq", opts) -- format selection +keymap("n", "Q", "gq", opts) -- format paragraph +keymap("x", "Q", "gq", opts) -- format selection -keymap("x", "Y", '"+y', opt) -- yank to system clipboard +keymap("x", "Y", '"+y', opt) -- yank to system clipboard keymap("n", "", [[:%s/\<=expand("")\>/]], opt) -- replace word under cursor -- }}} -- {{{ Editing / Behavior -keymap("n", "q", "", opts) -- disable q (record macro) -keymap("n", "qq", "q", opts) -- if you really want to record macro use qq +keymap("n", "q", "", opts) -- disable q (record macro) +keymap("n", "qq", "q", opts) -- if you really want to record macro use qq -keymap("n", "c", '"_c', opts) -- change without yanking to default register -keymap("n", "C", '"_C', opts) -- change line without yanking to default register +keymap("n", "c", '"_c', opts) -- change without yanking to default register +keymap("n", "C", '"_C', opts) -- change line without yanking to default register vim.keymap.set("n", "n", function() -- toggle line numbers - if vim.wo.number and vim.wo.relativenumber then - vim.wo.number = false - vim.wo.relativenumber = false - elseif vim.wo.number then - vim.wo.relativenumber = true - else - vim.wo.number = true - end + if vim.wo.number and vim.wo.relativenumber then + vim.wo.number = false + vim.wo.relativenumber = false + elseif vim.wo.number then + vim.wo.relativenumber = true + else + vim.wo.number = true + end end, { desc = "Toggle line number modes" }) keymap("n", "c", ":set cursorcolumn! cursorline! ", opts) -- toggle cursor column and line -keymap("n", "", ":nohlsearch", opts) -- clear search highlight +keymap("n", "", ":nohlsearch", opts) -- clear search highlight -- }}} -- {{{ Spellcheck keymap("n", "se", ":setlocal spell! spelllang=en_us", opts) -- toggle English spell check keymap("n", "sd", ":setlocal spell! spelllang=de_at", opts) -- toggle Austrian German spell check -keymap("n", "ss", ":setlocal spell! spelllang=es", opts) -- toggle Spanish spell check +keymap("n", "ss", ":setlocal spell! spelllang=es", opts) -- toggle Spanish spell check -- }}} -- {{{ Timestamp / Date keymap("n", "dt", [[i=strftime("%d.%m.%Y")]], opts) -- insert current date -keymap("n", "tt", [[i=strftime("%H:%M")]], opts) -- insert current time +keymap("n", "tt", [[i=strftime("%H:%M")]], opts) -- insert current time -- }}} -- {{{ External / System keymap("n", "x", [[:!open %]], opts) -- open current file with system default app -keymap("c", "w!!", [[w !sudo tee %]], opt) -- save file with sudo +keymap("c", "w!!", [[w !sudo tee %]], opt) -- save file with sudo -- }}} -- {{{ Diff / Git -keymap("n", "gd", ":Gvdiffsplit", opts) -- git diff split (requires fugitive) -keymap("n", "gs", ":Git", opts) -- open git status tree (requires fugitive) keymap("n", "gr", ":terminal git log --graph --oneline --decorate --alli", opts) -- show git log graph in terminal -- GitSigns toggles vim.keymap.set('n', 'gt', function() require('gitsigns').toggle_signs() end, { desc = 'Toggle git signs' }) -vim.keymap.set('n', 'gb', function() require('gitsigns').toggle_current_line_blame() end, { desc = 'Toggle git blame' }) +vim.keymap.set('n', 'gb', function() require('gitsigns').toggle_current_line_blame() end, + { desc = 'Toggle git blame' }) vim.keymap.set('n', 'gx', function() require('gitsigns').toggle_deleted() end, { desc = 'Toggle git deleted' }) -vim.keymap.set('n', 'gn', function() require('gitsigns').toggle_numhl() end, { desc = 'Toggle git number highlights' }) +vim.keymap.set('n', 'gn', function() require('gitsigns').toggle_numhl() end, + { desc = 'Toggle git number highlights' }) vim.keymap.set('n', 'gc', function() require('gitsigns').toggle_linehl() end, { desc = 'Toggle git line colors' }) -- LSP diagnostic toggle vim.keymap.set('n', 'ld', function() - local config = vim.diagnostic.config() - vim.diagnostic.config({ - virtual_text = not config.virtual_text, - signs = not config.signs, - underline = not config.underline, - }) + local config = vim.diagnostic.config() + vim.diagnostic.config({ + virtual_text = not config.virtual_text, + signs = not config.signs, + underline = not config.underline, + }) end, { desc = 'Toggle LSP diagnostics' }) -- Toggle fold column vim.keymap.set("n", "un", function() - vim.wo.foldcolumn = vim.wo.foldcolumn == "0" and "1" or "0" + vim.wo.foldcolumn = vim.wo.foldcolumn == "0" and "1" or "0" end, { desc = "Toggle fold column" }) keymap("n", "si", ":source $MYVIMRC", opts) -- source init.lua ($MYVIMRC is auto path to your init.lua) -- }}} -- {{{ Plugins --- Yazi (keeping this here since it's a simple command keymap) -keymap("n", "y", ":Yazi", opt) -- open Yazi file manager - -- mkdx checkbox toggle (disable default space key, use leader+t instead) vim.g["mkdx#settings"] = { - checkbox = { - toggles = {' ', 'x', '-'}, - toggle_key = '' - } + checkbox = { + toggles = { ' ', 'x', '-' }, + toggle_key = '' + } } keymap("n", "t", "(mkdx-checkbox-next-n)", opt) -- toggle markdown checkbox -- }}} diff --git a/nvim/lua/plugins/init.lua b/nvim/lua/plugins/init.lua index c6b1f4c..e4ec058 100644 --- a/nvim/lua/plugins/init.lua +++ b/nvim/lua/plugins/init.lua @@ -4,10 +4,6 @@ return { { "gruvbox-community/gruvbox", priority = 1000 }, { "folke/tokyonight.nvim", priority = 1000 }, { "catppuccin/nvim", name = "catppuccin", priority = 1000 }, - { "EdenEast/nightfox.nvim", priority = 1000 }, - { "rose-pine/neovim", name = "rose-pine", priority = 1000 }, - { "rebelot/kanagawa.nvim", priority = 1000 }, - { "sainnhe/everforest", priority = 1000 }, -- Auto-save @@ -73,10 +69,6 @@ return { current_line_blame = false, } }, - { - "tpope/vim-fugitive", - cmd = { "Git", "Gvdiffsplit" }, - }, { "kdheepak/lazygit.nvim", dependencies = { "nvim-lua/plenary.nvim" }, @@ -107,30 +99,70 @@ return { right_alt = "", space = " ", } + -- Tokyo Night Moon colors, transparent background + vim.g.tmuxline_theme = { + a = { "#1e2030", "#82aaff" }, + b = { "#82aaff", "default" }, + c = { "#636da6", "default" }, + x = { "#636da6", "default" }, + y = { "#82aaff", "default" }, + z = { "#1e2030", "#82aaff" }, + win = { "#636da6", "default" }, + cwin = { "#1e2030", "#c099ff" }, + bg = { "#636da6", "default" }, + } vim.g.tmuxline_preset = { - a = "#S", - win = "#I #W", + a = "#S", + win = "#I #W", cwin = "#I #W", - y = "%H:%M", - z = "%d %b", + y = "%H:%M", + z = "%d %b", } end, }, - -- FZF - { - "junegunn/fzf", - build = function() - vim.fn["fzf#install"]() - end, - }, - { "junegunn/fzf.vim" }, - -- Productivity - { "numToStr/Comment.nvim", event = "VeryLazy", opts = {} }, { "windwp/nvim-autopairs", event = "InsertEnter", opts = {} }, { "tpope/vim-surround", event = "VeryLazy" }, + -- Flash: jump anywhere with 2 chars (replaces f/t for long range) + { + "folke/flash.nvim", + event = "VeryLazy", + opts = {}, + keys = { + { "s", mode = { "n", "x", "o" }, function() require("flash").jump() end, desc = "Flash jump" }, + { "S", mode = { "n", "x", "o" }, function() require("flash").treesitter() end, desc = "Flash treesitter" }, + }, + }, + + -- Which-key: show available keybindings after leader + { + "folke/which-key.nvim", + event = "VeryLazy", + opts = { + spec = { + { "f", group = "find" }, + { "g", group = "git" }, + { "s", group = "split/spell" }, + { "w", group = "write" }, + { "d", group = "date" }, + { "l", group = "lsp" }, + }, + }, + }, + + -- Snacks: notifications + quality-of-life utilities + { + "folke/snacks.nvim", + priority = 1000, + lazy = false, + opts = { + notifier = { enabled = true }, + words = { enabled = true }, + }, + }, + -- -- Tmux navigation -- { "christoomey/vim-tmux-navigator" }, diff --git a/nvim/lua/plugins/lsp.lua b/nvim/lua/plugins/lsp.lua index 7d60aa0..915c483 100644 --- a/nvim/lua/plugins/lsp.lua +++ b/nvim/lua/plugins/lsp.lua @@ -1,12 +1,23 @@ -- ~/.config/nvim/lua/plugins/lsp.lua return { + -- Lazydev: proper Lua LSP for neovim config files + { + "folke/lazydev.nvim", + ft = "lua", + opts = { + library = { + { path = "luvit-meta/library", words = { "vim%.uv" } }, + }, + }, + }, + -- Mason (LSP installer) { "williamboman/mason.nvim", build = ":MasonUpdate", opts = {}, }, - + -- Mason LSP config integration { "williamboman/mason-lspconfig.nvim", @@ -15,19 +26,19 @@ return { ensure_installed = { "lua_ls", "pyright", "texlab", "bashls", "marksman" }, }, }, - + -- LSP configuration { "neovim/nvim-lspconfig", - dependencies = { + dependencies = { "mason.nvim", "mason-lspconfig.nvim", - "hrsh7th/cmp-nvim-lsp", + "saghen/blink.cmp", }, config = function() - local lspconfig = require("lspconfig") - local capabilities = require("cmp_nvim_lsp").default_capabilities() - + local capabilities = require("blink.cmp").get_lsp_capabilities() + local servers = { "lua_ls", "pyright", "texlab", "bashls", "marksman" } + -- Configure diagnostics to be hidden by default vim.diagnostic.config({ virtual_text = false, @@ -36,7 +47,7 @@ return { update_in_insert = false, severity_sort = false, }) - + -- Set up keymaps for LSP vim.api.nvim_create_autocmd("LspAttach", { group = vim.api.nvim_create_augroup("UserLspConfig", {}), @@ -51,86 +62,80 @@ return { vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts) end, }) - - -- Language servers setup with capabilities - lspconfig.lua_ls.setup({ - capabilities = capabilities, - }) - lspconfig.pyright.setup({ - capabilities = capabilities, - }) - lspconfig.texlab.setup({ - capabilities = capabilities, - }) - lspconfig.bashls.setup({ - capabilities = capabilities, - }) - lspconfig.marksman.setup({ - capabilities = capabilities, - }) + + -- Use Neovim 0.11+ LSP API to avoid deprecated lspconfig setup calls. + if vim.lsp.config and vim.lsp.enable then + for _, server in ipairs(servers) do + vim.lsp.config(server, { capabilities = capabilities }) + end + vim.lsp.enable(servers) + else + local lspconfig = require("lspconfig") + for _, server in ipairs(servers) do + lspconfig[server].setup({ capabilities = capabilities }) + end + end end, }, - - -- Completion + + -- Copilot (suggestion/panel disabled — blink-copilot handles completions) { - "hrsh7th/nvim-cmp", + "zbirenbaum/copilot.lua", + event = "InsertEnter", + opts = { + suggestion = { enabled = false }, + panel = { enabled = false }, + }, + }, + + -- blink.cmp: fast Rust-powered completion engine + { + "saghen/blink.cmp", + version = "*", dependencies = { - "hrsh7th/cmp-buffer", - "hrsh7th/cmp-path", - "hrsh7th/cmp-nvim-lsp", - "hrsh7th/cmp-nvim-lua", - "saadparwaiz1/cmp_luasnip", + "rafamadriz/friendly-snippets", { "L3MON4D3/LuaSnip", - dependencies = { "rafamadriz/friendly-snippets", "evesdropper/luasnip-latex-snippets.nvim" }, build = "make install_jsregexp", + dependencies = { "evesdropper/luasnip-latex-snippets.nvim" }, + config = function() + require("luasnip.loaders.from_vscode").lazy_load() + require("luasnip.loaders.from_lua").load({ paths = "~/.config/nvim/lua/snippets" }) + end, + }, + { + "fmuaddel/blink-copilot", + dependencies = { "zbirenbaum/copilot.lua" }, }, }, - config = function() - local cmp = require("cmp") - local luasnip = require("luasnip") - - -- Load snippets - require("luasnip.loaders.from_vscode").lazy_load() - require("luasnip.loaders.from_lua").load({ paths = "~/.config/nvim/lua/snippets" }) - - cmp.setup({ - snippet = { - expand = function(args) - luasnip.lsp_expand(args.body) - end, + opts = { + keymap = { + preset = "default", + [""] = { "select_next", "snippet_forward", "fallback" }, + [""] = { "select_prev", "snippet_backward", "fallback" }, + [""] = { "accept", "fallback" }, + }, + appearance = { nerd_font_variant = "mono" }, + sources = { + default = { "lazydev", "lsp", "path", "snippets", "buffer", "copilot" }, + providers = { + lazydev = { + name = "LazyDev", + module = "lazydev.integrations.blink", + score_offset = 100, + }, + copilot = { + name = "copilot", + module = "blink-copilot", + score_offset = 50, + async = true, + }, }, - mapping = cmp.mapping.preset.insert({ - [""] = cmp.mapping.complete(), - [""] = cmp.mapping.confirm({ select = true }), - [""] = cmp.mapping(function(fallback) - if require("copilot.suggestion").is_visible() then - require("copilot.suggestion").accept() - elseif cmp.visible() then - cmp.select_next_item() - elseif luasnip.expand_or_jumpable() then - luasnip.expand_or_jump() - else - fallback() - end - end, { "i", "s" }), - [""] = cmp.mapping(function(fallback) - if cmp.visible() then - cmp.select_prev_item() - elseif luasnip.jumpable(-1) then - luasnip.jump(-1) - else - fallback() - end - end, { "i", "s" }), - }), - sources = cmp.config.sources({ - { name = "nvim_lsp" }, - { name = "luasnip" }, - { name = "buffer" }, - { name = "path" }, - }), - }) - end, + }, + snippets = { preset = "luasnip" }, + completion = { + ghost_text = { enabled = true }, + }, + }, }, -} \ No newline at end of file +} diff --git a/nvim/lua/plugins/lualine.lua b/nvim/lua/plugins/lualine.lua index 8e73892..b3adedb 100644 --- a/nvim/lua/plugins/lualine.lua +++ b/nvim/lua/plugins/lualine.lua @@ -188,6 +188,6 @@ return { tabline = {}, winbar = {}, inactive_winbar = {}, - extensions = { "nvim-tree", "fzf", "lazy" }, + extensions = { "nvim-tree", "lazy" }, }, } \ No newline at end of file diff --git a/nvim/lua/plugins/prettier.lua b/nvim/lua/plugins/prettier.lua index 63ff3a3..a121158 100644 --- a/nvim/lua/plugins/prettier.lua +++ b/nvim/lua/plugins/prettier.lua @@ -1,28 +1,29 @@ return { - "nvimtools/none-ls.nvim", - dependencies = { "nvim-lua/plenary.nvim" }, - config = function() - local null_ls = require("null-ls") - null_ls.setup({ - debug = false, - sources = { - -- null_ls.builtins.diagnostics.markdownlint.with({ - -- filetypes = { "markdown", "markdown.pandoc", "pandoc" }, - -- }), - null_ls.builtins.formatting.prettier.with({ - filetypes = { "markdown", "markdown.pandoc", "pandoc" }, - }), - }, - on_attach = function(client, bufnr) - if client.supports_method("textDocument/formatting") then - vim.api.nvim_create_autocmd("BufWritePre", { - buffer = bufnr, - callback = function() - vim.lsp.buf.format({ bufnr = bufnr }) - end, - }) - end - end, - }) - end, -} \ No newline at end of file + "stevearc/conform.nvim", + event = { "BufWritePre" }, + cmd = { "ConformInfo" }, + keys = { + { + "fm", + function() require("conform").format({ async = true }) end, + desc = "Format buffer", + }, + }, + opts = { + formatters_by_ft = { + javascript = { "prettier" }, + typescript = { "prettier" }, + javascriptreact = { "prettier" }, + typescriptreact = { "prettier" }, + json = { "prettier" }, + html = { "prettier" }, + css = { "prettier" }, + markdown = { "prettier" }, + yaml = { "prettier" }, + }, + format_on_save = { + timeout_ms = 500, + lsp_fallback = true, + }, + }, +} diff --git a/nvim/lua/plugins/treesitter.lua b/nvim/lua/plugins/treesitter.lua index f5f4046..cda6d0b 100644 --- a/nvim/lua/plugins/treesitter.lua +++ b/nvim/lua/plugins/treesitter.lua @@ -1,6 +1,8 @@ return { + { "nvim-treesitter/nvim-treesitter", build = ":TSUpdate", + dependencies = { "nvim-treesitter/nvim-treesitter-textobjects" }, config = function() require("nvim-treesitter.configs").setup({ ensure_installed = { "c", "lua", "python", "javascript", "markdown", "markdown_inline", "html", "css", "bash", "vim", "vimdoc" }, @@ -39,6 +41,29 @@ return { node_decremental = "grm", }, }, + + textobjects = { + select = { + enable = true, + lookahead = true, + keymaps = { + ["af"] = "@function.outer", + ["if"] = "@function.inner", + ["ac"] = "@class.outer", + ["ic"] = "@class.inner", + ["aa"] = "@parameter.outer", + ["ia"] = "@parameter.inner", + }, + }, + move = { + enable = true, + set_jumps = true, + goto_next_start = { ["]f"] = "@function.outer", ["]c"] = "@class.outer" }, + goto_next_end = { ["]F"] = "@function.outer", ["]C"] = "@class.outer" }, + goto_previous_start = { ["[f"] = "@function.outer", ["[c"] = "@class.outer" }, + goto_previous_end = { ["[F"] = "@function.outer", ["[C"] = "@class.outer" }, + }, + }, }) -- Better folding setup @@ -47,4 +72,5 @@ return { vim.opt.foldlevel = 20 vim.opt.foldenable = false end, + }, } \ No newline at end of file