├── .gitignore ├── .mdformat.toml ├── .stylua.toml ├── README.md ├── init.lua ├── lua └── plugins │ └── core.lua └── reset.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | # lazy nvim plugin 2 | lazy-lock.json 3 | 4 | # neodev plugin 5 | .luarc.json 6 | -------------------------------------------------------------------------------- /.mdformat.toml: -------------------------------------------------------------------------------- 1 | # .mdformat.toml 2 | # 3 | # This file shows the default values and is equivalent to having 4 | # no configuration file at all. Change the values for non-default 5 | # behavior. 6 | # 7 | wrap = 80 # options: {"keep", "no", INTEGER} 8 | number = false # options: {false, true} 9 | end_of_line = "lf" # options: {"lf", "crlf", "keep"} 10 | validate = true # options: {false, true} 11 | # extensions = [ # options: a list of enabled extensions (default: all installed are enabled) 12 | # "gfm", 13 | # "toc", 14 | # ] 15 | # codeformatters = [ # options: a list of enabled code formatter languages (default: all installed are enabled) 16 | # "python", 17 | # "json", 18 | # ] 19 | 20 | # Python 3.13+ only: 21 | # exclude = [] # options: a list of file path pattern strings 22 | -------------------------------------------------------------------------------- /.stylua.toml: -------------------------------------------------------------------------------- 1 | indent_type = "Spaces" 2 | indent_width = 4 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Neovim Configuration 2 | 3 | https://github.com/user-attachments/assets/51d861a0-b026-4d94-a013-6a277ba6e52f 4 | 5 | This is my custom [Neovim] configuration. It features a single configuration 6 | file [core.lua] and a custom color scheme called [peanut] which is based off of 7 | [JellyBean]. In addition to integrated debugging, unit testing, language server 8 | support, and treesitter highlighting, the following plugins are also included: 9 | 10 | - Blink 11 | - Conform 12 | - DAP / DAP UI 13 | - GitSigns 14 | - Lazy 15 | - Lualine 16 | - Markview 17 | - Mason 18 | - Mini Icons 19 | - Mini Pairs 20 | - Neotest 21 | - Oil 22 | - Persistence 23 | - Rustaceanvim 24 | - Telescope 25 | - Trouble 26 | - Treesitter 27 | - Snacks 28 | - Which Key 29 | 30 | Out of box support for the following languages: 31 | 32 | - C/C++ 33 | - JSON 34 | - Lua 35 | - Markdown 36 | - Rust 37 | - TOML 38 | 39 | The color scheme is heavily integrated into all the plugins making for a very 40 | consistent visual experience. 41 | 42 | [core.lua]: https://github.com/freddiehaddad/nvim/blob/main/lua/plugins/core.lua 43 | [jellybean]: https://github.com/WTFox/jellybeans.nvim 44 | [neovim]: https://github.com/neovim/neovim 45 | [peanut]: https://github.com/freddiehaddad/nvim/blob/main/colors/peanut.lua 46 | -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | -- Options 2 | vim.g.mapleader = " " 3 | vim.g.maplocalleader = " " 4 | vim.g.loaded_node_provider = 0 5 | vim.g.loaded_perl_provider = 0 6 | vim.g.loaded_python3_provider = 0 7 | 8 | local opt = vim.opt 9 | -- Schedule the setting after `UiEnter` because it can increase startup-time. 10 | -- Remove this option if you want your OS clipboard to remain independent. 11 | vim.schedule(function() 12 | opt.clipboard = "unnamedplus" 13 | end) 14 | 15 | opt.cmdheight = 0 16 | opt.cursorline = true 17 | opt.fillchars:append({ 18 | eob = " ", 19 | fold = " ", 20 | foldopen = "", 21 | foldclose = "", 22 | foldsep = " ", 23 | horiz = " ", 24 | horizup = " ", 25 | horizdown = " ", 26 | vert = " ", 27 | vertleft = " ", 28 | vertright = " ", 29 | verthoriz = " ", 30 | }) 31 | opt.fileformat = "unix" 32 | opt.fileformats = "unix,dos" 33 | opt.foldenable = true 34 | opt.foldlevel = 99 35 | opt.ignorecase = true 36 | opt.laststatus = 3 37 | opt.mouse = "" 38 | opt.number = true 39 | opt.pumblend = 10 40 | opt.relativenumber = true 41 | opt.scrolloff = 2 42 | opt.showmode = false 43 | opt.showtabline = 0 44 | opt.sidescrolloff = 2 45 | opt.smartcase = true 46 | opt.smoothscroll = true 47 | opt.splitright = true 48 | opt.splitbelow = true 49 | opt.undofile = true 50 | opt.undolevels = 10000 51 | opt.virtualedit = "block" 52 | opt.wrap = false 53 | 54 | -- Default file format 55 | opt.expandtab = true 56 | opt.shiftwidth = 4 57 | opt.tabstop = 4 58 | opt.textwidth = 80 59 | 60 | -- Powershell options 61 | opt.shell = "pwsh" 62 | opt.shellcmdflag = 63 | "-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.UTF8Encoding]::new();$PSDefaultParameterValues['Out-File:Encoding']='utf8';$PSStyle.OutputRendering=[System.Management.Automation.OutputRendering]::PlainText;Remove-Alias -Force -ErrorAction SilentlyContinue tee;" 64 | opt.shellredir = "2>&1 | %%{ '$_' } | Out-File %s; exit $LastExitCode" 65 | opt.shellpipe = "2>&1 | %%{ '$_' } | tee %s; exit $LastExitCode" 66 | opt.shellquote = "" 67 | opt.shellxquote = "" 68 | 69 | --Keymaps 70 | local map = vim.keymap.set 71 | 72 | -- better up/down 73 | map({ "n", "x" }, "j", "v:count == 0 ? 'gj' : 'j'", { desc = "Down", expr = true, silent = true }) 74 | map({ "n", "x" }, "k", "v:count == 0 ? 'gk' : 'k'", { desc = "Up", expr = true, silent = true }) 75 | 76 | -- Move to window using the hjkl keys 77 | map("n", "", "h", { desc = "Go to left window", remap = true }) 78 | map("n", "", "j", { desc = "Go to lower window", remap = true }) 79 | map("n", "", "k", { desc = "Go to upper window", remap = true }) 80 | map("n", "", "l", { desc = "Go to right window", remap = true }) 81 | 82 | -- Resize window using arrow keys 83 | map("n", "", "resize +1", { desc = "Increase window height" }) 84 | map("n", "", "resize -1", { desc = "Decrease window height" }) 85 | map("n", "", "vertical resize -1", { desc = "Decrease window width" }) 86 | map("n", "", "vertical resize +1", { desc = "Increase window width" }) 87 | 88 | -- Split windows 89 | map("n", "w=", "=", { desc = "Equally high and wide", remap = true }) 90 | map("n", "wh", "rightbelow split", { desc = "Split window below" }) 91 | map("n", "wH", "leftabove split", { desc = "Split window above" }) 92 | map("n", "wv", "rightbelow vsplit", { desc = "Split window right" }) 93 | map("n", "wV", "leftabove vsplit", { desc = "Split window left" }) 94 | map("n", "wd", "c", { desc = "Delete window", remap = true }) 95 | 96 | -- Move lines 97 | map("n", "", "execute 'move .+' . v:count1==", { desc = "Move down" }) 98 | map("n", "", "execute 'move .-' . (v:count1 + 1)==", { desc = "Move up" }) 99 | map("i", "", "m .+1==gi", { desc = "Move down" }) 100 | map("i", "", "m .-2==gi", { desc = "Move up" }) 101 | map("v", "", ":execute \"'<,'>move '>+\" . v:count1gv=gv", { desc = "Move down" }) 102 | map("v", "", ":execute \"'<,'>move '<-\" . (v:count1 + 1)gv=gv", { desc = "Move up" }) 103 | 104 | -- Buffers 105 | map("n", "", "bprevious", { desc = "Prev buffer" }) 106 | map("n", "", "bnext", { desc = "Next buffer" }) 107 | map("n", "[b", "bprevious", { desc = "Prev buffer" }) 108 | map("n", "]b", "bnext", { desc = "Next buffer" }) 109 | 110 | -- Clear search and stop snippet on escape 111 | map({ "i", "n", "s" }, "", function() 112 | vim.cmd("nohlsearch") 113 | if vim.snippet then 114 | vim.snippet.stop() 115 | end 116 | return "" 117 | end, { expr = true, desc = "Escape and clear hlsearch" }) 118 | 119 | -- https://github.com/mhinz/vim-galore#saner-behavior-of-n-and-n 120 | map("n", "n", "'Nn'[v:searchforward].'zv'", { expr = true, desc = "Next search result" }) 121 | map("x", "n", "'Nn'[v:searchforward]", { expr = true, desc = "Next search result" }) 122 | map("o", "n", "'Nn'[v:searchforward]", { expr = true, desc = "Next search result" }) 123 | map("n", "N", "'nN'[v:searchforward].'zv'", { expr = true, desc = "Prev search result" }) 124 | map("x", "N", "'nN'[v:searchforward]", { expr = true, desc = "Prev search result" }) 125 | map("o", "N", "'nN'[v:searchforward]", { expr = true, desc = "Prev search result" }) 126 | 127 | -- Add undo break-points 128 | map("i", ",", ",u") 129 | map("i", ".", ".u") 130 | map("i", ";", ";u") 131 | 132 | -- Save file 133 | map("n", "", "w", { desc = "Save file" }) 134 | 135 | -- Better indenting 136 | map("v", "<", "", ">gv") 138 | 139 | -- Commenting 140 | map("n", "gco", "oVcxnormal gccfxa", { desc = "Add comment below" }) 141 | map("n", "gcO", "OVcxnormal gccfxa", { desc = "Add comment above" }) 142 | 143 | -- lazy 144 | map("n", "pl", "Lazy", { desc = "Lazy" }) 145 | 146 | -- New file 147 | map("n", "fn", "enew", { desc = "New file" }) 148 | 149 | -- Lists 150 | map("n", "xl", "lopen", { desc = "Location list" }) 151 | map("n", "xq", "copen", { desc = "Quickfix list" }) 152 | 153 | -- Quit 154 | map("n", "qq", "qa", { desc = "Quit all" }) 155 | 156 | -- Diagnostic 157 | local diagnostic_goto = function(count, severity) 158 | local opts = { count = count, severity = severity and vim.diagnostic.severity[severity] or nil } 159 | 160 | return function() 161 | vim.diagnostic.jump(opts) 162 | end 163 | end 164 | map("n", "cd", vim.diagnostic.open_float, { desc = "Line diagnostics" }) 165 | map("n", "]d", diagnostic_goto(1), { desc = "Next diagnostic" }) 166 | map("n", "[d", diagnostic_goto(-1), { desc = "Prev diagnostic" }) 167 | map("n", "]e", diagnostic_goto(1, "ERROR"), { desc = "Next error" }) 168 | map("n", "[e", diagnostic_goto(-1, "ERROR"), { desc = "Prev error" }) 169 | map("n", "]w", diagnostic_goto(1, "WARN"), { desc = "Next warning" }) 170 | map("n", "[w", diagnostic_goto(-1, "WARN"), { desc = "Prev warning" }) 171 | 172 | -- Autocmds 173 | local autocmd = vim.api.nvim_create_autocmd 174 | local function augroup(name) 175 | return vim.api.nvim_create_augroup("MyConfig" .. name, { clear = true }) 176 | end 177 | 178 | -- Go to last location when opening a buffer 179 | autocmd("BufReadPost", { 180 | group = augroup("LastLocation"), 181 | callback = function(event) 182 | local exclude = { "gitcommit" } 183 | local buf = event.buf 184 | if vim.tbl_contains(exclude, vim.bo[buf].filetype) then 185 | return 186 | end 187 | local mark = vim.api.nvim_buf_get_mark(buf, '"') 188 | local lcount = vim.api.nvim_buf_line_count(buf) 189 | if mark[1] > 0 and mark[1] <= lcount then 190 | pcall(vim.api.nvim_win_set_cursor, 0, mark) 191 | end 192 | end, 193 | }) 194 | 195 | -- Close some filetypes with 196 | autocmd("FileType", { 197 | group = augroup("CloseWithQ"), 198 | pattern = { 199 | "checkhealth", 200 | "dap-float", 201 | "gitsigns-blame", 202 | "help", 203 | "qf", 204 | }, 205 | callback = function(event) 206 | vim.bo[event.buf].buflisted = false 207 | vim.schedule(function() 208 | map("n", "q", function() 209 | vim.cmd("close") 210 | pcall(vim.api.nvim_buf_delete, event.buf, { force = true }) 211 | end, { 212 | buffer = event.buf, 213 | silent = true, 214 | desc = "Quit buffer", 215 | }) 216 | end) 217 | end, 218 | }) 219 | 220 | -- Enable spellcheck and line wrapping in select file types 221 | autocmd("FileType", { 222 | group = augroup("SpellWrap"), 223 | pattern = { "text", "gitcommit", "markdown" }, 224 | callback = function() 225 | vim.opt_local.wrap = true 226 | vim.opt_local.spell = true 227 | end, 228 | }) 229 | 230 | -- Highlight when yanking (copying) text 231 | autocmd("TextYankPost", { 232 | desc = "Highlight when yanking (copying) text", 233 | group = augroup("HighlightYank"), 234 | callback = function() 235 | vim.highlight.on_yank() 236 | end, 237 | }) 238 | 239 | -- Install Lazy plugin manager 240 | local lazy_path = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" 241 | local lazy_is_installed = vim.uv.fs_stat(lazy_path) 242 | if not lazy_is_installed then 243 | local lazy_repo = "https://github.com/folke/lazy.nvim.git" 244 | local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazy_repo, lazy_path }) 245 | if vim.v.shell_error ~= 0 then 246 | vim.api.nvim_echo({ 247 | { "Failed to clone lazy.nvim:\n", "ErrorMsg" }, 248 | { out, "WarningMsg" }, 249 | { "\nPress any key to exit..." }, 250 | }, true, {}) 251 | vim.fn.getchar() 252 | os.exit(1) 253 | end 254 | end 255 | vim.opt.runtimepath:prepend(lazy_path) 256 | 257 | -- Load Lazy plugin manager 258 | require("lazy").setup({ 259 | ui = { backdrop = 25 }, 260 | spec = { { import = "plugins" } }, 261 | rocks = { enabled = false }, 262 | checker = { enabled = true }, 263 | }) 264 | -------------------------------------------------------------------------------- /lua/plugins/core.lua: -------------------------------------------------------------------------------- 1 | -- Dashboard 2 | local header = [[ 3 | █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀▀█ █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀▀▀█ 4 | █ │░███▀█▀▀▀▀▀▓████▓▄ ▀▀▀▀ │░████▓▄ │▓████▓▄ █ 5 | █ │▒███████ │▓███████ │▒███████ │▓███████ █ 6 | █ │▓███████ │▓███████ │▓███████ │▓███████ █ 7 | ▀ │▓███████ │▓███████ │▓███████ │▓███████ █ 8 | ▀ │▓███████ │▓███████▄ ▄ │▓███████ │▓███████ █ 9 | █ │▓███████ │▓███████ ▓███████ █▄▄▄ 10 | █ │▓███████▀▀ ▀ ▀ │▓███████▀▀▀▓█▄█████▄ ▄ █ 11 | █▄▄▄▄▄▄▄▄ ▀ █▀▀▀▀▀▀▀▀▀▀▀▀█▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄█ 12 | █ ▀ █ 13 | ▀▀▀▀▀ ]] 14 | local footer = [[ 15 | ▀ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀ 16 | n e o v i m ]] 17 | 18 | -- Plugins 19 | return { 20 | -- Colorscheme 21 | { 22 | "folke/tokyonight.nvim", 23 | lazy = false, 24 | priority = 1000, 25 | ---@module "tokyonight" 26 | ---@type tokyonight.Config 27 | opts = { 28 | style = "storm", 29 | }, 30 | config = function(opts) 31 | require("tokyonight").setup(opts) 32 | vim.cmd([[colorscheme tokyonight-storm]]) 33 | vim.api.nvim_set_hl(0, "Statusline", { bg = "NONE" }) 34 | vim.api.nvim_set_hl(0, "StatuslineNC", { bg = "NONE" }) 35 | end, 36 | }, 37 | 38 | -- Icon provider 39 | { 40 | "echasnovski/mini.icons", 41 | lazy = true, 42 | opts = {}, 43 | init = function() 44 | package.preload["nvim-web-devicons"] = function() 45 | require("mini.icons").mock_nvim_web_devicons() 46 | return package.loaded["nvim-web-devicons"] 47 | end 48 | end, 49 | }, 50 | 51 | -- Copilot 52 | { 53 | "github/copilot.vim", 54 | enabled = false, 55 | event = { "BufReadPost", "BufNewFile", "BufWritePre" }, 56 | }, 57 | 58 | -- File explorer 59 | { 60 | "stevearc/oil.nvim", 61 | cmd = "Oil", 62 | keys = { 63 | --stylua: ignore start 64 | { "o", "Oil", desc = "Oil" }, 65 | { "O", function() require("oil").open(vim.uv.cwd()) end, desc = "Oil (cwd)" }, 66 | --stylua: ignore end 67 | }, 68 | ---@module "oil.nvim" 69 | ---@type oil.setupOpts 70 | opts = { 71 | default_file_explorer = false, 72 | keymaps = { 73 | [""] = false, -- replace with C-v 74 | [""] = { "actions.select", opts = { vertical = true } }, 75 | }, 76 | float = { border = "single" }, 77 | confirmation = { border = "single" }, 78 | progress = { border = "single" }, 79 | ssh = { border = "single" }, 80 | keymaps_help = { border = "single" }, 81 | }, 82 | }, 83 | 84 | -- Statusline 85 | { 86 | "nvim-lualine/lualine.nvim", 87 | event = { "BufReadPost", "BufNewFile", "BufWritePre" }, 88 | init = function() 89 | vim.g.lualine_laststatus = vim.o.laststatus 90 | if vim.fn.argc(-1) > 0 then 91 | -- set an empty statusline till lualine loads 92 | vim.o.statusline = " " 93 | else 94 | -- hide the statusline on the starter page 95 | vim.o.laststatus = 0 96 | end 97 | end, 98 | opts = function() 99 | -- PERF: we don't need this lualine require madness 100 | local lualine_require = require("lualine_require") 101 | lualine_require.require = require 102 | 103 | vim.o.laststatus = vim.g.lualine_laststatus 104 | local opts = { 105 | options = { 106 | component_separators = "", 107 | globalstatus = vim.o.laststatus == 3, 108 | section_separators = "", 109 | disabled_filetypes = { 110 | statusline = { "snacks_dashboard" }, 111 | }, 112 | ignore_focus = { 113 | "dap-repl", 114 | "dapui_breakpoints", 115 | "dapui_console", 116 | "dapui_scopes", 117 | "dapui_stacks", 118 | "dapui_watches", 119 | }, 120 | theme = { 121 | normal = { 122 | a = "Identifier", 123 | b = "LineNr", 124 | c = "LineNr", 125 | x = "LineNr", 126 | y = "LineNr", 127 | z = "LineNr", 128 | }, 129 | insert = { 130 | a = "GitSignsChange", 131 | b = "LineNr", 132 | c = "LineNr", 133 | x = "LineNr", 134 | y = "LineNr", 135 | z = "LineNr", 136 | }, 137 | visual = { 138 | a = "GitSignsAdd", 139 | b = "LineNr", 140 | c = "LineNr", 141 | x = "LineNr", 142 | y = "LineNr", 143 | z = "LineNr", 144 | }, 145 | replace = { 146 | a = "GitSignsDelete", 147 | b = "LineNr", 148 | c = "LineNr", 149 | x = "LineNr", 150 | y = "LineNr", 151 | z = "LineNr", 152 | }, 153 | inactive = { 154 | a = "LineNr", 155 | b = "LineNr", 156 | c = "LineNr", 157 | x = "LineNr", 158 | y = "LineNr", 159 | z = "LineNr", 160 | }, 161 | }, 162 | }, 163 | extensions = { "lazy", "mason", "nvim-dap-ui", "oil", "trouble", "quickfix" }, 164 | sections = { 165 | lualine_a = { 166 | { 167 | require("lazy.status").updates, 168 | fmt = function() 169 | return require("lazy.status").updates() or " " 170 | end, 171 | padding = 0, 172 | }, 173 | }, 174 | lualine_b = { 175 | { "filetype", icon_only = true, padding = { left = 2 } }, 176 | { 177 | "filename", 178 | symbols = { 179 | modified = "󰯹 ", 180 | readonly = "󰰠 ", 181 | unnamed = "󰰩 ", 182 | newfile = "󰰔 ", 183 | }, 184 | padding = { left = 1 }, 185 | }, 186 | }, 187 | lualine_c = { 188 | { "branch", icon = " ", padding = { left = 2 } }, 189 | { 190 | "diff", 191 | diff_color = { 192 | added = "GitSignsAdd", 193 | modified = "GitSignsChange", 194 | removed = "GitSignsDelete", 195 | }, 196 | symbols = { added = "󰯭 ", modified = "󰯳 ", removed = "󰯶 " }, 197 | source = function() 198 | local gitsigns = vim.b.gitsigns_status_dict 199 | if gitsigns then 200 | return { 201 | added = gitsigns.added, 202 | modified = gitsigns.changed, 203 | removed = gitsigns.removed, 204 | } 205 | end 206 | end, 207 | padding = { left = 1 }, 208 | }, 209 | }, 210 | lualine_x = { { "lsp_status", icon = "", symbols = { done = "●" }, padding = { right = 2 } } }, 211 | lualine_y = { 212 | { 213 | "macro", 214 | fmt = function() 215 | local reg = vim.fn.reg_recording() 216 | if reg == "" then 217 | return "" 218 | end 219 | return "󰑋 " .. reg 220 | end, 221 | padding = { right = 2 }, 222 | }, 223 | { "selectioncount", padding = { right = 2 } }, 224 | { "searchcount", padding = { right = 2 } }, 225 | }, 226 | lualine_z = { 227 | { "location", padding = { right = 1 } }, 228 | { "progress", padding = 0 }, 229 | }, 230 | }, 231 | inactive_sections = { 232 | lualine_a = { 233 | { "filetype", icon_only = true, padding = 0 }, 234 | { "filename", padding = 0 }, 235 | }, 236 | lualine_b = {}, 237 | lualine_c = {}, 238 | lualine_x = {}, 239 | lualine_y = {}, 240 | lualine_z = {}, 241 | }, 242 | } 243 | return opts 244 | end, 245 | }, 246 | 247 | -- Session managment 248 | { 249 | "folke/persistence.nvim", 250 | event = "BufReadPre", 251 | init = function() 252 | vim.opt.sessionoptions = 253 | { "buffers", "curdir", "tabpages", "winsize", "help", "globals", "skiprtp", "folds" } 254 | end, 255 | keys = { 256 | { "qs", "lua require('persistence').load()", desc = "Restore session" }, 257 | { "qS", "lua require('persistence').select()", desc = "Select session" }, 258 | { "ql", "lua require('persistence').load({last=true})", desc = "Restore last session" }, 259 | { "qd", "lua require('persistence').stop()", desc = "Don't save current session" }, 260 | }, 261 | ---@module "persistence" 262 | ---@type Persistence.Config 263 | ---@diagnostic disable: missing-fields 264 | opts = {}, 265 | }, 266 | 267 | -- Collection of small QoL plugins 268 | { 269 | "folke/snacks.nvim", 270 | priority = 1000, 271 | lazy = false, 272 | init = function() 273 | vim.api.nvim_create_autocmd("User", { 274 | pattern = "VeryLazy", 275 | callback = function() 276 | -- Create some toggle mappings 277 | Snacks.toggle.animate():map("ua") 278 | Snacks.toggle.line_number():map("ul") 279 | Snacks.toggle.option("relativenumber", { name = "relative number" }):map("uL") 280 | Snacks.toggle.option("spell", { name = "spelling" }):map("us") 281 | Snacks.toggle.scroll():map("uS") 282 | Snacks.toggle.option("wrap", { name = "wrap" }):map("uw") 283 | end, 284 | }) 285 | end, 286 | ---@module "snacks" 287 | ---@type snacks.Config 288 | opts = { 289 | dashboard = { 290 | width = 46, 291 | preset = { 292 | header = header, 293 | keys = { 294 | -- stylua: ignore start 295 | { icon = " ", key = "f", desc = "Find File", action = ":Telescope find_files" }, 296 | { icon = " ", key = "n", desc = "New File", action = ":enew | startinsert" }, 297 | { icon = " ", key = "g", desc = "Find Text", action = ":Telescope live_grep" }, 298 | { icon = " ", key = "r", desc = "Recent Files", action = ":Telescope oldfiles" }, 299 | { icon = " ", key = "c", desc = "Config", action = ":Telescope find_files " .. "cwd=" .. vim.fn.stdpath("config")}, 300 | { icon = " ", key = "s", desc = "Restore Session", section = "session" }, 301 | { icon = "󰒲 ", key = "l", desc = "Lazy", action = ":Lazy" }, 302 | { icon = "󱌢 ", key = "m", desc = "Mason", action = ":Mason" }, 303 | { icon = " ", key = "q", desc = "Quit", action = ":qa" }, 304 | -- stylua: ignore end 305 | }, 306 | }, 307 | sections = { 308 | { section = "header", gap = 0, padding = 0 }, 309 | { section = "keys", gap = 1, padding = 1 }, 310 | { 311 | text = { 312 | { 313 | footer, 314 | hl = "SnacksDashboardHeader", 315 | }, 316 | }, 317 | gap = 0, 318 | padding = 1, 319 | }, 320 | { section = "startup", gap = 0, padding = 0 }, 321 | }, 322 | }, 323 | styles = { 324 | notification = { border = "single", wo = { winblend = 0 } }, 325 | notification_history = { backdrop = 25, border = "single" }, 326 | input = { backdrop = 25, border = "single" }, 327 | }, 328 | input = { enabled = true }, 329 | notifier = { 330 | enabled = true, 331 | icons = { 332 | error = "󰯸 ", 333 | warn = "󰰮 ", 334 | info = "󰰄 ", 335 | debug = "󰯵 ", 336 | trace = "󰰥 ", 337 | }, 338 | }, 339 | scroll = { enabled = true }, 340 | statuscolumn = { enabled = true }, 341 | bigfile = { enabled = true }, 342 | }, 343 | keys = { 344 | { "bd", "lua Snacks.bufdelete()", desc = "Delete buffer" }, 345 | { "n", "lua Snacks.notifier.show_history()", desc = "Notification history" }, 346 | { "un", "lua Snacks.notifier.hide()", desc = "Dismiss all notifications" }, 347 | }, 348 | }, 349 | 350 | -- Automatic handling of closing to avoid leaving insert mode 351 | { 352 | "echasnovski/mini.pairs", 353 | event = "InsertEnter", 354 | opts = { 355 | mappings = { 356 | ["<"] = { action = "open", pair = "<>", neigh_pattern = "[%a:]." }, 357 | [">"] = { action = "close", pair = "<>", neigh_pattern = "[^\\]." }, 358 | ["'"] = { action = "closeopen", pair = "''", neigh_pattern = "[^<&]." }, 359 | }, 360 | }, 361 | }, 362 | 363 | -- Key binding help 364 | { 365 | "folke/which-key.nvim", 366 | event = "VeryLazy", 367 | ---@module "which-key" 368 | ---@type wk.Config 369 | ---@diagnostic disable: missing-fields 370 | opts = { 371 | icons = { separator = ":" }, 372 | spec = { 373 | mode = { "n", "v" }, 374 | { "c", group = "Code" }, 375 | { "d", group = "Debug" }, 376 | { "f", group = "File/find" }, 377 | { "g", group = "Git" }, 378 | { "gh", group = "Hunks" }, 379 | { "m", group = "Markdown" }, 380 | { "p", group = "Plugins" }, 381 | { "q", group = "Quit/session" }, 382 | { "s", group = "Search" }, 383 | { "u", group = "Ui" }, 384 | { "x", group = "Diagnostics/quickfix" }, 385 | { "z", group = "Folds/jumps/spelling" }, 386 | { "g", group = "Goto/actions" }, 387 | { "[", group = "Prev" }, 388 | { "]", group = "Next" }, 389 | { 390 | "b", 391 | group = "Buffer", 392 | expand = function() 393 | return require("which-key.extras").expand.buf() 394 | end, 395 | }, 396 | { 397 | "w", 398 | group = "Windows", 399 | proxy = "", 400 | expand = function() 401 | return require("which-key.extras").expand.win() 402 | end, 403 | }, 404 | -- better descriptions 405 | { "gx", desc = "Open with system app" }, 406 | }, 407 | }, 408 | -- stylua: ignore 409 | keys = { 410 | { "?", "lua require('which-key').show({ global = false })", desc = "Buffer keymaps (which-key)" }, 411 | }, 412 | }, 413 | 414 | -- Git integration 415 | { 416 | "lewis6991/gitsigns.nvim", 417 | event = { "BufReadPost", "BufNewFile", "BufWritePre" }, 418 | ---@module "gitsigns" 419 | ---@type Gitsigns.Config 420 | opts = { 421 | signs = { 422 | add = { text = "▎" }, 423 | change = { text = "▎" }, 424 | delete = { text = "" }, 425 | topdelete = { text = "" }, 426 | changedelete = { text = "▎" }, 427 | untracked = { text = "▎" }, 428 | }, 429 | signs_staged = { 430 | add = { text = "▎" }, 431 | change = { text = "▎" }, 432 | delete = { text = "" }, 433 | topdelete = { text = "" }, 434 | changedelete = { text = "▎" }, 435 | }, 436 | on_attach = function(bufnr) 437 | local gs = package.loaded.gitsigns 438 | 439 | local function map(mode, l, r, desc) 440 | vim.keymap.set(mode, l, r, { buffer = bufnr, desc = desc }) 441 | end 442 | 443 | -- stylua: ignore start 444 | map("n", "]h", function() if vim.wo.diff then vim.cmd.normal({ "]c", bang = true }) else gs.nav_hunk("next") end end, "Next hunk") 445 | map("n", "[h", function() if vim.wo.diff then vim.cmd.normal({ "[c", bang = true }) else gs.nav_hunk("prev") end end, "Prev hunk") 446 | map("n", "]H", function() gs.nav_hunk("last") end, "Last hunk") 447 | map("n", "[H", function() gs.nav_hunk("first") end, "First hunk") 448 | map({ "n", "v" }, "ghs", ":Gitsigns stage_hunk", "Stage hunk") 449 | map({ "n", "v" }, "ghr", ":Gitsigns reset_hunk", "Reset hunk") 450 | map("n", "ghS", gs.stage_buffer, "Stage buffer") 451 | map("n", "ghu", gs.undo_stage_hunk, "Undo stage hunk") 452 | map("n", "ghR", gs.reset_buffer, "Reset buffer") 453 | map("n", "ghp", gs.preview_hunk_inline, "Preview hunk inline") 454 | map("n", "ghb", function() gs.blame_line({ full = true }) end, "Blame line") 455 | map("n", "ghB", function() gs.blame() end, "Blame buffer") 456 | map("n", "ghd", gs.diffthis, "Diff this") 457 | map("n", "ghD", function() gs.diffthis("~") end, "Diff this ~") 458 | map({ "o", "x" }, "ih", ":Gitsigns select_hunk", "GitSigns select hunk") 459 | map("n", "gc", "Telescope git_commits", "Commits") 460 | map("n", "gs", "Telescope git_status", "Status") 461 | -- stylua: ignore end 462 | end, 463 | }, 464 | }, 465 | 466 | -- Finder and previewer 467 | { 468 | "nvim-telescope/telescope.nvim", 469 | cmd = "Telescope", 470 | dependencies = { 471 | "nvim-lua/plenary.nvim", 472 | { 473 | "nvim-telescope/telescope-fzf-native.nvim", 474 | build = "cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release && cmake --build build --config Release && cmake --install build --prefix build", 475 | }, 476 | }, 477 | --stylua: ignore 478 | keys = { 479 | -- find 480 | { "fb", "Telescope buffers sort_mru=true sort_lastused=true ignore_current_buffer=true", desc = "Buffers" }, 481 | { "fc", "Telescope find_files cwd=" .. vim.fn.stdpath("config") .. "", desc = "Config file" }, 482 | { "ff", "Telescope find_files", desc = "Files" }, 483 | { "fF", "Telescope find_files hidden=true", desc = "Files (hidden)" }, 484 | { "fg", "Telescope git_files", desc = "Files (git)" }, 485 | { "fr", "Telescope oldfiles", desc = "Recent" }, 486 | -- search 487 | { "sa", "Telescope autocommands", desc = "Auto commands" }, 488 | { "sb", "Telescope current_buffer_fuzzy_find", desc = "Buffer" }, 489 | { "sc", "Telescope command_history", desc = "Command history" }, 490 | { "sC", "Telescope commands", desc = "Commands" }, 491 | { "sd", "Telescope diagnostics bufnr=0", desc = "Document diagnostics" }, 492 | { "sD", "Telescope diagnostics", desc = "Workspace diagnostics" }, 493 | { "sg", "Telescope live_grep", desc = "Grep (cwd)" }, 494 | { "sG", desc = "Glob grep (cwd)" }, 495 | { "sh", "Telescope help_tags", desc = "Help pages" }, 496 | { "sH", "Telescope highlights", desc = "highlight groups" }, 497 | { "sj", "Telescope jumplist", desc = "Jumplist" }, 498 | { "sk", "Telescope keymaps", desc = "Keymaps" }, 499 | { "sl", "Telescope loclist", desc = "Location list" }, 500 | { "sm", "Telescope marks", desc = "Marks" }, 501 | { "so", "Telescope vim_options", desc = "Options" }, 502 | { "sq", "Telescope quickfix", desc = "Quickfix list" }, 503 | { "sR", "Telescope resume", desc = "Resume" }, 504 | { "sw", "Telescope grep_string word_match=-w", desc = "Word" }, 505 | { "sw", "Telescope grep_string word_match=-w", desc = "Selection", mode = "v" }, 506 | }, 507 | opts = function() 508 | local actions = require("telescope.actions") 509 | local open_with_trouble = function(...) 510 | return require("trouble.sources.telescope").open(...) 511 | end 512 | return { 513 | defaults = { 514 | prompt_prefix = " ", 515 | selection_caret = " ", 516 | multi_icon = " ", 517 | -- use square corners 518 | borderchars = { 519 | " ", 520 | " ", 521 | " ", 522 | " ", 523 | " ", 524 | " ", 525 | " ", 526 | " ", 527 | }, 528 | mappings = { 529 | i = { 530 | [""] = actions.preview_scrolling_down, 531 | [""] = actions.preview_scrolling_up, 532 | [""] = "select_horizontal", 533 | [""] = open_with_trouble, 534 | }, 535 | n = { 536 | ["q"] = actions.close, 537 | }, 538 | }, 539 | }, 540 | extensions = { fzf = {} }, 541 | } 542 | end, 543 | config = function(_, opts) 544 | -- configure backdrop dimming 545 | vim.api.nvim_create_autocmd("FileType", { 546 | pattern = "TelescopePrompt", 547 | callback = function(ctx) 548 | local backdrop = 25 549 | local backdropName = "TelescopeBackdrop" 550 | local telescopeBufnr = ctx.buf 551 | 552 | -- `Telescope` does not set a zindex, so it uses the default value 553 | -- of `nvim_open_win`, which is 50: https://neovim.io/doc/user/api.html#nvim_open_win() 554 | local telescopeZindex = 50 555 | 556 | local backdropBufnr = vim.api.nvim_create_buf(false, true) 557 | local winnr = vim.api.nvim_open_win(backdropBufnr, false, { 558 | relative = "editor", 559 | border = "none", 560 | row = 0, 561 | col = 0, 562 | width = vim.o.columns, 563 | height = vim.o.lines, 564 | focusable = false, 565 | style = "minimal", 566 | zindex = telescopeZindex - 1, -- ensure it's below the reference window 567 | }) 568 | 569 | vim.api.nvim_set_hl(0, backdropName, { bg = "#000000", default = true }) 570 | vim.wo[winnr].winhighlight = "Normal:" .. backdropName 571 | vim.wo[winnr].winblend = backdrop 572 | vim.bo[backdropBufnr].buftype = "nofile" 573 | 574 | -- close backdrop when the reference buffer is closed 575 | vim.api.nvim_create_autocmd({ "WinClosed", "BufLeave" }, { 576 | once = true, 577 | buffer = telescopeBufnr, 578 | callback = function() 579 | if vim.api.nvim_win_is_valid(winnr) then 580 | vim.api.nvim_win_close(winnr, true) 581 | end 582 | if vim.api.nvim_buf_is_valid(backdropBufnr) then 583 | vim.api.nvim_buf_delete(backdropBufnr, { force = true }) 584 | end 585 | end, 586 | }) 587 | end, 588 | }) 589 | 590 | require("telescope").setup(opts) 591 | require("telescope").load_extension("fzf") 592 | 593 | local live_grep_globbing = function(options) 594 | local pickers = require("telescope.pickers") 595 | local finders = require("telescope.finders") 596 | local make_entry = require("telescope.make_entry") 597 | local conf = require("telescope.config").values 598 | 599 | options = options or {} 600 | options.cwd = options.cwd or vim.uv.cwd() 601 | 602 | local finder = finders.new_async_job({ 603 | command_generator = function(prompt) 604 | if not prompt or prompt == "" then 605 | return nil 606 | end 607 | 608 | local pieces = vim.split(prompt, " ") 609 | local args = { "rg" } 610 | if pieces[1] then 611 | table.insert(args, "-e") 612 | table.insert(args, pieces[1]) 613 | end 614 | 615 | if pieces[2] then 616 | table.insert(args, "-g") 617 | table.insert(args, pieces[2]) 618 | end 619 | 620 | return vim.iter({ 621 | args, 622 | { 623 | "--color=never", 624 | "--no-heading", 625 | "--with-filename", 626 | "--line-number", 627 | "--column", 628 | "--smart-case", 629 | }, 630 | }) 631 | :flatten() 632 | :totable() 633 | end, 634 | entry_maker = make_entry.gen_from_vimgrep(options), 635 | cwd = options.cwd, 636 | }) 637 | pickers 638 | .new(options, { 639 | debounce = 100, 640 | prompt_title = "multigrep", 641 | finder = finder, 642 | previewer = conf.grep_previewer(options), 643 | sorter = require("telescope.sorters").empty(), 644 | }) 645 | :find() 646 | end 647 | 648 | vim.keymap.set("n", "sG", live_grep_globbing, { desc = "Glob grep (cwd)" }) 649 | end, 650 | }, 651 | 652 | -- Better diagnostics list and others 653 | { 654 | "folke/trouble.nvim", 655 | cmd = { "Trouble" }, 656 | 657 | ---@module "trouble" 658 | ---@type trouble.Config 659 | opts = {}, 660 | keys = { 661 | { "xx", "Trouble diagnostics toggle", desc = "Diagnostics (trouble)" }, 662 | { "xX", "Trouble diagnostics toggle filter.buf=0", desc = "Buffer diagnostics (trouble)" }, 663 | { "cs", "Trouble symbols toggle", desc = "Symbols (trouble)" }, 664 | { "cS", "Trouble lsp toggle", desc = "LSP references/definitions/... (trouble)" }, 665 | { "xL", "Trouble loclist toggle", desc = "Location list (trouble)" }, 666 | { "xQ", "Trouble qflist toggle", desc = "Quickfix list (trouble)" }, 667 | { 668 | "[q", 669 | function() 670 | if require("trouble").is_open() then 671 | ---@diagnostic disable-next-line: missing-fields 672 | require("trouble").prev({ skip_groups = true, jump = true }, {}) 673 | else 674 | local ok, err = pcall(vim.cmd.cprev) 675 | if not ok then 676 | vim.notify(err, vim.log.levels.ERROR) 677 | end 678 | end 679 | end, 680 | desc = "Previous Trouble/Quickfix item", 681 | }, 682 | { 683 | "]q", 684 | function() 685 | if require("trouble").is_open() then 686 | ---@diagnostic disable-next-line: missing-fields 687 | require("trouble").next({ skip_groups = true, jump = true }, {}) 688 | else 689 | local ok, err = pcall(vim.cmd.cnext) 690 | if not ok then 691 | vim.notify(err, vim.log.levels.ERROR) 692 | end 693 | end 694 | end, 695 | desc = "Next Trouble/Quickfix item", 696 | }, 697 | }, 698 | }, 699 | 700 | -- Syntax highlighting and code parsing 701 | { 702 | "nvim-treesitter/nvim-treesitter", 703 | branch = "main", 704 | build = ":TSUpdate", 705 | cmd = { "TSUpdateSync", "TSUpdate", "TSInstall" }, 706 | event = { "BufReadPost", "BufNewFile", "BufWritePre" }, 707 | -- specified as a dependency to be loaded when using telescope from the 708 | -- snacks dashboard. 709 | dependencies = "nvim-telescope/telescope.nvim", 710 | config = function() 711 | require("nvim-treesitter").install({ 712 | "c", 713 | "cpp", 714 | "cmake", 715 | "git_config", 716 | "git_rebase", 717 | "gitattributes", 718 | "gitcommit", 719 | "gitignore", 720 | "html", 721 | "json", 722 | "lua", 723 | "markdown", 724 | "markdown_inline", 725 | "regex", 726 | "ron", 727 | "rust", 728 | "toml", 729 | "vim", 730 | "vimdoc", 731 | "yaml", 732 | }) 733 | end, 734 | }, 735 | 736 | -- Advanced text selection 737 | { 738 | "nvim-treesitter/nvim-treesitter-textobjects", 739 | branch = "main", 740 | keys = { 741 | { 742 | "af", 743 | function() 744 | require("nvim-treesitter-textobjects.select").select_textobject("@function.outer", "textobjects") 745 | end, 746 | desc = "Select outer function", 747 | mode = { "x", "o" }, 748 | }, 749 | { 750 | "if", 751 | function() 752 | require("nvim-treesitter-textobjects.select").select_textobject("@function.inner", "textobjects") 753 | end, 754 | desc = "Select inner function", 755 | mode = { "x", "o" }, 756 | }, 757 | { 758 | "ac", 759 | function() 760 | require("nvim-treesitter-textobjects.select").select_textobject("@class.outer", "textobjects") 761 | end, 762 | desc = "Select outer class", 763 | mode = { "x", "o" }, 764 | }, 765 | { 766 | "ic", 767 | function() 768 | require("nvim-treesitter-textobjects.select").select_textobject("@class.inner", "textobjects") 769 | end, 770 | desc = "Select inner class", 771 | mode = { "x", "o" }, 772 | }, 773 | { 774 | "as", 775 | function() 776 | require("nvim-treesitter-textobjects.select").select_textobject("@local.scope", "locals") 777 | end, 778 | desc = "Select local scope", 779 | mode = { "x", "o" }, 780 | }, 781 | }, 782 | ---@module "nvim-treesitter-textobjects" 783 | opts = {}, 784 | }, 785 | 786 | -- LSP for Cargo.toml 787 | { 788 | "Saecki/crates.nvim", 789 | event = "BufRead Cargo.toml", 790 | opts = { 791 | completion = { 792 | crates = { 793 | enabled = true, 794 | }, 795 | }, 796 | lsp = { 797 | enabled = true, 798 | actions = true, 799 | completion = true, 800 | hover = true, 801 | }, 802 | }, 803 | }, 804 | 805 | -- Rust 806 | { 807 | "mrcjkb/rustaceanvim", 808 | ft = "rust", 809 | ---@module "rustaceanvim" 810 | ---@type rustaceanvim.Config 811 | opts = { 812 | server = { 813 | -- stylua: ignore 814 | on_attach = function(client, bufnr) 815 | -- vim.notify(vim.inspect(client)) 816 | 817 | -- stylua: ignore start 818 | local map = vim.keymap.set 819 | map("n", "dn", "RustLsp debug", { desc = "Debug nearest", buffer = bufnr }) 820 | map("n", "dR", "RustLsp debuggables", { desc = "Rust debuggables ", buffer = bufnr }) 821 | 822 | map("n", "gd", function() require("telescope.builtin").lsp_definitions({ reuse_win = true }) end, { buffer = bufnr, desc = "Go to definition" }) 823 | map("n", "gy", function() require("telescope.builtin").lsp_type_definitions({ reuse_win = true }) end, { buffer = bufnr, desc = "Go to type definition" }) 824 | map("n", "gC", "RustLsp openCargo", { buffer = bufnr, desc = "Open Cargo.toml" }) 825 | map("n", "gD", vim.lsp.buf.declaration, { buffer = bufnr, desc = "Go to declaration" }) 826 | map("n", "gI", function() require("telescope.builtin").lsp_implementations({ reuse_win = true }) end, { buffer = bufnr, desc = "Go to implementation" }) 827 | map("n", "gK", function() return vim.lsp.buf.signature_help() end, { buffer = bufnr, desc = "Signature help" }) 828 | 829 | map("n", "K", "RustLsp hover actions", { buffer = bufnr, desc = "Hover" }) 830 | 831 | map({ "n", "v" }, "ca", "RustLsp codeAction", { buffer = bufnr, desc = "Code action" }) 832 | map("n", "ce", "RustLsp explainError current", { buffer = bufnr, desc = "Explain error" }) 833 | map("n", "cl", "LspInfo", { buffer = bufnr, desc = "LSP information" }) 834 | map("n", "cr", vim.lsp.buf.rename, { buffer = bufnr, desc = "Rename buffer" }) 835 | 836 | map("n", "cA", function() vim.lsp.buf.code_action({ context = { only = { "source" }, diagnostics = {} } }) end, { buffer = bufnr, desc = "Source action" }) 837 | map({ "n", "v" }, "J", "RustLsp joinLines", { buffer = bufnr, desc = "Join lines" }) 838 | map("n", "cR", function() Snacks.rename.rename_file() end, { buffer = bufnr, desc = "Rename file" }) 839 | 840 | map("n", "sr", "Telescope lsp_references", { buffer = bufnr, nowait = true, desc = "References" }) 841 | map("n", "ss", "Telescope lsp_document_symbols", { buffer = bufnr, desc = "Symbols" }) 842 | map("n", "sS", "Telescope lsp_dynamic_workspace_symbols", { buffer = bufnr, desc = "Symbols (workspace)" }) 843 | -- stylua: ignore end 844 | 845 | vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) 846 | Snacks.toggle.inlay_hints():map("uh") 847 | 848 | -- toggle virtual diagnostics 849 | map("n", "ud", function() 850 | if vim.diagnostic.config().virtual_text then 851 | vim.diagnostic.config({ virtual_lines = false, virtual_text = false }) 852 | else 853 | vim.diagnostic.config(vim.g.diagnostic_config) 854 | end 855 | end, { buffer = bufnr, desc = "Toggle virtual text" }) 856 | 857 | vim.o.foldmethod = "expr" 858 | if client:supports_method("textDocument/foldingRange") then 859 | local win = vim.api.nvim_get_current_win() 860 | vim.wo[win][0].foldexpr = "v:lua.vim.lsp.foldexpr()" 861 | end 862 | 863 | if client.server_capabilities.codeLensProvider then 864 | -- stylua: ignore start 865 | map("n", "cC", vim.lsp.codelens.refresh, { buffer = bufnr, desc = "Refresh and display codelens" }) 866 | map({ "n", "v" }, "cc", vim.lsp.codelens.run, { buffer = bufnr, desc = "Run codelens" }) 867 | -- stylua: ignore end 868 | 869 | vim.lsp.codelens.refresh() 870 | vim.api.nvim_create_autocmd( 871 | { "TextChanged", "InsertLeave", "CursorHold", "LspAttach", "BufEnter" }, 872 | { buffer = bufnr, callback = vim.lsp.codelens.refresh } 873 | ) 874 | end 875 | 876 | vim.b[bufnr].autoformat = true 877 | -- stylua: ignore 878 | map("n", "uf", function() vim.b[bufnr].autoformat = not vim.b[bufnr].autoformat end, { buffer = bufnr, desc = "Toggle autoformat (buffer)" }) 879 | end, 880 | default_settings = { 881 | -- rust-analyzer language server configuration 882 | ["rust-analyzer"] = { 883 | cargo = { 884 | allFeatures = true, 885 | loadOutDirsFromCheck = true, 886 | buildScripts = { 887 | enable = true, 888 | }, 889 | }, 890 | -- Add clippy lints for Rust 891 | checkOnSave = true, 892 | -- Enable diagnostics 893 | diagnostics = { 894 | enable = true, 895 | }, 896 | procMacro = { 897 | enable = true, 898 | ignored = { 899 | ["async-trait"] = { "async_trait" }, 900 | ["napi-derive"] = { "napi" }, 901 | ["async-recursion"] = { "async_recursion" }, 902 | }, 903 | }, 904 | files = { 905 | excludeDirs = { 906 | ".direnv", 907 | ".git", 908 | ".github", 909 | ".gitlab", 910 | "bin", 911 | "node_modules", 912 | "target", 913 | "venv", 914 | ".venv", 915 | }, 916 | }, 917 | }, 918 | }, 919 | }, 920 | }, 921 | config = function(_, opts) 922 | vim.g.rustaceanvim = vim.tbl_deep_extend("keep", vim.g.rustaceanvim or {}, opts or {}) 923 | end, 924 | }, 925 | 926 | -- Installer for LSP, formatting, and debugging 927 | { 928 | "mason-org/mason.nvim", 929 | build = ":MasonUpdate", 930 | cmd = "Mason", 931 | keys = { { "pm", "Mason", desc = "Mason" } }, 932 | init = function() 933 | vim.api.nvim_create_autocmd("FileType", { 934 | pattern = "mason", 935 | callback = function() 936 | vim.opt_local.cursorline = false 937 | end, 938 | }) 939 | end, 940 | ---@module "mason" 941 | ---@type MasonSettings 942 | opts = { 943 | ui = { 944 | backdrop = 25, 945 | keymaps = { apply_language_filter = "F" }, 946 | icons = { 947 | package_installed = "● ", 948 | package_pending = "󰦗 ", 949 | package_uninstalled = "○ ", 950 | }, 951 | }, 952 | -- Not an actual option! Manually install the packages in config. 953 | ensure_installed = { 954 | "clangd", 955 | "clang-format", 956 | "codelldb", 957 | "json-lsp", 958 | "lua-language-server", 959 | "marksman", 960 | "neocmakelsp", 961 | "stylua", 962 | "yamlfmt", 963 | "yaml-language-server", 964 | }, 965 | }, 966 | config = function(_, opts) 967 | require("mason").setup(opts) 968 | 969 | local mr = require("mason-registry") 970 | mr.refresh(function() 971 | for _, pkg in ipairs(opts.ensure_installed) do 972 | local p = mr.get_package(pkg) 973 | if not p:is_installed() then 974 | p:install() 975 | end 976 | end 977 | end) 978 | end, 979 | }, 980 | 981 | { 982 | "mason-org/mason-lspconfig.nvim", 983 | lazy = true, 984 | dependencies = "williamboman/mason.nvim", 985 | ---@module "mason-lspconfig" 986 | ---@type MasonLspconfigSettings 987 | opts = { 988 | automatic_enable = false, 989 | }, 990 | }, 991 | 992 | -- SchemaStore catalog for JSON and YAML. Explicitly enabled in LSP config. 993 | { 994 | "b0o/SchemaStore.nvim", 995 | ft = { "json", "yaml" }, 996 | }, 997 | 998 | -- LSP configuration 999 | { 1000 | "neovim/nvim-lspconfig", 1001 | event = { "BufReadPost", "BufNewFile", "BufWritePre" }, 1002 | dependencies = { "mason-org/mason-lspconfig.nvim", "saghen/blink.cmp" }, 1003 | init = function() 1004 | local icons = { 1005 | -- see vim.diagnostic.severity 1006 | [1] = "󰯹 ", -- error 1007 | [2] = "󰰯 ", -- warn 1008 | [3] = "󰰂 ", -- hint 1009 | [4] = "󰰅 ", -- info 1010 | } 1011 | 1012 | -- diagnostics 1013 | local diagnostics = { 1014 | underline = true, 1015 | update_in_insert = false, 1016 | virtual_lines = { current_line = true }, 1017 | virtual_text = { 1018 | current_line = true, 1019 | spacing = 2, 1020 | source = "if_many", 1021 | prefix = function(diagnostic) 1022 | return icons[diagnostic.severity] or "󰘥 " 1023 | end, 1024 | }, 1025 | severity_sort = true, 1026 | signs = { 1027 | text = { 1028 | [vim.diagnostic.severity.ERROR] = icons.ERROR, 1029 | [vim.diagnostic.severity.WARN] = icons.WARN, 1030 | [vim.diagnostic.severity.HINT] = icons.HINT, 1031 | [vim.diagnostic.severity.INFO] = icons.INFO, 1032 | }, 1033 | }, 1034 | } 1035 | vim.diagnostic.config(diagnostics) 1036 | vim.g.diagnostic_config = diagnostics 1037 | end, 1038 | config = function() 1039 | local lsp = require("lspconfig") 1040 | 1041 | lsp.yamlls.setup({ 1042 | settings = { 1043 | yaml = { 1044 | schemaStore = { 1045 | -- disable in favor of schemastore plugin 1046 | enable = false, 1047 | url = "", 1048 | }, 1049 | schemas = require("schemastore").yaml.schemas(), 1050 | }, 1051 | }, 1052 | capabilities = require("blink.cmp").get_lsp_capabilities({}, true), 1053 | }) 1054 | 1055 | lsp.jsonls.setup({ 1056 | settings = { 1057 | json = { 1058 | schemas = require("schemastore").json.schemas(), 1059 | validate = { enable = true }, 1060 | }, 1061 | }, 1062 | capabilities = require("blink.cmp").get_lsp_capabilities({}, true), 1063 | }) 1064 | 1065 | lsp.lua_ls.setup({ 1066 | settings = { 1067 | Lua = { 1068 | workspace = { 1069 | checkThirdParty = false, 1070 | maxPreload = 10000, -- performance 1071 | preloadFileSize = 1000, -- performance 1072 | }, 1073 | codeLens = { 1074 | enable = true, 1075 | }, 1076 | completion = { 1077 | callSnippet = "Replace", 1078 | }, 1079 | doc = { 1080 | privateName = { "^_" }, 1081 | }, 1082 | hint = { 1083 | enable = true, 1084 | setType = false, 1085 | paramType = true, 1086 | paramName = "Disable", 1087 | semicolon = "Disable", 1088 | arrayIndex = "Disable", 1089 | }, 1090 | }, 1091 | }, 1092 | capabilities = require("blink.cmp").get_lsp_capabilities( 1093 | { workspace = { fileOperations = { didRename = true, willRename = true } } }, 1094 | true 1095 | ), 1096 | on_attach = function(client, bufnr) 1097 | -- vim.notify(vim.inspect(client)) 1098 | 1099 | -- stylua: ignore start 1100 | local map = vim.keymap.set 1101 | map("n", "gd", function() require("telescope.builtin").lsp_definitions({ reuse_win = true }) end, { buffer = bufnr, desc = "Go to definition" }) 1102 | map("n", "gy", function() require("telescope.builtin").lsp_type_definitions({ reuse_win = true }) end, { buffer = bufnr, desc = "Go to type definition" }) 1103 | map("n", "gD", vim.lsp.buf.declaration, { buffer = bufnr, desc = "Go to declaration" }) 1104 | map("n", "gI", function() require("telescope.builtin").lsp_implementations({ reuse_win = true }) end, { buffer = bufnr, desc = "Go to implementation" }) 1105 | map("n", "gK", function() return vim.lsp.buf.signature_help() end, { buffer = bufnr, desc = "Signature help" }) 1106 | 1107 | map({ "n", "v" }, "ca", vim.lsp.buf.code_action, { buffer = bufnr, desc = "Code action" }) 1108 | map("n", "cl", "LspInfo", { buffer = bufnr, desc = "LSP information" }) 1109 | map("n", "cr", vim.lsp.buf.rename, { buffer = bufnr, desc = "Rename buffer" }) 1110 | 1111 | map("n", "cA", function() vim.lsp.buf.code_action({ context = { only = { "source" }, diagnostics = {} } }) end, { buffer = bufnr, desc = "Source action" }) 1112 | map("n", "cR", function() Snacks.rename.rename_file() end, { buffer = bufnr, desc = "Rename file" }) 1113 | 1114 | map("n", "sr", "Telescope lsp_references", { buffer = bufnr, nowait = true, desc = "References" }) 1115 | map("n", "ss", "Telescope lsp_document_symbols", { buffer = bufnr, desc = "Symbols" }) 1116 | map("n", "sS", "Telescope lsp_dynamic_workspace_symbols", { buffer = bufnr, desc = "Symbols (workspace)" }) 1117 | -- stylua: ignore end 1118 | 1119 | vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) 1120 | Snacks.toggle.inlay_hints():map("uh") 1121 | 1122 | -- toggle virtual diagnostics 1123 | map("n", "ud", function() 1124 | if vim.diagnostic.config().virtual_text then 1125 | vim.diagnostic.config({ virtual_lines = false, virtual_text = false }) 1126 | else 1127 | vim.diagnostic.config(vim.g.diagnostic_config) 1128 | end 1129 | end, { buffer = bufnr, desc = "Toggle virtual text" }) 1130 | 1131 | vim.o.foldmethod = "expr" 1132 | if client:supports_method("textDocument/foldingRange") then 1133 | vim.o.foldexpr = "v:lua.vim.lsp.foldexpr()" 1134 | else 1135 | vim.o.foldexpr = "v:lua.vim.treesitter.foldexpr()" 1136 | end 1137 | 1138 | if client.server_capabilities.codeLensProvider then 1139 | -- stylua: ignore start 1140 | map("n", "cC", vim.lsp.codelens.refresh, { buffer = bufnr, desc = "Refresh and display codelens" }) 1141 | map({ "n", "v" }, "cc", vim.lsp.codelens.run, { buffer = bufnr, desc = "Run codelens" }) 1142 | -- stylua: ignore end 1143 | 1144 | vim.lsp.codelens.refresh() 1145 | vim.api.nvim_create_autocmd( 1146 | { "TextChanged", "InsertLeave", "CursorHold", "LspAttach", "BufEnter" }, 1147 | { buffer = bufnr, callback = vim.lsp.codelens.refresh } 1148 | ) 1149 | end 1150 | 1151 | vim.b[bufnr].autoformat = true 1152 | -- stylua: ignore 1153 | map("n", "uf", function() vim.b[bufnr].autoformat = not vim.b[bufnr].autoformat end, { buffer = bufnr, desc = "Toggle autoformat (buffer)" }) 1154 | end, 1155 | }) 1156 | 1157 | lsp.neocmake.setup({ 1158 | init_options = { 1159 | format = { 1160 | enable = true, 1161 | }, 1162 | lint = { 1163 | enable = true, 1164 | }, 1165 | scan_cmake_in_package = true, 1166 | semantic_token = true, 1167 | }, 1168 | capabilities = require("blink.cmp").get_lsp_capabilities({ 1169 | workspace = { 1170 | didChangeWatchedFiles = { dynamicRegistration = true, relative_pattern_support = true }, 1171 | }, 1172 | textDocument = { 1173 | completion = { 1174 | completionItem = { 1175 | snippetSupport = true, 1176 | }, 1177 | }, 1178 | }, 1179 | }, true), 1180 | on_attach = function(client, bufnr) 1181 | -- vim.notify(vim.inspect(client)) 1182 | 1183 | -- stylua: ignore start 1184 | local map = vim.keymap.set 1185 | map("n", "gd", function() require("telescope.builtin").lsp_definitions({ reuse_win = true }) end, { buffer = bufnr, desc = "Go to definition" }) 1186 | map("n", "gy", function() require("telescope.builtin").lsp_type_definitions({ reuse_win = true }) end, { buffer = bufnr, desc = "Go to type definition" }) 1187 | map("n", "gD", vim.lsp.buf.declaration, { buffer = bufnr, desc = "Go to declaration" }) 1188 | map("n", "gI", function() require("telescope.builtin").lsp_implementations({ reuse_win = true }) end, { buffer = bufnr, desc = "Go to implementation" }) 1189 | map("n", "gK", function() return vim.lsp.buf.signature_help() end, { buffer = bufnr, desc = "Signature help" }) 1190 | 1191 | map({ "n", "v" }, "ca", vim.lsp.buf.code_action, { buffer = bufnr, desc = "Code action" }) 1192 | map("n", "cl", "LspInfo", { buffer = bufnr, desc = "LSP information" }) 1193 | map("n", "cr", vim.lsp.buf.rename, { buffer = bufnr, desc = "Rename buffer" }) 1194 | 1195 | map("n", "cA", function() vim.lsp.buf.code_action({ context = { only = { "source" }, diagnostics = {} } }) end, { buffer = bufnr, desc = "Source action" }) 1196 | map("n", "cR", function() Snacks.rename.rename_file() end, { buffer = bufnr, desc = "Rename file" }) 1197 | 1198 | map("n", "sr", "Telescope lsp_references", { buffer = bufnr, nowait = true, desc = "References" }) 1199 | map("n", "ss", "Telescope lsp_document_symbols", { buffer = bufnr, desc = "Symbols" }) 1200 | map("n", "sS", "Telescope lsp_dynamic_workspace_symbols", { buffer = bufnr, desc = "Symbols (workspace)" }) 1201 | -- stylua: ignore end 1202 | 1203 | vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) 1204 | Snacks.toggle.inlay_hints():map("uh") 1205 | 1206 | -- toggle virtual diagnostics 1207 | map("n", "ud", function() 1208 | if vim.diagnostic.config().virtual_text then 1209 | vim.diagnostic.config({ virtual_lines = false, virtual_text = false }) 1210 | else 1211 | vim.diagnostic.config(vim.g.diagnostic_config) 1212 | end 1213 | end, { buffer = bufnr, desc = "Toggle virtual text" }) 1214 | 1215 | vim.o.foldmethod = "expr" 1216 | if client:supports_method("textDocument/foldingRange") then 1217 | vim.o.foldexpr = "v:lua.vim.lsp.foldexpr()" 1218 | else 1219 | vim.o.foldexpr = "v:lua.vim.treesitter.foldexpr()" 1220 | end 1221 | 1222 | if client.server_capabilities.codeLensProvider then 1223 | -- stylua: ignore start 1224 | map("n", "cC", vim.lsp.codelens.refresh, { buffer = bufnr, desc = "Refresh and display codelens" }) 1225 | map({ "n", "v" }, "cc", vim.lsp.codelens.run, { buffer = bufnr, desc = "Run codelens" }) 1226 | -- stylua: ignore end 1227 | 1228 | vim.lsp.codelens.refresh() 1229 | vim.api.nvim_create_autocmd( 1230 | { "TextChanged", "InsertLeave", "CursorHold", "LspAttach", "BufEnter" }, 1231 | { buffer = bufnr, callback = vim.lsp.codelens.refresh } 1232 | ) 1233 | end 1234 | 1235 | vim.b[bufnr].autoformat = true 1236 | -- stylua: ignore 1237 | map("n", "uf", function() vim.b[bufnr].autoformat = not vim.b[bufnr].autoformat end, { buffer = bufnr, desc = "Toggle autoformat (buffer)" }) 1238 | end, 1239 | }) 1240 | 1241 | lsp.clangd.setup({ 1242 | capabilities = require("blink.cmp").get_lsp_capabilities({}, true), 1243 | cmd = { 1244 | "clangd", 1245 | "--background-index", 1246 | "--clang-tidy", 1247 | "--completion-style=detailed", 1248 | "--header-insertion=iwyu", 1249 | }, 1250 | on_attach = function(client, bufnr) 1251 | -- vim.notify(vim.inspect(client)) 1252 | 1253 | -- stylua: ignore start 1254 | local map = vim.keymap.set 1255 | map("n", "gd", vim.lsp.buf.definition, { buffer = bufnr, desc = "Go to definition" }) 1256 | map("n", "gr", vim.lsp.buf.references, { buffer = bufnr, nowait = true, desc = "References" }) 1257 | map("n", "gy", vim.lsp.buf.type_definition, { buffer = bufnr, desc = "Go to type definition" }) 1258 | map("n", "gD", vim.lsp.buf.declaration, { buffer = bufnr, desc = "Go to declaration" }) 1259 | map("n", "gI", vim.lsp.buf.implementation, { buffer = bufnr, desc = "Go to implementation" }) 1260 | map("n", "gK", function() return vim.lsp.buf.signature_help() end, { buffer = bufnr, desc = "Signature help" }) 1261 | 1262 | map({ "n", "v" }, "ca", vim.lsp.buf.code_action, { buffer = bufnr, desc = "Code action" }) 1263 | map("n", "ch", "ClangdSwitchSourceHeader", { buffer = bufnr, desc = "Switch source/header" }) 1264 | map("n", "ci", "ClangdShowSymbolInfo", { buffer = bufnr, desc = "Symbol information" }) 1265 | map("n", "cl", "LspInfo", { buffer = bufnr, desc = "LSP information" }) 1266 | map("n", "cr", vim.lsp.buf.rename, { buffer = bufnr, desc = "Rename buffer" }) 1267 | 1268 | map("n", "cA", function() vim.lsp.buf.code_action({ context = { only = { "source" }, diagnostics = {} } }) end, { buffer = bufnr, desc = "Source action" }) 1269 | 1270 | map("n", "ss", "Telescope lsp_document_symbols", { buffer = bufnr, desc = "Symbols" }) 1271 | map("n", "sS", "Telescope lsp_dynamic_workspace_symbols", { buffer = bufnr, desc = "Symbols (workspace)" }) 1272 | -- stylua: ignore end 1273 | 1274 | vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) 1275 | Snacks.toggle.inlay_hints():map("uh") 1276 | 1277 | -- toggle virtual diagnostics 1278 | map("n", "ud", function() 1279 | if vim.diagnostic.config().virtual_text then 1280 | vim.diagnostic.config({ virtual_lines = false, virtual_text = false }) 1281 | else 1282 | vim.diagnostic.config(vim.g.diagnostic_config) 1283 | end 1284 | end, { buffer = bufnr, desc = "Toggle virtual text" }) 1285 | 1286 | vim.o.foldmethod = "expr" 1287 | if client:supports_method("textDocument/foldingRange") then 1288 | vim.o.foldexpr = "v:lua.vim.lsp.foldexpr()" 1289 | else 1290 | vim.o.foldexpr = "v:lua.vim.treesitter.foldexpr()" 1291 | end 1292 | 1293 | if client.server_capabilities.codeLensProvider then 1294 | -- stylua: ignore start 1295 | map("n", "cC", vim.lsp.codelens.refresh, { buffer = bufnr, desc = "Refresh and display codelens" }) 1296 | map({ "n", "v" }, "cc", vim.lsp.codelens.run, { buffer = bufnr, desc = "Run codelens" }) 1297 | -- stylua: ignore end 1298 | 1299 | vim.lsp.codelens.refresh() 1300 | vim.api.nvim_create_autocmd( 1301 | { "TextChanged", "InsertLeave", "CursorHold", "LspAttach", "BufEnter" }, 1302 | { buffer = bufnr, callback = vim.lsp.codelens.refresh } 1303 | ) 1304 | end 1305 | 1306 | vim.b[bufnr].autoformat = true 1307 | -- stylua: ignore 1308 | map("n", "uf", function() vim.b[bufnr].autoformat = not vim.b[bufnr].autoformat end, { buffer = bufnr, desc = "Toggle autoformat (buffer)" }) 1309 | end, 1310 | }) 1311 | 1312 | lsp.marksman.setup({ 1313 | capabilities = require("blink.cmp").get_lsp_capabilities( 1314 | { workspace = { didChangeWatchedFiles = { dynamicRegistration = true } } }, 1315 | true 1316 | ), 1317 | on_attach = function(client, bufnr) 1318 | -- vim.notify(vim.inspect(client)) 1319 | 1320 | -- stylua: ignore start 1321 | local map = vim.keymap.set 1322 | map("n", "gd", function() require("telescope.builtin").lsp_definitions({ reuse_win = true }) end, { buffer = bufnr, desc = "Go to definition" }) 1323 | map("n", "gy", function() require("telescope.builtin").lsp_type_definitions({ reuse_win = true }) end, { buffer = bufnr, desc = "Go to type definition" }) 1324 | map("n", "gD", vim.lsp.buf.declaration, { buffer = bufnr, desc = "Go to declaration" }) 1325 | map("n", "gI", function() require("telescope.builtin").lsp_implementations({ reuse_win = true }) end, { buffer = bufnr, desc = "Go to implementation" }) 1326 | map("n", "gK", function() return vim.lsp.buf.signature_help() end, { buffer = bufnr, desc = "Signature help" }) 1327 | 1328 | map({ "n", "v" }, "ca", vim.lsp.buf.code_action, { buffer = bufnr, desc = "Code action" }) 1329 | map("n", "cl", "LspInfo", { buffer = bufnr, desc = "LSP information" }) 1330 | map("n", "cr", vim.lsp.buf.rename, { buffer = bufnr, desc = "Rename buffer" }) 1331 | 1332 | map("n", "cA", function() vim.lsp.buf.code_action({ context = { only = { "source" }, diagnostics = {} } }) end, { buffer = bufnr, desc = "Source action" }) 1333 | map("n", "cR", function() Snacks.rename.rename_file() end, { buffer = bufnr, desc = "Rename file" }) 1334 | 1335 | map("n", "sr", "Telescope lsp_references", { buffer = bufnr, nowait = true, desc = "References" }) 1336 | map("n", "ss", "Telescope lsp_document_symbols", { buffer = bufnr, desc = "Symbols" }) 1337 | map("n", "sS", "Telescope lsp_dynamic_workspace_symbols", { buffer = bufnr, desc = "Symbols (workspace)" }) 1338 | 1339 | map("n", "mp", "Markview splitToggle", { buffer = bufnr, desc = "Markdown preview" }) 1340 | map("n", "mt", "Markview Toggle", { buffer = bufnr, desc = "Markdown toggle" }) 1341 | -- stylua: ignore end 1342 | 1343 | vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) 1344 | Snacks.toggle.inlay_hints():map("uh") 1345 | 1346 | -- toggle virtual diagnostics 1347 | map("n", "ud", function() 1348 | if vim.diagnostic.config().virtual_text then 1349 | vim.diagnostic.config({ virtual_lines = false, virtual_text = false }) 1350 | else 1351 | vim.diagnostic.config(vim.g.diagnostic_config) 1352 | end 1353 | end, { buffer = bufnr, desc = "Toggle virtual text" }) 1354 | 1355 | vim.o.foldmethod = "expr" 1356 | if client:supports_method("textDocument/foldingRange") then 1357 | vim.o.foldexpr = "v:lua.vim.lsp.foldexpr()" 1358 | else 1359 | vim.o.foldexpr = "v:lua.vim.treesitter.foldexpr()" 1360 | end 1361 | 1362 | if client.server_capabilities.codeLensProvider then 1363 | -- stylua: ignore start 1364 | map("n", "cC", vim.lsp.codelens.refresh, { buffer = bufnr, desc = "Refresh and display codelens" }) 1365 | map({ "n", "v" }, "cc", vim.lsp.codelens.run, { buffer = bufnr, desc = "Run codelens" }) 1366 | -- stylua: ignore end 1367 | 1368 | vim.lsp.codelens.refresh() 1369 | vim.api.nvim_create_autocmd( 1370 | { "TextChanged", "InsertLeave", "CursorHold", "LspAttach", "BufEnter" }, 1371 | { buffer = bufnr, callback = vim.lsp.codelens.refresh } 1372 | ) 1373 | end 1374 | 1375 | vim.b[bufnr].autoformat = true 1376 | -- stylua: ignore 1377 | map("n", "uf", function() vim.b[bufnr].autoformat = not vim.b[bufnr].autoformat end, { buffer = bufnr, desc = "Toggle autoformat (buffer)" }) 1378 | end, 1379 | }) 1380 | end, 1381 | }, 1382 | 1383 | -- Formatting support 1384 | { 1385 | "stevearc/conform.nvim", 1386 | dependencies = "williamboman/mason.nvim", -- dependency updates RTP 1387 | event = "BufWritePre", 1388 | cmd = "ConformInfo", 1389 | keys = { 1390 | { 1391 | "cf", 1392 | function() 1393 | require("conform").format({ async = true }) 1394 | end, 1395 | desc = "Format buffer", 1396 | }, 1397 | }, 1398 | ---@module "conform" 1399 | ---@type conform.setupOpts 1400 | opts = { 1401 | formatters_by_ft = { 1402 | c = { "clang_format" }, 1403 | cpp = { "clang_format" }, 1404 | json = { "jq" }, 1405 | lua = { "stylua" }, 1406 | markdown = { "mdformat" }, 1407 | rust = { "rustfmt" }, 1408 | toml = { "taplo" }, 1409 | yaml = { "yamlfmt" }, 1410 | }, 1411 | default_format_opts = { 1412 | lsp_format = "fallback", 1413 | }, 1414 | format_on_save = function(bufnr) 1415 | -- set during lsp config 1416 | if vim.b[bufnr].autoformat then 1417 | return { time_ms = 3000, lsp_format = "fallback" } 1418 | end 1419 | end, 1420 | }, 1421 | }, 1422 | 1423 | -- Completion support 1424 | { 1425 | "saghen/blink.cmp", 1426 | build = "cargo build --release", 1427 | dependencies = "rafamadriz/friendly-snippets", 1428 | event = "InsertEnter", 1429 | ---@module "blink.cmp" 1430 | ---@type blink.cmp.Config 1431 | opts = { 1432 | keymap = { 1433 | preset = "default", 1434 | [""] = {}, 1435 | [""] = { "hide", "show_signature", "hide_signature" }, 1436 | [""] = { "show", "show_documentation", "hide_documentation" }, 1437 | [""] = { "hide", "show" }, 1438 | }, 1439 | signature = { enabled = true }, 1440 | appearance = { nerd_font_variant = "normal" }, 1441 | completion = { ghost_text = { enabled = true } }, 1442 | }, 1443 | }, 1444 | 1445 | -- Improved LSP for Neovim configuration 1446 | { 1447 | "folke/lazydev.nvim", 1448 | ft = "lua", 1449 | cmd = "LazyDev", 1450 | opts = { 1451 | library = { 1452 | { path = "snacks.nvim", words = { "Snacks" } }, 1453 | { path = "${3rd}/luv/library", words = { "vim%.uv" } }, 1454 | }, 1455 | }, 1456 | }, 1457 | 1458 | -- Debugging (DAP) 1459 | { 1460 | "mfussenegger/nvim-dap", 1461 | lazy = true, 1462 | dependencies = "jay-babu/mason-nvim-dap.nvim", 1463 | config = function() 1464 | local icons = { 1465 | Stopped = { "", "DiagnosticWarn", "DapStoppedLine" }, 1466 | Breakpoint = "󰯰", 1467 | BreakpointCondition = "󰯳", 1468 | BreakpointRejected = { "󰰠", "DiagnosticError" }, 1469 | LogPoint = "󰰎", 1470 | } 1471 | for name, sign in pairs(icons) do 1472 | sign = type(sign) == "table" and sign or { sign } 1473 | vim.fn.sign_define( 1474 | "Dap" .. name, 1475 | ---@diagnostic disable-next-line 1476 | { text = sign[1], texthl = sign[2] or "DiagnosticInfo", linehl = sign[3], numhl = sign[3] } 1477 | ) 1478 | end 1479 | 1480 | -- Setup DAP config by VsCode launch.json file 1481 | local vscode = require("dap.ext.vscode") 1482 | local json = require("plenary.json") 1483 | ---@diagnostic disable-next-line 1484 | vscode.json_decode = function(str) 1485 | return vim.json.decode(json.json_strip_comments(str)) 1486 | end 1487 | end, 1488 | }, 1489 | 1490 | -- Mason integration 1491 | { 1492 | "jay-babu/mason-nvim-dap.nvim", 1493 | dependencies = "williamboman/mason.nvim", 1494 | cmd = { "DapInstall", "DapUninstall" }, 1495 | opts = { automatic_installation = true, handlers = {}, ensure_installed = {} }, 1496 | }, 1497 | 1498 | -- UI for the debugger 1499 | { 1500 | "rcarriga/nvim-dap-ui", 1501 | dependencies = { 1502 | "mfussenegger/nvim-dap", 1503 | "nvim-neotest/nvim-nio", 1504 | { 1505 | "theHamsta/nvim-dap-virtual-text", 1506 | ---@module "nvim-dap-virtual-text" 1507 | ---@type nvim_dap_virtual_text_options 1508 | opts = {}, 1509 | }, 1510 | }, 1511 | keys = { 1512 | -- stylua: ignore start 1513 | { "dB", function() require("dap").set_breakpoint(vim.fn.input("Breakpoint condition: ")) end, desc = "Breakpoint condition" }, 1514 | { "db", function() require("dap").toggle_breakpoint() end, desc = "Toggle breakpoint" }, 1515 | { "dc", function() require("dap").run_to_cursor() end, desc = "Run to cursor" }, 1516 | { "dC", function() require("dap").continue() end, desc = "Run/Continue" }, 1517 | { "dg", function() require("dap").goto_() end, desc = "Go to line (no execute)" }, 1518 | { "di", function() require("dap").step_into() end, desc = "Step into" }, 1519 | { "dj", function() require("dap").down() end, desc = "down" }, 1520 | { "dk", function() require("dap").up() end, desc = "up" }, 1521 | { "dl", function() require("dap").run_last() end, desc = "Run last" }, 1522 | { "do", function() require("dap").step_over() end, desc = "Step over" }, 1523 | { "dO", function() require("dap").step_out() end, desc = "Step out" }, 1524 | { "dP", function() require("dap").pause() end, desc = "Pause" }, 1525 | { "dr", function() require("dap").repl.toggle() end, desc = "Toggle REPL" }, 1526 | { "ds", function() require("dap").session() end, desc = "Session" }, 1527 | { "dt", function() require("dap").terminate() end, desc = "Terminate" }, 1528 | { "dw", function() require("dap.ui.widgets").hover() end, desc = "Widgets" }, 1529 | { "dX", "DapClearBreakpoints", desc = "Clear breakpoints" }, 1530 | { "du", function() require("dapui").toggle({}) end, desc = "Dap UI" }, 1531 | { "de", function() require("dapui").eval() end, desc = "Eval", mode = { "n", "v" } }, 1532 | { 1533 | "da", 1534 | function() 1535 | require("dap").continue({ 1536 | before = function(config) 1537 | local args = type(config.args) == "function" and config.args() or config.args or {} 1538 | local args_str = type(args) == "table" and table.concat(args, " ") or args 1539 | config = vim.deepcopy(config) 1540 | config.args = function() 1541 | ---@diagnostic disable-next-line 1542 | local new_args = vim.fn.expand(vim.fn.input("Run with args: ", args_str)) 1543 | ---@diagnostic disable-next-line 1544 | return require("dap.utils").splitstr(new_args) 1545 | end 1546 | return config 1547 | end, 1548 | }) 1549 | end, 1550 | desc = "Run with args", 1551 | }, 1552 | -- stylua: ignore end 1553 | }, 1554 | opts = { controls = { enabled = false } }, 1555 | config = function(_, opts) 1556 | local dap = require("dap") 1557 | local dapui = require("dapui") 1558 | dapui.setup(opts) 1559 | dap.listeners.after.event_initialized["dapui_config"] = function() 1560 | dapui.open({}) 1561 | end 1562 | dap.listeners.before.event_terminated["dapui_config"] = function() 1563 | dapui.close({}) 1564 | end 1565 | dap.listeners.before.event_exited["dapui_config"] = function() 1566 | dapui.close({}) 1567 | end 1568 | end, 1569 | }, 1570 | 1571 | -- Integrated unit testing 1572 | { 1573 | "nvim-neotest/neotest", 1574 | dependencies = { "nvim-neotest/nvim-nio" }, 1575 | 1576 | ---@module "neotest" 1577 | ---@type neotest.Config 1578 | opts = { 1579 | adapters = { 1580 | ["rustaceanvim.neotest"] = {}, 1581 | }, 1582 | status = { virtual_text = true }, 1583 | output = { open_on_run = true }, 1584 | quickfix = { 1585 | open = function() 1586 | vim.cmd("copen") 1587 | end, 1588 | }, 1589 | floating = { border = "none" }, 1590 | icons = { expanded = "┐", final_child_prefix = "└" }, 1591 | summary = { animated = false }, 1592 | }, 1593 | config = function(_, opts) 1594 | if opts.adapters then 1595 | local adapters = {} 1596 | for name in pairs(opts.adapters or {}) do 1597 | local adapter = require(name) 1598 | adapters[#adapters + 1] = adapter 1599 | end 1600 | opts.adapters = adapters 1601 | end 1602 | require("neotest").setup(opts) 1603 | end, 1604 | -- stylua: ignore 1605 | keys = { 1606 | { "t", "", desc = "+test" }, 1607 | { "tt", function() require("neotest").run.run(vim.fn.expand("%")) end, desc = "Run file (neotest)" }, 1608 | { "tT", function() require("neotest").run.run(vim.uv.cwd()) end, desc = "Run all test files (neotest)" }, 1609 | { "tr", function() require("neotest").run.run() end, desc = "Run nearest (neotest)" }, 1610 | { "tl", function() require("neotest").run.run_last() end, desc = "Run last (neotest)" }, 1611 | { "ts", function() require("neotest").summary.toggle() end, desc = "Toggle summary (neotest)" }, 1612 | { "to", function() require("neotest").output.open({ enter = true, auto_close = true }) end, desc = "Show output (neotest)" }, 1613 | { "tO", function() require("neotest").output_panel.toggle() end, desc = "Toggle output panel (neotest)" }, 1614 | { "tS", function() require("neotest").run.stop() end, desc = "Stop (neotest)" }, 1615 | { "tw", function() require("neotest").watch.toggle(vim.fn.expand("%")) end, desc = "Toggle watch (neotest)" }, 1616 | ---@diagnostic disable-next-line: missing-fields 1617 | { "td", function() require("neotest").run.run({ strategy = "dap" }) end, desc = "Debug nearest" }, 1618 | }, 1619 | }, 1620 | 1621 | -- Markdown 1622 | { 1623 | "OXY2DEV/markview.nvim", 1624 | ---@module "markview" 1625 | ---@type mkv.config 1626 | opts = { 1627 | preview = { enable = false }, 1628 | }, 1629 | ft = "markdown", 1630 | }, 1631 | } 1632 | 1633 | --[[ 1634 | █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀▀█ █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀▀▀█ 1635 | █ │░███▀█▀▀▀▀▀▓████▓▄ ▀▀▀▀ │░████▓▄ │▓████▓▄ █ 1636 | █ │▒███████ │▓███████ │▒███████ │▓███████ █ 1637 | █ │▓███████ │▓███████ │▓███████ │▓███████ █ 1638 | ▀ │▓███████ │▓███████ │▓███████ │▓███████ █ 1639 | ▀ │▓███████ │▓███████▄ ▄ │▓███████ │▓███████ █ 1640 | █ │▓███████ │▓███████ ▓███████ █▄▄▄ 1641 | █ │▓███████▀▀ ▀ ▀ │▓███████▀▀▀▓█▄█████▄ ▄ █ 1642 | █▄▄▄▄▄▄▄▄ ▀ █▀▀▀▀▀▀▀▀▀▀▀▀█▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄█ 1643 | █ ▀ █ 1644 | ▀▀▀▀▀ 1645 | 1646 | ▀ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀ 1647 | n e o v i m 1648 | 1649 | _______ _ __ _____ /\ 1650 | _ __ ____ | /____ ____ \ /\____ _/ \___ __ __ _ 1651 | \_ \| /_ / \ \/ /___/ \ ___ 1652 | / \ / / /_ \ \ / / / \/ \ 1653 | / \ / / / _/ /\ / / / \ 1654 | \____\ /\_____/\_____/ \__/___/________\/ \ 1655 | \/ \_____/ 1656 | 1657 | __ _ ____ __ /\___ _ __ ______ __/\ n e o v i m 1658 | \/ \ _ ___ __ _ ____ 1659 | \/ 1660 | 1661 | _______ 1662 | ____ | /____ ____ ___ ____ ___/\ __ 1663 | \_ \| /_ / \ \/ /___/ \ / \ 1664 | / \ / / /_ /\ \ / / / \/ \ 1665 | / \ / / / _/ /\ / / / \ 1666 | \____\ /\_____/\_____/ \__/___/______\/ \ 1667 | \/ \______/ 1668 | 1669 | ______/\ __________________/\ n e o v i m 1670 | \/ \ _______________ 1671 | \/ 1672 | --]] 1673 | -------------------------------------------------------------------------------- /reset.ps1: -------------------------------------------------------------------------------- 1 | Remove-Item -Recurse -Force -Path @("$env:LOCALAPPDATA\nvim-data",".\lazy-lock.json") 2 | --------------------------------------------------------------------------------