-- always set leader first! vim.keymap.set("n", "", "", { silent = true }) vim.g.mapleader = " " ------------------------------------------------------------------------------- -- -- preferences -- ------------------------------------------------------------------------------- -- never ever folding vim.opt.foldenable = false vim.opt.foldmethod = 'manual' vim.opt.foldlevelstart = 99 -- very basic "continue indent" mode (autoindent) is always on in neovim -- could try smartindent/cindent, but meh. -- vim.opt.cindent = true -- XXX -- vim.opt.cmdheight = 2 -- vim.opt.completeopt = 'menuone,noinsert,noselect' -- not setting updatedtime because I use K to manually trigger hover effects -- and lowering it also changes how frequently files are written to swap. -- Uncommented to see if it'll make tooltips pop up automatically vim.opt.updatetime = 300 -- if key combos seem to be "lagging" -- http://stackoverflow.com/questions/2158516/delay-before-o-opens-a-new-line -- vim.opt.timeoutlen = 300 -- keep more context on screen while scrolling vim.opt.scrolloff = 2 -- never show me line breaks if they're not there vim.opt.wrap = false -- always draw sign column. prevents buffer moving when adding/deleting sign vim.opt.signcolumn = 'yes' -- sweet sweet relative line numbers vim.opt.relativenumber = true -- and show the absolute line number for the current line vim.opt.number = true -- keep current content top + left when splitting vim.opt.splitright = true vim.opt.splitbelow = true -- infinite undo! -- NOTE: ends up in ~/.local/state/nvim/undo/ vim.opt.undofile = true --" Decent wildmenu -- in completion, when there is more than one match, -- list all matches, and only complete to longest common match vim.opt.wildmode = 'list:longest' -- when opening a file with a command (like :e), -- don't suggest files like there: vim.opt.wildignore = '.hg,.svn,*~,*.png,*.jpg,*.gif,*.min.js,*.swp,*.o,vendor,dist,_site' -- tabs: go big or go home vim.opt.shiftwidth = 4 vim.opt.softtabstop = 4 vim.opt.tabstop = 8 vim.opt.expandtab = false -- case-insensitive search/replace vim.opt.ignorecase = true -- unless uppercase in search term vim.opt.smartcase = true -- never ever make my terminal beep vim.opt.vb = true -- more useful diffs (nvim -d) --- by ignoring whitespace vim.opt.diffopt:append('iwhite') --- and using a smarter algorithm --- https://vimways.org/2018/the-power-of-diff/ --- https://stackoverflow.com/questions/32365271/whats-the-difference-between-git-diff-patience-and-git-diff-histogram --- https://luppeng.wordpress.com/2020/10/10/when-to-use-each-of-the-git-diff-algorithms/ vim.opt.diffopt:append('algorithm:histogram') vim.opt.diffopt:append('indent-heuristic') -- show a column at 80 characters as a guide for long lines vim.opt.colorcolumn = '80' --- except in Rust where the rule is 100 characters vim.api.nvim_create_autocmd('Filetype', { pattern = 'rust', command = 'set colorcolumn=100' }) -- show more hidden characters -- also, show tabs nicer vim.opt.listchars = 'tab:^ ,nbsp:¬,extends:»,precedes:«,trail:•' -- Arc, March 20, 2025 -- Setting the filetype for Verilog vim.api.nvim_create_autocmd( { "BufNewFile", "BufRead" }, { pattern = { "*.v" }, command = "set filetype=verilog", } ) ------------------------------------------------------------------------------- -- -- hotkeys -- ------------------------------------------------------------------------------- -- quick-open -- Replaced with FZF instead of Files because files pulls up empty for some reason - 2024-10-2 vim.keymap.set('', '', 'FZF') -- vim.keymap.set('', '', 'Files') -- search buffers vim.keymap.set('n', ';', 'Buffers') -- quick-save vim.keymap.set('n', 'w', 'w') -- make missing : less annoying vim.keymap.set('n', ';', ':') -- Ctrl+j and Ctrl+k as Esc vim.keymap.set('n', '', '') vim.keymap.set('i', '', '') vim.keymap.set('v', '', '') vim.keymap.set('s', '', '') vim.keymap.set('x', '', '') vim.keymap.set('c', '', '') vim.keymap.set('o', '', '') vim.keymap.set('l', '', '') vim.keymap.set('t', '', '') -- Ctrl-j is a little awkward unfortunately: -- https://github.com/neovim/neovim/issues/5916 -- So we also map Ctrl+k vim.keymap.set('n', '', '') vim.keymap.set('i', '', '') vim.keymap.set('v', '', '') vim.keymap.set('s', '', '') vim.keymap.set('x', '', '') vim.keymap.set('c', '', '') vim.keymap.set('o', '', '') vim.keymap.set('l', '', '') vim.keymap.set('t', '', '') -- Ctrl+h to stop searching vim.keymap.set('v', '', 'nohlsearch') vim.keymap.set('n', '', 'nohlsearch') -- Jump to start and end of line using the home row keys vim.keymap.set('', 'H', '^') vim.keymap.set('', 'L', '$') -- Neat X clipboard integration -- p will paste clipboard into buffer -- c will copy entire buffer into clipboard vim.keymap.set('n', 'p', 'read !wl-paste') -- toggles between buffers vim.keymap.set('n', '', '') -- , shows/hides hidden characters vim.keymap.set('n', ',', ':set invlist') -- always center search results vim.keymap.set('n', 'n', 'nzz', { silent = true }) vim.keymap.set('n', 'N', 'Nzz', { silent = true }) vim.keymap.set('n', '*', '*zz', { silent = true }) vim.keymap.set('n', '#', '#zz', { silent = true }) vim.keymap.set('n', 'g*', 'g*zz', { silent = true }) -- "very magic" (less escaping needed) regexes by default vim.keymap.set('n', '?', '?\\v') vim.keymap.set('n', '/', '/\\v') vim.keymap.set('c', '%s/', '%sm/') -- open new file adjacent to current file vim.keymap.set('n', 'o', ':e =expand("%:p:h") . "/" ') -- no arrow keys --- force yourself to use the home row vim.keymap.set('n', '', '') vim.keymap.set('n', '', '') vim.keymap.set('i', '', '') vim.keymap.set('i', '', '') vim.keymap.set('i', '', '') vim.keymap.set('i', '', '') -- let the left and right arrows be useful: they can switch buffers vim.keymap.set('n', '', ':bp') vim.keymap.set('n', '', ':bn') -- make j and k move by visual line, not actual line, when text is soft-wrapped vim.keymap.set('n', 'j', 'gj') vim.keymap.set('n', 'k', 'gk') -- handy keymap for replacing up to next _ (like in variable names) vim.keymap.set('n', 'm', 'ct_') -- F1 is pretty close to Esc, so you probably meant Esc vim.keymap.set('', '', '') vim.keymap.set('i', '', '') -- Allow virtual text vim.diagnostic.config({ virtual_text = true, virtual_lines = false }) -- Use f2 to rename symbol (arc) - 2024-10-02 vim.keymap.set('n', '', function() vim.lsp.buf.rename() end) ------------------------------------------------------------------------------ -- -- autocommands -- ------------------------------------------------------------------------------- -- highlight yanked text vim.api.nvim_create_autocmd( 'TextYankPost', { pattern = '*', command = 'silent! lua vim.highlight.on_yank({ timeout = 500 })' } ) -- jump to last edit position on opening file vim.api.nvim_create_autocmd( 'BufReadPost', { pattern = '*', callback = function(ev) if vim.fn.line("'\"") > 1 and vim.fn.line("'\"") <= vim.fn.line("$") then -- except for in git commit messages -- https://stackoverflow.com/questions/31449496/vim-ignore-specifc-file-in-autocommand if not vim.fn.expand('%:p'):find('.git', 1, true) then vim.cmd('exe "normal! g\'\\""') end end end } ) -- prevent accidental writes to buffers that shouldn't be edited vim.api.nvim_create_autocmd('BufRead', { pattern = '*.orig', command = 'set readonly' }) vim.api.nvim_create_autocmd('BufRead', { pattern = '*.pacnew', command = 'set readonly' }) -- leave paste mode when leaving insert mode (if it was on) vim.api.nvim_create_autocmd('InsertLeave', { pattern = '*', command = 'set nopaste' }) -- help filetype detection (add as needed) --vim.api.nvim_create_autocmd('BufRead', { pattern = '*.ext', command = 'set filetype=someft' }) -- correctly classify mutt buffers local email = vim.api.nvim_create_augroup('email', { clear = true }) vim.api.nvim_create_autocmd({ 'BufNewFile', 'BufRead' }, { pattern = '/tmp/mutt*', group = email, command = 'setfiletype mail', }) -- also, produce "flowed text" wrapping -- https://brianbuccola.com/line-breaks-in-mutt-and-vim/ vim.api.nvim_create_autocmd('Filetype', { pattern = 'mail', group = email, command = 'setlocal formatoptions+=w', }) -- shorter columns in text because it reads better that way local text = vim.api.nvim_create_augroup('text', { clear = true }) for _, pat in ipairs({ 'text', 'markdown', 'mail', 'gitcommit' }) do vim.api.nvim_create_autocmd('Filetype', { pattern = pat, group = text, command = 'setlocal spell tw=72 colorcolumn=73', }) end --- tex has so much syntax that a little wider is ok vim.api.nvim_create_autocmd('Filetype', { pattern = 'tex', group = text, command = 'setlocal spell tw=80 colorcolumn=81', }) -- TODO: no autocomplete in text ------------------------------------------------------------------------------- -- -- plugin configuration -- ------------------------------------------------------------------------------- -- first, grab the manager -- https://github.com/folke/lazy.nvim local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" if not vim.loop.fs_stat(lazypath) then vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", "--branch=stable", -- latest stable release lazypath, }) end vim.opt.rtp:prepend(lazypath) -- then, setup! require("lazy").setup({ -- main color scheme -- nice bar at the bottom { "wincent/base16-nvim", lazy = false, -- load at start priority = 1000, -- load first config = function() vim.cmd([[colorscheme gruvbox-dark-hard]]) vim.o.background = 'dark' vim.cmd([[hi Normal ctermbg=NONE]]) vim.api.nvim_set_hl(0, "WinSeparator", { fg = 1250067 }) -- Make comments more prominent -- they are important. local bools = vim.api.nvim_get_hl(0, { name = 'Boolean' }) vim.api.nvim_set_hl(0, 'Comment', bools) -- Make it clearly visible which argument we're at. local marked = vim.api.nvim_get_hl(0, { name = 'PMenu' }) vim.api.nvim_set_hl(0, 'LspSignatureActiveParameter', { fg = marked.fg, bg = marked.bg, ctermfg = marked.ctermfg, ctermbg = marked.ctermbg, bold = true }) -- XXX -- Would be nice to customize the highlighting of warnings and the like to make -- them less glaring. But alas -- https://github.com/nvim-lua/lsp_extensions.nvim/issues/21 -- call Base16hi("CocHintSign", g:base16_gui03, "", g:base16_cterm03, "", "", "") end }, -- nice bar at the bottom { 'itchyny/lightline.vim', lazy = false, -- also load at start since it's UI config = function() -- no need to also show mode in cmd line when we have bar vim.o.showmode = false vim.g.lightline = { active = { left = { { 'mode', 'paste' }, { 'readonly', 'filename', 'modified' } }, right = { { 'lineinfo' }, { 'percent' }, { 'fileencoding', 'filetype' } }, }, component_function = { filename = 'LightlineFilename' }, } function LightlineFilenameInLua(opts) if vim.fn.expand('%:t') == '' then return '[No Name]' else return vim.fn.getreg('%') end end -- https://github.com/itchyny/lightline.vim/issues/657 vim.api.nvim_exec( [[ function! g:LightlineFilename() return v:lua.LightlineFilenameInLua() endfunction ]], true ) end }, -- quick navigation { "https://codeberg.org/andyg/leap.nvim", config = function() vim.keymap.set({ 'n', 'x', 'o' }, 's', '(leap)') vim.keymap.set('n', 'S', '(leap-from-window)') end }, -- better % { 'andymass/vim-matchup', config = function() vim.g.matchup_matchparen_offscreen = { method = "popup" } end }, -- auto-cd to root of git project -- 'airblade/vim-rooter' -- Commented out because I don't like it (arc) - 2024-10-03 -- { -- 'notjedi/nvim-rooter.lua', -- config = function() -- require('nvim-rooter').setup() -- end -- }, -- hopefully fix random crashes? { 'hrsh7th/vim-vsnip' }, { 'ibhagwan/fzf-lua', config = function() require 'fzf-lua'.setup { winopts = { -- stop putting a giant window over my editor split = "belowright 10new", preview = { hidden = true, } }, files = { -- file icons are distracting file_icons = false, -- git icons are nice git_icons = true, -- don't touch anchored search _fzf_nth_devicons = true }, buffers = { file_icons = false, git_icons = true }, fzf_opts = { -- no reverse view ["--layout"] = "default" } } -- when using C-p for quick file open, pass the file list through -- -- https://github.com/jonhoo/proximity-sort -- -- to prefer files closer to the current file. vim.keymap.set('', '', function() opts = {} opts.cmd = 'fd --color=never --hidden --type f --type l --exclude .git' local base = vim.fn.fnamemodify(vim.fn.expand('%'), ':h:.:S') if base ~= '.' then -- if there is no current file, -- proximity-sort can't do its thing opts.cmd = opts.cmd .. (" | proximity-sort %s"):format(vim.fn.shellescape(vim.fn.expand('%'))) end opts.fzf_opts = { ['--scheme'] = 'path', ['--tiebreak'] = 'index', ["--layout"] = "default", } require 'fzf-lua'.files(opts) end) -- use fzf to search buffers as well vim.keymap.set('n', ';', function() require 'fzf-lua'.buffers({ -- just include the paths in the fzf bits, and nothing else -- https://github.com/ibhagwan/fzf-lua/issues/2230#issuecomment-3164258823 fzf_opts = { ["--with-nth"] = "{-3..-2}", ["--nth"] = "-1", ["--delimiter"] = "[:\u{2002}]", ["--header-lines"] = "false", }, header = false, }) end) end }, -- LSP { 'neovim/nvim-lspconfig', config = function() -- Setup language servers. -- Server-specific settings. See `:help lspconfig-setup` vim.lsp.config('rust_analyzer', { settings = { ["rust_analyzer"] = { cargo = { features = "all" }, checkOnSave = { enable = true, }, check = { command = "clippy" }, imports = { group = { enable = false, }, }, completion = { postfix = { -- Commened out to see if I like it more (arc) - 2024-10-03 -- enable = false, }, }, }, }, }) -- Rust vim.lsp.enable('rust_analyzer') -- C/C++ if vim.fn.executable('clangd') == 1 then vim.lsp.enable('clangd') end -- Dafny vim.lsp.enable('dafny') -- Javascript if vim.fn.executable('ts_ls') then vim.lsp.enable("ts_ls") end -- Python if vim.fn.executable('ruff') == 1 then vim.lsp.enable('ruff') end -- Svelte vim.lsp.enable("svelte") -- Lua vim.lsp.enable('lua_ls') -- Global mappings. -- See `:help vim.diagnostic.*` for documentation on any of the below functions vim.keymap.set('n', 'e', vim.diagnostic.open_float) vim.keymap.set('n', '[d', vim.diagnostic.goto_prev) vim.keymap.set('n', ']d', vim.diagnostic.goto_next) vim.keymap.set('n', 'q', vim.diagnostic.setloclist) -- Use LspAttach autocommand to only map the following keys -- after the language server attaches to the current buffer vim.api.nvim_create_autocmd('LspAttach', { group = vim.api.nvim_create_augroup('UserLspConfig', {}), callback = function(ev) -- Enable completion triggered by vim.bo[ev.buf].omnifunc = 'v:lua.vim.lsp.omnifunc' -- Buffer local mappings. -- See `:help vim.lsp.*` for documentation on any of the below functions local opts = { buffer = ev.buf } vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts) vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts) vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts) vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts) vim.keymap.set('n', '', vim.lsp.buf.signature_help, opts) vim.keymap.set('n', 'wa', vim.lsp.buf.add_workspace_folder, opts) vim.keymap.set('n', 'wr', vim.lsp.buf.remove_workspace_folder, opts) vim.keymap.set('n', 'wl', function() print(vim.inspect(vim.lsp.buf.list_workspace_folders())) end, opts) --vim.keymap.set('n', 'D', vim.lsp.buf.type_definition, opts) vim.keymap.set('n', 'r', vim.lsp.buf.rename, opts) vim.keymap.set({ 'n', 'v' }, 'a', vim.lsp.buf.code_action, opts) vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts) vim.keymap.set('n', 'f', function() vim.lsp.buf.format { async = true } end, opts) local client = vim.lsp.get_client_by_id(ev.data.client_id) -- TODO: find some way to make it only apply to the current line. if client.server_capabilities.inlayHintProvider then vim.lsp.inlay_hint.enable(false, { bufnr = bufnr }) end -- None of this semantics tokens business. -- https://www.reddit.com/r/neovim/comments/143efmd/is_it_possible_to_disable_treesitter_completely/ client.server_capabilities.semanticTokensProvider = nil -- format on save for Rust if client.server_capabilities.documentFormattingProvider then vim.api.nvim_create_autocmd("BufWritePre", { group = vim.api.nvim_create_augroup("RustFormat", { clear = true }), buffer = bufnr, callback = function() vim.lsp.buf.format({ bufnr = bufnr }) end }) end end, }) end }, -- LSP-based code-completion { "hrsh7th/nvim-cmp", -- load cmp on InsertEnter event = "InsertEnter", -- these dependencies will only be loaded when cmp loads -- dependencies are always lazy-loaded unless specified otherwise dependencies = { 'neovim/nvim-lspconfig', "hrsh7th/cmp-nvim-lsp", "hrsh7th/cmp-buffer", "hrsh7th/cmp-path", }, config = function() local cmp = require 'cmp' cmp.setup({ snippet = { -- REQUIRED by nvim-cmp. get rid of it once we can expand = function(args) vim.snippet.expand(args.body) end, }, mapping = cmp.mapping.preset.insert({ [''] = cmp.mapping.scroll_docs(-4), [''] = cmp.mapping.scroll_docs(4), [''] = cmp.mapping.complete(), [''] = cmp.mapping.abort(), -- Accept currently selected item. -- Set `select` to `false` to only confirm explicitly selected items. [''] = cmp.mapping.confirm({ select = true }), }), sources = cmp.config.sources({ { name = 'nvim_lsp' }, }, { { name = 'path' }, }), experimental = { ghost_text = true, }, }) -- Enable completing paths in : cmp.setup.cmdline(':', { sources = cmp.config.sources({ { name = 'path' } }) }) end }, -- inline function signatures { "ray-x/lsp_signature.nvim", event = "VeryLazy", opts = {}, config = function(_, opts) -- Get signatures (and _only_ signatures) when in argument lists. require "lsp_signature".setup({ doc_lines = 0, handler_opts = { border = "none" }, }) end }, -- language support -- treesitter { 'nvim-treesitter/nvim-treesitter', lazy = false, build = ':TSUpdate', -- config = function() -- require('nvim-treesitter.configs').setup({ -- highlight = { -- enable = true -- } -- }) -- end }, -- terraform { 'hashivim/vim-terraform', ft = { "hcl" }, }, -- toml 'cespare/vim-toml', -- yaml { "https://tangled.org/cuducos.me/yaml.nvim", ft = { "yaml" }, dependencies = { "nvim-treesitter/nvim-treesitter", }, }, -- rust { 'rust-lang/rust.vim', ft = { "rust" }, config = function() vim.g.rustfmt_autosave = 1 vim.g.rustfmt_emit_files = 1 vim.g.rustfmt_fail_silently = 0 vim.g.rust_clip_command = 'wl-copy' end }, -- fish 'khaveesh/vim-fish-syntax', -- markdown { 'plasticboy/vim-markdown', ft = { "markdown" }, dependencies = { 'godlygeek/tabular', }, config = function() -- never ever fold! vim.g.vim_markdown_folding_disabled = 1 -- support front-matter in .md files vim.g.vim_markdown_frontmatter = 1 -- 'o' on a list item should insert at same level vim.g.vim_markdown_new_list_item_indent = 0 -- don't add bullets when wrapping: -- https://github.com/preservim/vim-markdown/issues/232 vim.g.vim_markdown_auto_insert_bullets = 0 end }, }) -- Manually inject support for Dafny into tree-sitter vim.api.nvim_create_autocmd('FileType', { pattern = { 'dafny' }, callback = function() vim.treesitter.start() end, }) vim.api.nvim_create_autocmd('User', { pattern = 'TSUpdate', callback = function() require('nvim-treesitter.parsers').dafny = { install_info = { url = 'https://github.com/oscar-bender-stone/tree-sitter-dafny', revision = "4a78fb62bbafcaac23fd2cb07ac8063e92c2af51", -- commit hash for revision to check out; HEAD if missing -- optional entries: queries = 'queries/neovim', -- also install queries from given directory }, } end }) -- Hypothetically makes hover work on hover - arc, aug 6, 2025 -- Commented out because it prevents me from looking at errors, aug 7: -- vim.api.nvim_create_autocmd({"CursorHold", "CursorHoldI"}, { -- callback = function() -- -- Check if an LSP client is attached to the current buffer -- if vim.lsp.buf_is_attached(0) then -- vim.lsp.buf.hover() -- end -- end, -- desc = "Show LSP hover on cursor hold", -- }) --[[ leftover things from init.vim that i may still end up wanting " Completion " Better completion " menuone: popup even when there's only one match " noinsert: Do not insert text until a selection is made " noselect: Do not select, force user to select one from the menu set completeopt=menuone,noinsert,noselect " Settings needed for .lvimrc set exrc set secure " Wrapping options set formatoptions=tc " wrap text and comments using textwidth set formatoptions+=r " continue comments when pressing ENTER in I mode set formatoptions+=q " enable formatting of comments with gq set formatoptions+=n " detect lists for formatting set formatoptions+=b " auto-wrap in insert mode, and do not wrap old long lines " s for Rg search noremap s :Rg let g:fzf_layout = { 'down': '~20%' } command! -bang -nargs=* Rg \ call fzf#vim#grep( \ 'rg --column --line-number --no-heading --color=always '.shellescape(), 1, \ 0 ? fzf#vim#with_preview('up:60%') \ : fzf#vim#with_preview('right:50%:hidden', '?'), \ 0) " q shows stats nnoremap q g --]]