My personal configuration files and scripts.
at master 18 kB view raw
1-- Preamble ⦃ 2 3-- Add a more convenient way of creating key bindings. 4local function map(mode, lhs, rhs, options) 5 local base_options = { noremap = true, silent = true } 6 vim.keymap.set( 7 mode, 8 lhs, 9 rhs, 10 not options and base_options 11 or vim.tbl_extend("force", base_options, options) 12 ) 13end 14 15-- Bootstrap the plugin manager. 16local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" 17if not vim.uv.fs_stat(lazypath) then 18 vim.fn.system({ 19 "git", 20 "clone", 21 "--filter=blob:none", 22 "https://github.com/folke/lazy.nvim.git", 23 "--branch=stable", 24 lazypath, 25 }) 26end 27vim.opt.rtp:prepend(lazypath) 28 29-- Set the global leader to SPC and the local leader to SPC m. 30vim.g.mapleader = " " -- NOTE: Must be set before specifying plugins. 31vim.g.maplocalleader = " m" 32 33-- ⦄ 34 35-- Plugins ⦃ 36 37local lazy_config = { 38 performance = { 39 rtp = { 40 -- Prevent loading bundled plugins that are not required or superseded by 41 -- other plugins. 42 disabled_plugins = { 43 "gzip", 44 "matchit", 45 "matchparen", 46 "netrwPlugin", 47 "tarPlugin", 48 "tohtml", 49 "tutor", 50 "zipPlugin", 51 }, 52 }, 53 }, 54} 55 56require("lazy").setup({ 57 -- Libraries ⦃ 58 { "nvim-lua/plenary.nvim", name = "plenary" }, 59 -- ⦄ 60 61 -- Visuals ⦃ 62 { 63 "catppuccin/nvim", 64 name = "catppuccin", 65 priority = 1000, 66 opts = { 67 show_end_of_buffer = true, 68 dim_inactive = { enabled = true }, 69 custom_highlights = function(colors) 70 return { 71 -- Make the background for folds slightly more muted than the default 72 -- of `colors.surface1`. 73 Folded = { bg = colors.surface0 }, 74 } 75 end, 76 }, 77 }, 78 { 79 "nvim-lualine/lualine.nvim", 80 event = "VeryLazy", 81 dependencies = { "kyazdani42/nvim-web-devicons" }, 82 opts = { 83 options = { 84 theme = "catppuccin", 85 section_separators = { left = "", right = "" }, 86 -- Only display one 'global' status line rather than one per window. 87 globalstatus = true, 88 }, 89 sections = { 90 lualine_a = { 91 { "mode", separator = { left = "" }, right_padding = 2 }, 92 }, 93 lualine_b = { "filename", "branch" }, 94 lualine_c = {}, 95 lualine_x = {}, 96 lualine_y = { 97 "filetype", 98 { 99 "fileformat", 100 separator = { right = "" }, 101 padding = { left = 1, right = 0 }, 102 }, 103 "encoding", 104 "progress", 105 }, 106 lualine_z = { 107 { "location", separator = { right = "" }, left_padding = 2 }, 108 }, 109 }, 110 inactive_sections = { 111 lualine_a = { "filename" }, 112 lualine_b = {}, 113 lualine_c = {}, 114 lualine_x = {}, 115 lualine_y = {}, 116 lualine_z = { "location" }, 117 }, 118 extensions = { "lazy", "mundo", "quickfix" }, 119 }, 120 }, 121 { "stevearc/dressing.nvim", name = "dressing", event = "VeryLazy" }, 122 -- ⦄ 123 124 -- Interface ⦃ 125 { 126 "folke/persistence.nvim", 127 event = "BufReadPre", -- this will only start session saving when an actual file was opened 128 opts = {}, 129 }, 130 { 131 "folke/which-key.nvim", 132 name = "which-key", 133 config = true, 134 }, 135 { 136 "folke/snacks.nvim", 137 dependencies = { "echasnovski/mini.nvim", version = false }, 138 priority = 1000, 139 lazy = false, 140 opts = { 141 dashboard = { enabled = true }, 142 explorer = { enabled = true }, 143 input = { enabled = true }, 144 picker = { enabled = true }, 145 session = { enabled = true }, 146 statuscolumn = { 147 enabled = true, 148 folds = { 149 -- Display an icon for an open fold. 150 open = true, 151 -- Indicate added, changed, and deleted lines. 152 git_hl = true, 153 }, 154 }, 155 zen = { enabled = true }, 156 }, 157 keys = { 158 { 159 "<leader><leader>", 160 function() 161 Snacks.picker.smart() 162 end, 163 desc = "Smart Find Files", 164 }, 165 { 166 "<leader>,", 167 function() 168 Snacks.picker.buffers() 169 end, 170 desc = "Buffers", 171 }, 172 { 173 "<leader>/", 174 function() 175 Snacks.picker.grep() 176 end, 177 desc = "Grep", 178 }, 179 { 180 "<leader>:", 181 function() 182 Snacks.picker.command_history() 183 end, 184 desc = "Command History", 185 }, 186 { 187 "<leader>n", 188 function() 189 Snacks.picker.notifications() 190 end, 191 desc = "Notification History", 192 }, 193 { 194 "<leader>e", 195 function() 196 Snacks.explorer() 197 end, 198 desc = "File Explorer", 199 }, 200 201 { 202 "<leader>fb", 203 function() 204 Snacks.picker.buffers() 205 end, 206 desc = "Buffers", 207 }, 208 { 209 "<leader>fc", 210 function() 211 Snacks.picker.files({ cwd = vim.fn.stdpath("config") }) 212 end, 213 desc = "Find Config File", 214 }, 215 { 216 "<leader>ff", 217 function() 218 Snacks.picker.files() 219 end, 220 desc = "Find Files", 221 }, 222 { 223 "<leader>fg", 224 function() 225 Snacks.picker.git_files() 226 end, 227 desc = "Find Git Files", 228 }, 229 { 230 "<leader>fp", 231 function() 232 Snacks.picker.projects() 233 end, 234 desc = "Projects", 235 }, 236 { 237 "<leader>fr", 238 function() 239 Snacks.picker.recent() 240 end, 241 desc = "Recent", 242 }, 243 244 { 245 "<leader>z", 246 function() 247 Snacks.zen() 248 end, 249 desc = "Toggle Zen Mode", 250 }, 251 { 252 "<leader>N", 253 desc = "Neovim News", 254 function() 255 Snacks.win({ 256 file = vim.api.nvim_get_runtime_file("doc/news.txt", false)[1], 257 width = 0.9, 258 height = 0.8, 259 wo = { 260 spell = false, 261 wrap = false, 262 signcolumn = "yes", 263 statuscolumn = " ", 264 conceallevel = 3, 265 }, 266 }) 267 end, 268 }, 269 }, 270 }, 271 { 272 "mikavilpas/yazi.nvim", 273 event = "VeryLazy", 274 dependencies = { "folke/snacks.nvim", lazy = false }, 275 opts = { 276 open_for_directories = true, 277 }, 278 init = function() 279 -- Fake loading the netrw plugin so that the `open_for_directories` option 280 -- functions. 281 vim.g.loaded_netrwPlugin = 1 282 end, 283 keys = { 284 { 285 "<leader>fb", 286 mode = { "n", "v" }, 287 "<cmd>Yazi<cr>", 288 desc = "Browse files", 289 }, 290 }, 291 }, 292 { "simnalamburt/vim-mundo", cmd = { "MundoShow", "MundoToggle" } }, 293 { 294 "lewis6991/gitsigns.nvim", 295 name = "gitsigns", 296 event = "VeryLazy", 297 config = true, 298 }, 299 { 300 "folke/todo-comments.nvim", 301 dependencies = { "folke/snacks.nvim" }, 302 event = { "BufNewFile", "BufReadPost" }, 303 opts = { 304 search = { pattern = [[\b(KEYWORDS)(\([^\)]*\))?:]] }, 305 highlight = { pattern = [[.*<((KEYWORDS)%(\(.{-1,}\))?):]] }, 306 keywords = { 307 -- This keyword is often used in Rust code to mark comments justifying 308 -- why the code inside an unsafe block is actually safe. 309 SAFETY = { icon = "", color = "warning" }, 310 } 311 }, 312 keys = { 313 { 314 "<leader>st", 315 function() 316 Snacks.picker.todo_comments() 317 end, 318 desc = "Todo", 319 }, 320 { 321 "<leader>sT", 322 function() 323 Snacks.picker.todo_comments({ keywords = { "TODO", "FIX", "FIXME" } }) 324 end, 325 desc = "Todo/Fix/Fixme", 326 }, 327 }, 328 }, 329 { 330 "NvChad/nvim-colorizer.lua", 331 name = "colorizer", 332 event = "VeryLazy", 333 opts = { filetypes = { "html", "css", "javascript", "typescript" } }, 334 }, 335 { "rcarriga/nvim-notify", name = "notify", event = "VeryLazy" }, 336 { "folke/noice.nvim", event = "VeryLazy" }, 337 -- ⦄ 338 339 -- Editing ⦃ 340 { 341 "tmsvg/pear-tree", 342 event = "InsertEnter", 343 init = function() 344 vim.g.pear_tree_smart_openers = 1 345 vim.g.pear_tree_smart_closers = 1 346 vim.g.pear_tree_smart_backspace = 1 347 end, 348 }, 349 { "echasnovski/mini.ai", config = true }, 350 { "echasnovski/mini.comment", config = true }, 351 { "kylechui/nvim-surround", config = true }, 352 { 353 "ggandor/leap.nvim", 354 name = "leap", 355 config = function() 356 require("leap").add_default_mappings() 357 end, 358 }, 359 { "ggandor/leap-spooky.nvim", name = "leap-spooky", config = true }, 360 { "ggandor/flit.nvim", name = "flit", config = true }, 361 -- ⦄ 362 363 -- Completion and Diagnostics ⦃ 364 -- TODO: Set up completion. 365 --- ⦄ 366 367 -- Language Support ⦃ 368 { 369 "nvim-treesitter/nvim-treesitter", 370 -- TODO: Remove this once main is the default branch. 371 branch = "main", 372 lazy = false, 373 build = function() 374 local ts = require("nvim-treesitter") 375 ts.install({ 376 "bash", 377 "bibtex", 378 "c", 379 "c_sharp", 380 "cmake", 381 "cpp", 382 "css", 383 "dockerfile", 384 "eex", 385 "elixir", 386 "erlang", 387 "fish", 388 "glsl", 389 "go", 390 "haskell", 391 "heex", 392 "html", 393 "java", 394 "javascript", 395 "jsdoc", 396 "json", 397 "just", 398 "kotlin", 399 "lua", 400 "markdown", 401 "markdown_inline", 402 "meson", 403 "ninja", 404 "nix", 405 "ocaml", 406 "ocaml_interface", 407 "perl", 408 "php", 409 "prolog", 410 "python", 411 "qmljs", 412 "query", 413 "regex", 414 "ruby", 415 "rust", 416 "sql", 417 "svelte", 418 "toml", 419 "typescript", 420 "typst", 421 "vim", 422 "vimdoc", 423 "wgsl", 424 "wit", 425 "yaml", 426 "zig", 427 }) 428 ts.update() 429 end, 430 }, 431 { "Shirk/vim-gas" }, 432 { "lervag/vimtex", ft = "tex" }, 433 { 434 "nvim-neorg/neorg", 435 ft = "norg", 436 config = function() 437 require("neorg").setup({ 438 load = { 439 ["core.defaults"] = {}, 440 ["core.norg.concealer"] = {}, 441 ["core.norg.completion"] = { 442 config = { engine = "nvim-cmp" }, 443 }, 444 ["core.integrations.nvim-cmp"] = {}, 445 }, 446 }) 447 end, 448 }, 449 -- ⦄ 450}, lazy_config) 451 452-- ⦄ 453 454-- Visuals ⦃ 455 456-- Use 24-bit colour like it's 1995! 457vim.opt.termguicolors = true 458 459-- Load my default colorscheme of choice. 460vim.cmd.colorscheme("catppuccin-mocha") 461 462-- The current mode is already displayed in the status line (by Lualine). 463vim.opt.showmode = false 464 465-- Modify cursor styling so that... 466vim.opt.guicursor = { 467 -- the normal, visual, command line normal, and show-match modes use a block 468 -- cursor with the default colours defined by the terminal; 469 "n-v-c-sm:block", 470 -- the insert, command line insert, and visual with selection modes use a 471 -- blinking, vertical cursor with colours from the `Cursor` highlight group; 472 "i-ci-ve:ver25-blinkwait700-blinkoff400-blinkon250-Cursor/lCursor", 473 -- the replace, command line replace, and operator-pending modes use a 474 -- blinking, horizontal cursor with colours from the `Cursor` highlight group; 475 "r-cr-o:hor20-blinkwait700-blinkoff400-blinkon250-Cursor/lCursor", 476 -- the built-in terminal uses a blinking, block cursor with colours from the 477 -- `TermCursor` highlight group. 478 "t:block-blinkon500-blinkoff500-TermCursor", 479} 480 481-- ⦄ 482 483-- Interface ⦃ 484 485map("n", "<leader>q", ":qa<CR>", { desc = "Quit" }) 486map("n", "<leader>Q", ":qa<CR>", { desc = "Force quit" }) 487 488-- Disable arrow keys in normal/visual mode so that I can break the habit. 489for _, direction in ipairs({ "up", "down", "left", "right" }) do 490 map("", "<" .. direction .. ">", "<nop>") 491end 492 493-- Set the title of the window if possible. 494vim.opt.title = true 495 496-- Always display the sign column to avoid unwarranted jumps. 497vim.opt.signcolumn = "yes" 498 499-- Do not wrap lines longer than the window width. 500vim.opt.wrap = false 501 502-- Align wrapped lines with the indentation for that line. 503vim.opt.breakindent = true 504 505-- Enable flash on yank. 506vim.api.nvim_create_autocmd("TextYankPost", { 507 group = vim.api.nvim_create_augroup("YankFlash", {}), 508 callback = function() 509 vim.highlight.on_yank({ higroup = "Visual", timeout = 300 }) 510 end, 511}) 512 513-- Improve the behaviour of search. 514vim.opt.ignorecase = true 515vim.opt.smartcase = true 516vim.opt.gdefault = true 517 518-- Start scrolling 4 lines before the edge of the window. 519vim.opt.scrolloff = 4 520vim.opt.sidescrolloff = 4 521 522-- Enable Tree-Sitter-powered folding and automatically close folds that are 12 523-- or more levels deep. That much nesting should be fairly rare. 524vim.opt.foldlevel = 12 525vim.opt.foldmethod = "expr" 526vim.opt.foldexpr = "v:lua.vim.treesitter.foldexpr()" 527vim.opt.fillchars:append({ fold = " " }) 528 529-- Enable relative line numbers. 530vim.opt.number = true 531vim.opt.relativenumber = true 532 533-- More specifically, use relative line numbers in normal mode and absolute line 534-- numbers in insert mode. 535local line_number_group = vim.api.nvim_create_augroup("LineNumber", {}) 536vim.api.nvim_create_autocmd( 537 { "BufEnter", "FocusGained", "InsertLeave", "WinEnter" }, 538 { 539 group = line_number_group, 540 callback = function() 541 if vim.opt.number:get() then 542 vim.opt.relativenumber = true 543 end 544 end, 545 } 546) 547vim.api.nvim_create_autocmd( 548 { "BufLeave", "FocusLost", "InsertEnter", "WinLeave" }, 549 { 550 group = line_number_group, 551 callback = function() 552 if vim.opt.number:get() then 553 vim.opt.relativenumber = false 554 end 555 end, 556 } 557) 558 559-- Disable line numbers in the integrated terminal and default to insert mode. 560vim.api.nvim_create_autocmd("TermOpen", { 561 group = vim.api.nvim_create_augroup("CustomizeTerminal", {}), 562 callback = function() 563 vim.opt_local.number = false 564 vim.opt_local.relativenumber = false 565 566 vim.cmd.startinsert() 567 end, 568}) 569 570-- Highlight the line the cursor is on in the currently active buffer. 571local cursor_line_group = vim.api.nvim_create_augroup("ShowCursorLine", {}) 572vim.api.nvim_create_autocmd({ "VimEnter", "WinEnter", "BufWinEnter" }, { 573 group = cursor_line_group, 574 callback = function() 575 vim.opt_local.cursorline = true 576 end, 577}) 578vim.api.nvim_create_autocmd({ "WinLeave" }, { 579 group = cursor_line_group, 580 callback = function() 581 vim.opt_local.cursorline = false 582 end, 583}) 584 585-- Jump to the last known cursor position when opening a file, so long as the 586-- position is still valid and we are not editing a commit message (it's likely 587-- a different one than last time). 588vim.api.nvim_create_autocmd("BufReadPost", { 589 group = vim.api.nvim_create_augroup("RestorePosition", {}), 590 callback = function(args) 591 local last_posn = vim.fn.line([['"]]) 592 593 local is_valid_line = 0 <= last_posn and last_posn <= vim.fn.line("$") 594 -- TODO: Investigate replacing this by more generally excluding such files 595 -- from shada. 596 local is_commit = 597 vim.list_contains({ "commit", "jjdescription" }, vim.b[args.buf].filetype) 598 599 if not is_valid_line or is_commit then 600 return 601 end 602 603 vim.cmd.normal({ args = { 'g`"' }, bang = true }) 604 -- Deferring this command is somewhat of a hack to ensure that the fold 605 -- containing the cursor is expanded since folds can be created (are 606 -- created?) _after_ the `BufReadPost` event. 607 vim.defer_fn(function() 608 vim.cmd.normal({ args = { "zv" }, bang = true }) 609 end, 5) 610 end, 611}) 612 613-- Automatically create parent directories when saving a file. 614vim.api.nvim_create_autocmd("BufWritePre", { 615 group = vim.api.nvim_create_augroup("CreateParentDirs", {}), 616 callback = function(event) 617 local file = vim.uv.fs_realpath(event.match) or event.match 618 619 vim.fn.mkdir(vim.fn.fnamemodify(file, ":p:h"), "p") 620 local backup = vim.fn.fnamemodify(file, ":p:~:h") 621 backup = backup:gsub("[/\\]", "%%") 622 vim.opt_global.backupext = backup 623 end, 624}) 625 626-- ⦄ 627 628-- Editing ⦃ 629 630-- Use 2 spaces for indentation. 631local indent_width = 2 632vim.opt.tabstop = indent_width -- indenting with tab 633vim.opt.softtabstop = indent_width 634vim.opt.shiftwidth = indent_width -- indenting with > 635vim.opt.expandtab = true -- use spaces, not tab characters 636 637-- Use 80 characters as the maximum line length. 638local text_width = 80 639vim.opt.textwidth = text_width 640vim.opt.colorcolumn = { text_width + 1 } 641 642-- Stop the Rust ftplugin from fucking with the above. 643vim.g.rust_recommended_style = false 644 645-- Delete a level of indentation with Shift+Tab. 646map("i", "<S-Tab>", "<C-d>") 647 648-- Map Ctrl+Backspace to delete backwards by words. 649map("i", "<C-BS>", "<C-w>") 650 651-- Display invisible characters. 652vim.opt.list = true 653vim.opt.listchars = { 654 tab = "•·", 655 trail = "·", 656 extends = "", 657 precedes = "", 658 nbsp = "×", 659} 660 661-- Persist undo history. 662vim.opt.undofile = true 663 664-- Add dedicated key bindings for yanking to and pasting from the system 665-- clipboard. 666map({ "n", "v" }, "<C-y>", '"+y', { desc = "Yank to clipboard" }) 667map({ "n", "v" }, "<C-p>", '"+p', { desc = "Paste from clipboard" }) 668 669-- Associate the GLSL filetype with the typical file extensions used for 670-- shaders. 671vim.api.nvim_create_autocmd({ "BufNewFile", "BufRead" }, { 672 group = vim.api.nvim_create_augroup("AssociateGlsl", {}), 673 pattern = { "*.vert", "*.frag", "*.tesc", "*.geom", "*.comp" }, 674 callback = function() 675 vim.opt_local.filetype = "glsl" 676 end, 677}) 678 679-- ⦄ 680 681-- Completion and Diagnostics ⦃ 682 683-- Use British English and New Zealand English dictionaries for spell checking. 684vim.opt.spelllang = { "en_gb", "en_nz" } 685 686-- Enable spell checking. 687vim.opt.spell = true 688 689-- ⦄ 690 691-- vim: set foldlevel=0 foldmethod=marker foldmarker=⦃,⦄: