migrate nvim config to 0.12.0: vim.pack, ui2, snacks -> mini, just general spring cleaning

This commit is contained in:
Martin Larsson 2026-04-05 20:05:00 +02:00
parent d1178fbe59
commit 14b1a9b057
70 changed files with 1081 additions and 1483 deletions

59
nvim/plugin/0-pack.lua Normal file
View file

@ -0,0 +1,59 @@
local function gh(repo)
return "https://github.com/" .. repo
end
vim.pack.add({
-- Mini suite
gh("LarssonMartin1998/mini.nvim"),
-- Colorscheme
gh("rktjmp/lush.nvim"),
gh("LarssonMartin1998/nvim-norrsken"),
-- Treesitter
{ src = gh("nvim-treesitter/nvim-treesitter"), version = "main" },
gh("nvim-treesitter/nvim-treesitter-context"),
gh("nvim-treesitter/nvim-treesitter-textobjects"),
-- Completion
gh("rafamadriz/friendly-snippets"),
{ src = gh("L3MON4D3/LuaSnip"), version = vim.version.range("2.x") },
{ src = gh("saghen/blink.cmp"), version = vim.version.range("1.x") },
-- UI
gh("nvim-lualine/lualine.nvim"),
gh("b0o/incline.nvim"),
gh("kosayoda/nvim-lightbulb"),
gh("rachartier/tiny-glimmer.nvim"),
gh("rachartier/tiny-inline-diagnostic.nvim"),
-- DAP
gh("mfussenegger/nvim-dap"),
gh("rcarriga/nvim-dap-ui"),
gh("nvim-neotest/nvim-nio"),
gh("LiadOz/nvim-dap-repl-highlights"),
gh("theHamsta/nvim-dap-virtual-text"),
gh("Weissle/persistent-breakpoints.nvim"),
gh("leoluz/nvim-dap-go"),
-- Misc
gh("OXY2DEV/markview.nvim"),
gh("MunifTanjim/nui.nvim"),
gh("xzbdmw/colorful-menu.nvim"),
})
require("colorful-menu").setup({})
vim.cmd.packadd("nvim.undotree")
vim.api.nvim_create_user_command("VimPackClean", function()
local inactive_plugins = vim.iter(vim.pack.get())
:filter(function(x) return not x.active end)
:map(function(x) return x.spec.name end)
:totable()
vim.notify("Attempting to delete inactive plugins: \n" .. table.concat(inactive_plugins, "\n"))
if not pcall(vim.pack.del, inactive_plugins) then
vim.notify("Failed to delete inactive plugins...")
end
end, {})

11
nvim/plugin/00-hooks.lua Normal file
View file

@ -0,0 +1,11 @@
vim.api.nvim_create_autocmd("PackChanged", {
callback = function(ev)
local name = ev.data.spec.name
local kind = ev.data.kind
if name == "nvim-treesitter" and (kind == "install" or kind == "update") then
if not ev.data.active then vim.cmd.packadd("nvim-treesitter") end
vim.cmd("TSUpdate")
end
end,
})

View file

@ -0,0 +1,10 @@
require("norrsken").setup({
integrations = {
lualine = true,
blink = true,
incline = true,
neogit = true,
mini = true,
tiny_inline_diagnostics = true,
},
})

View file

@ -0,0 +1,110 @@
vim.g.no_plugin_maps = true -- must be set before plugin loads
-- Rewrite API: configs.setup() is gone. Highlighting is handled by Neovim 0.12 builtins.
-- install() skips already-installed parsers automatically.
vim.schedule(function()
require("nvim-treesitter.install").install({
"vim",
"vimdoc",
"bash",
"lua",
"c",
"cpp",
"c_sharp",
"rust",
"cmake",
"make",
"yaml",
"ninja",
"gitignore",
"markdown",
"markdown_inline",
"hyprlang",
"json",
"html",
"hlsl",
"glsl",
"gdshader",
"gdscript",
"dockerfile",
"dart",
"go",
"zig",
"css",
"regex",
"muttrc",
"python",
"latex",
"typst",
"ruby",
"svelte",
"typescript",
"just",
"tsx",
"javascript",
})
end)
require("treesitter-context").setup({
max_lines = 2,
multiline_threshold = 3,
trim_scope = "inner",
})
-- Textobject helpers
local function ts_select(query)
return function()
require("nvim-treesitter-textobjects.select").select_textobject(query, "textobjects")
end
end
local function ts_move_prev(query)
return function()
require("nvim-treesitter-textobjects.move").goto_previous_start(query, "textobjects")
end
end
local function ts_move_next(query)
return function()
require("nvim-treesitter-textobjects.move").goto_next_start(query, "textobjects")
end
end
require("nvim-treesitter-textobjects").setup({
select = { lookahead = true },
move = { set_jumps = true },
})
local utils = require("utils")
-- Textobject select keymaps
for _, mode_maps in ipairs({
{
{ "x", "o" },
{
{ "ic", ts_select("@class.inner") },
{ "ac", ts_select("@class.outer") },
{ "ii", ts_select("@conditional.inner") },
{ "ai", ts_select("@conditional.outer") },
{ "if", ts_select("@function.inner") },
{ "af", ts_select("@function.outer") },
{ "il", ts_select("@loop.inner") },
{ "al", ts_select("@loop.outer") },
{ "ia", ts_select("@parameter.inner") },
{ "aa", ts_select("@parameter.outer") },
},
},
{
{ "n", "x", "o" },
{
{ "[f", ts_move_prev("@function.outer") },
{ "[i", ts_move_prev("@conditional.outer") },
{ "[c", ts_move_prev("@class.outer") },
{ "[l", ts_move_prev("@loop.outer") },
{ "]f", ts_move_next("@function.outer") },
{ "]i", ts_move_next("@conditional.outer") },
{ "]c", ts_move_next("@class.outer") },
{ "]l", ts_move_next("@loop.outer") },
},
},
}) do
utils.set_keymap_list(mode_maps[2], mode_maps[1])
end

43
nvim/plugin/blink.lua Normal file
View file

@ -0,0 +1,43 @@
local ls = require("luasnip")
require("luasnip.loaders.from_vscode").lazy_load()
ls.filetype_extend("typescriptreact", { "html" })
ls.filetype_extend("javascriptreact", { "html" })
ls.config.set_config({
enable_autosnippets = false,
store_selection_keys = false,
})
require("blink.cmp").setup({
keymap = { preset = "super-tab" },
appearance = {
nerd_font_variant = "mono",
},
completion = {
documentation = {
auto_show = false,
},
menu = {
draw = {
columns = { { "kind_icon" }, { "label", gap = 1 } },
components = {
label = {
text = function(ctx)
return require("colorful-menu").blink_components_text(ctx)
end,
highlight = function(ctx)
return require("colorful-menu").blink_components_highlight(ctx)
end,
},
},
},
},
},
snippets = {
preset = "luasnip",
active = function() return false end,
},
sources = {
default = { "lsp", "path", "snippets", "buffer" },
},
fuzzy = { implementation = "prefer_rust_with_warning" },
})

View file

@ -0,0 +1,8 @@
vim.schedule(function()
vim.pack.add({ "https://github.com/mawkler/demicolon.nvim" })
require("demicolon").setup({
keymaps = {
repeat_motions = "stateful",
},
})
end)

View file

@ -0,0 +1,21 @@
vim.schedule(function()
vim.pack.add({
"https://github.com/rmagatti/logger.nvim",
"https://github.com/rmagatti/goto-preview",
})
require("goto-preview").setup({
border = { "", "", "", "", "", "", "", "" },
focus_on_open = true,
stack_floating_preview_windows = false,
preview_window_title = { enable = true, position = "left" },
vim_ui_input = false,
})
local utils = require("utils")
utils.set_keymap_list({
{ "gp", function() require("goto-preview").goto_preview_definition() end },
{ "gy", function() require("goto-preview").goto_preview_type_definition() end },
{ "<Leader>q", function() require("goto-preview").close_all_win() end },
})
end)

103
nvim/plugin/incline.lua Normal file
View file

@ -0,0 +1,103 @@
local function setup_incline()
require("incline").setup({
window = {
padding = 0,
},
hide = {
cursorline = true,
},
render = function(props)
local fullpath = vim.api.nvim_buf_get_name(props.buf)
local filename = vim.fn.fnamemodify(fullpath, ":t")
if filename == "" then
filename = "[No Name]"
end
local function get_ft_icon()
local ft_icon, ft_color = require("nvim-web-devicons").get_icon_color(filename)
return { (ft_icon or "") .. " ", guifg = ft_color, guibg = "none" }
end
local function get_file_path()
local path_display = ""
if fullpath == "" then
path_display = filename
else
local parts = {}
for part in string.gmatch(vim.fn.fnamemodify(fullpath, ":.:h"), "[^/]+") do
table.insert(parts, part)
end
local ellipsis = ""
local max_path_parts = 2
if #parts > max_path_parts then
local start_index = #parts - max_path_parts + 1
path_display = ellipsis .. "/" .. table.concat(parts, "/", start_index)
elseif #parts > 0 then
path_display = table.concat(parts, "/")
end
if path_display ~= "" then
path_display = path_display .. "/" .. filename
else
path_display = filename
end
end
return { path_display .. "", gui = vim.bo[props.buf].modified and "bold,italic" or "bold" }
end
local function get_git_diff()
local summary = vim.b[props.buf].minidiff_summary
local labels = {}
if summary == nil then
return labels
end
for name, icon in pairs({ Delete = "-", Change = "~", Add = "+" }) do
if tonumber(summary[name:lower()]) and summary[name:lower()] > 0 then
table.insert(labels, { icon .. " " .. summary[name:lower()] .. " ", group = "MiniDiffSign" .. name })
end
end
if #labels > 0 then
table.insert(labels, { "" })
end
return labels
end
local function get_diagnostic_label()
local sev = vim.diagnostic.severity
local label = {}
for _, entry in ipairs({
{ sev.ERROR, "Error", "DiagnosticError" },
{ sev.WARN, "Warn", "DiagnosticWarn" },
{ sev.INFO, "Info", "DiagnosticInfo" },
{ sev.HINT, "Hint", "DiagnosticHint" },
}) do
local severity, kind, group = entry[1], entry[2], entry[3]
local n = #vim.diagnostic.get(props.buf, { severity = severity })
if n > 0 then
local icon = MiniIcons.get("lsp", kind)
table.insert(label, { icon .. " " .. n .. " ", group = group })
end
end
if #label > 0 then
table.insert(label, { "" })
end
return label
end
return {
{ " " },
{ get_diagnostic_label() },
{ get_git_diff() },
{ get_ft_icon() },
{ get_file_path() },
{ " " }
}
end,
})
end
vim.api.nvim_create_autocmd("User",
{ pattern = "ColorsyncThemeChanged", callback = setup_incline, group = "ColorsyncEvents" })
setup_incline()

View file

@ -0,0 +1,28 @@
vim.schedule(function()
vim.pack.add({ "https://github.com/saecki/live-rename.nvim" })
require("live-rename").setup({
prepare_rename = true,
request_timeout = 1500,
show_other_ocurrences = true,
use_patterns = true,
scratch_register = "l",
keys = {
submit = {
{ "n", "<cr>" },
{ "v", "<cr>" },
{ "i", "<cr>" },
},
cancel = {
{ "n", "<esc>" },
{ "n", "q" },
},
},
hl = {
current = "CurSearch",
others = "Search",
},
})
vim.keymap.set("n", "<leader>r", function()
require("live-rename").rename({ cursorpos = 0 })
end)
end)

58
nvim/plugin/lualine.lua Normal file
View file

@ -0,0 +1,58 @@
local function resize_mode()
if require("window_management").is_in_resizing_mode() then
return "▲ Resizing ▼ "
else
return " "
end
end
local tabs = {
"tabs",
use_mode_colors = true,
tabs_color = { active = "lualine_b_command" },
show_modified_status = false,
component_separators = { left = "", right = "" },
}
local function setup_lualine()
require("lualine").setup({
options = {
theme = require("norrsken.integrations.lualine"),
globalstatus = true,
section_separators = { left = "", right = "" },
component_separators = { left = "", right = "" },
icons_enabled = true,
},
sections = {
lualine_a = { "mode" },
lualine_b = {
"branch",
{
"diagnostics",
sources = { "nvim_lsp" },
sections = { "error", "warn", "info", "hint" },
update_in_insert = false,
},
function() return vim.ui.progress_status() end,
resize_mode,
},
lualine_c = { tabs },
lualine_x = { "encoding", "fileformat", "filetype" },
lualine_y = { "progress" },
lualine_z = { "location" },
},
inactive_sections = {
lualine_a = {},
lualine_b = {},
lualine_c = {},
lualine_x = {},
lualine_y = {},
lualine_z = {},
},
tabline = {},
})
end
vim.api.nvim_create_autocmd("User",
{ pattern = "ColorsyncThemeChanged", callback = setup_lualine, group = "ColorsyncEvents" })
setup_lualine()

101
nvim/plugin/mini.lua Normal file
View file

@ -0,0 +1,101 @@
local utils = require("utils")
require("mini.icons").setup({})
-- mock_nvim_web_devicons() shims the devicons API so plugins that require it continue to work without the real package.
MiniIcons.mock_nvim_web_devicons()
require("mini.notify").setup({})
vim.notify = MiniNotify.make_notify()
require("mini.indentscope").setup({
symbol = "",
options = { try_as_border = true },
draw = {
animation = require("mini.indentscope").gen_animation.none(),
},
})
require("mini.sessions").setup({
autowrite = true,
})
require("mini.surround").setup({
custom_surroundings = {
["("] = { output = { left = "(", right = ")" } },
["{"] = { output = { left = "{", right = "}" } },
["<"] = { output = { left = "<", right = ">" } },
["["] = { output = { left = "[", right = "]" } },
},
mappings = {
add = "ys",
delete = "ds",
replace = "cs",
find = "",
find_left = "",
highlight = "",
update_n_lines = "",
suffix_last = "",
suffix_next = "",
},
search_method = "cover_or_next",
})
utils.del_keymap_list({ { "ys" } }, "x")
utils.set_keymap_list({
{ "S", [[:<C-u>lua MiniSurround.add('visual')<CR>]], { silent = true } },
}, "x")
-- yss, surround current line
utils.set_keymap_list({
{ "yss", "ys_", { remap = true } },
})
local starter = require("mini.starter")
starter.setup({
items = {
starter.sections.recent_files(8, false),
{
{ name = "Find File", action = "lua MiniPick.builtin.files()", section = "Actions" },
{ name = "Live Grep", action = "lua MiniPick.builtin.grep_live()", section = "Actions" },
{ name = "New File", action = "ene | startinsert", section = "Actions" },
{ name = "Sessions", action = "lua MiniSessions.select()", section = "Actions" },
{ name = "Update Plugins", action = "lua vim.pack.update()", section = "Actions" },
{ name = "Quit", action = "qa", section = "Actions" },
},
},
content_hooks = {
starter.gen_hook.adding_bullet(),
starter.gen_hook.aligning("center", "center"),
},
})
require("mini.diff").setup({})
utils.set_keymap_list({
{ "[g", function() MiniDiff.goto_hunk("prev") end },
{ "]g", function() MiniDiff.goto_hunk("next") end },
{ "<leader>d", function() MiniDiff.toggle_overlay(0) end },
})
require("mini.cursorword").setup({})
-- mini modules reset their highlight groups during setup(), so re-apply
-- the norrsken integration after setup and again on ColorScheme change.
local apply_mini_hl = require("norrsken.integrations.mini")
apply_mini_hl()
vim.api.nvim_create_autocmd("ColorScheme", { callback = apply_mini_hl })
require("mini.pick").setup({})
require("mini.extra").setup()
utils.set_keymap_list({
{ "<leader>f", function()
MiniPick.builtin.cli(
{ command = { "fd", "--type", "f", "--hidden", "--follow", "--exclude", ".git" } },
{ source = { name = "Files", show = MiniPick.default_show } }
)
end },
{ "<leader>g", function() MiniPick.builtin.grep_live() end },
{ "<leader>b", function() MiniPick.builtin.buffers() end },
{ "<leader>o", function() MiniExtra.pickers.lsp({ scope = "workspace_symbol" }) end },
{ "<leader>s", function() MiniExtra.pickers.lsp({ scope = "document_symbol" }) end },
{ "<leader>n", function() MiniNotify.show_history() end },
{ "<leader>x", function() MiniExtra.pickers.diagnostic({ win = { preview = { wo = { wrap = true } } } }) end },
})

18
nvim/plugin/neogit.lua Normal file
View file

@ -0,0 +1,18 @@
vim.schedule(function()
vim.pack.add({
"https://github.com/nvim-lua/plenary.nvim",
"https://github.com/NeogitOrg/neogit",
})
local p = require("norrsken.palette")
require("neogit").setup({
highlight = {
italic = false,
bold = true,
underline = true,
green = p.green,
red = p.red,
},
})
require("norrsken.integrations.neogit")()
vim.keymap.set("n", "<leader>v", function() require("neogit").open() end)
end)

115
nvim/plugin/nvim-dap.lua Normal file
View file

@ -0,0 +1,115 @@
local utils = require("utils")
local inlay_hints_handler = require("inlay_hints_handler")
local is_debug_mode_active = false
local dap = require("dap")
require("nvim-dap-repl-highlights").setup({})
require("nvim-dap-virtual-text").setup({})
require("persistent-breakpoints").setup({ load_breakpoints_event = { "BufReadPost" } })
require("dapui").setup({
controls = { enabled = false },
layouts = {
{
elements = {
{ id = "watches", size = 0.5 },
{ id = "stacks", size = 0.5 },
},
position = "bottom",
size = 15,
},
},
})
require("dap-go").setup({})
dap.adapters.codelldb = require("dap.codelldb")
local virtual_text = require("nvim-dap-virtual-text/virtual_text")
local breakpoint_api = require("persistent-breakpoints.api")
local stepping_keymaps = {
{ "<F10>", function() dap.step_over() end },
{ "<F11>", function() dap.step_into() end },
{ "<F12>", function() dap.step_out() end },
{
"<leader>dc",
function()
require("dapui").float_element("console", {
enter = true,
title = "output",
border = "rounded",
position = "center",
width = math.floor(vim.o.columns * 0.8),
height = math.floor(vim.o.lines * 0.6),
})
end,
},
}
for _, sign in ipairs({
{ "DapBreakpoint", { text = "🛑", texthl = "", linehl = "", numhl = "" } },
{ "DapBreakpointRejected", { text = "🔵", texthl = "", linehl = "", numhl = "" } },
{ "DapBreakpointCondition", { text = "🟥", texthl = "", linehl = "", numhl = "" } },
}) do
vim.fn.sign_define(unpack(sign))
end
local function enter_debug_mode()
if is_debug_mode_active then return end
utils.set_keymap_list(stepping_keymaps)
is_debug_mode_active = true
inlay_hints_handler.disable()
require("dapui").open()
end
local function exit_debug_mode()
if not is_debug_mode_active then return end
utils.del_keymap_list(stepping_keymaps)
is_debug_mode_active = false
inlay_hints_handler.restore()
virtual_text.clear_virtual_text()
require("dapui").close()
end
for _, request in ipairs({ "attach", "launch" }) do
dap.listeners.before[request]["dapui_config"] = enter_debug_mode
end
for _, event in ipairs({ "event_terminated", "event_exited" }) do
dap.listeners.after[event]["dapui_config"] = exit_debug_mode
end
local function dap_stop()
dap.terminate()
dap.close()
exit_debug_mode()
end
utils.set_keymap_list({
{ "<leader>dr", dap.continue },
{ "<leader>bt", breakpoint_api.toggle_breakpoint },
{ "<leader>bc", breakpoint_api.set_conditional_breakpoint },
{ "<leader>br", breakpoint_api.clear_all_breakpoints },
{ "<leader>ds", dap_stop },
})
vim.api.nvim_create_user_command("LaunchTemplate", function()
vim.api.nvim_buf_set_lines(0, 0, -1, false, {
"{",
' "version": "0.2.0",',
' "configurations": [',
" {",
' "type": "codelldb",',
' "request": "launch",',
' "name": "Launch",',
' "program": "${workspaceFolder}/build/binary",',
' "cwd": "${workspaceFolder}",',
' "args": [],',
' "stopOnEntry": false,',
' "environment": []',
" }",
" ]",
"}",
})
end, {})

View file

@ -0,0 +1,9 @@
require("nvim-lightbulb").setup({
hide_in_unfocused_buffer = true,
code_lenses = false, -- 0.12 shows code lenses as virtual lines natively
sign = { enabled = true },
virtual_text = { enabled = false },
float = { enabled = false },
status_text = { enabled = false },
autocmd = { enabled = true, updatetime = 25 },
})

4
nvim/plugin/rephrase.lua Normal file
View file

@ -0,0 +1,4 @@
vim.opt.runtimepath:append("/Users/larssonmartin/dev/git/nvim-rephrase")
if pcall(require, "rephrase") then
require("rephrase").setup({})
end

View file

@ -0,0 +1,35 @@
vim.api.nvim_create_autocmd("FileType", {
pattern = "rust",
once = true,
callback = function()
vim.g.rustaceanvim = {
inlay_hints = {
highlight = "NonText",
},
tools = {
hover_actions = {
auto_focus = true,
replace_builtin_hover = true,
},
},
server = {
default_settings = {
["rust-analyzer"] = {
inlayHints = {
chainingHints = true,
parameterHints = true,
typeHints = true,
},
diagnostics = {
enable = true,
experimental = { enable = true },
},
},
},
},
}
vim.pack.add({
{ src = "https://github.com/mrcjkb/rustaceanvim", version = vim.version.range("5.x") },
})
end,
})

View file

@ -0,0 +1,24 @@
require("tiny-glimmer").setup({
refresh_interval_ms = 6,
overwrite = {
auto_map = true,
paste = {
enabled = true,
default_animation = { name = "fade", settings = { from_color = "DiffText" } },
},
undo = {
enabled = true,
default_animation = { name = "fade", settings = { from_color = "DiffDelete" } },
},
redo = {
enabled = true,
default_animation = { name = "fade", settings = { from_color = "DiffAdd" } },
},
},
animations = {
fade = {
chars_for_max_duration = 1,
to_color = "Folded",
},
},
})

View file

@ -0,0 +1,11 @@
require("tiny-inline-diagnostic").setup({
preset = "modern",
transparent_bg = false,
transparent_cursorline = false,
options = {
multilines = {
enabled = true,
always_show = true,
},
},
})

View file

@ -0,0 +1,8 @@
vim.api.nvim_create_autocmd("ModeChanged", {
pattern = "*:[vV\22]",
once = true,
callback = function()
vim.pack.add({ "https://github.com/mcauley-penney/visual-whitespace.nvim" })
require("visual-whitespace").setup({})
end,
})