From 457674c5b25b9f4be84f0f0ddf0cea7c1811174a Mon Sep 17 00:00:00 2001 From: Josh Rahm Date: Sun, 18 Oct 2026 15:54:35 -0600 Subject: Redesign my vim config. Now uses Lua primarily Breaks apart the configuration into rc-service style configuration files. Deletes a ton of the cruft with old LSP settings. --- lua/config/00-options.lua | 42 ++++++++ lua/config/01-plugins.lua | 56 +++++++++++ lua/config/02-mappings.lua | 148 +++++++++++++++++++++++++++++ lua/config/03-commands.lua | 85 +++++++++++++++++ lua/config/04-autocmds.lua | 67 +++++++++++++ lua/config/05-lsp.lua | 141 +++++++++++++++++++++++++++ lua/config/99-extregs-config.lua | 17 ++++ lua/config/99-gitsigns-config.lua | 27 ++++++ lua/config/99-hop-config.lua | 2 + lua/config/99-lualine-config.lua | 32 +++++++ lua/config/99-nvim-surround-config.lua | 2 + lua/config/99-nvim-treesitter-config.lua | 86 +++++++++++++++++ lua/config/99-nvim-web-devicons-config.lua | 2 + lua/config/99-telescope-config.lua | 19 ++++ lua/config/99-treesitter-config.lua | 87 +++++++++++++++++ 15 files changed, 813 insertions(+) create mode 100644 lua/config/00-options.lua create mode 100644 lua/config/01-plugins.lua create mode 100644 lua/config/02-mappings.lua create mode 100644 lua/config/03-commands.lua create mode 100644 lua/config/04-autocmds.lua create mode 100644 lua/config/05-lsp.lua create mode 100644 lua/config/99-extregs-config.lua create mode 100644 lua/config/99-gitsigns-config.lua create mode 100644 lua/config/99-hop-config.lua create mode 100644 lua/config/99-lualine-config.lua create mode 100644 lua/config/99-nvim-surround-config.lua create mode 100644 lua/config/99-nvim-treesitter-config.lua create mode 100644 lua/config/99-nvim-web-devicons-config.lua create mode 100644 lua/config/99-telescope-config.lua create mode 100644 lua/config/99-treesitter-config.lua (limited to 'lua/config') diff --git a/lua/config/00-options.lua b/lua/config/00-options.lua new file mode 100644 index 0000000..629c14d --- /dev/null +++ b/lua/config/00-options.lua @@ -0,0 +1,42 @@ +-- Editor options extracted from the original init.vim. +-- Load before plugin setup where possible (especially netrw and mapleader). + +local opt = vim.opt +local g = vim.g + +g.loaded_netrw = 1 +g.loaded_netrwPlugin = 1 +g.mapleader = ' ' + + +-- Display and navigation. +opt.termguicolors = true +opt.number = true +opt.relativenumber = true +opt.wrap = false +opt.scrolloff = 8 +opt.title = true +opt.colorcolumn = '+1' + +-- Indentation and text layout. +opt.shiftwidth = 2 +opt.tabstop = 2 +opt.expandtab = true +opt.textwidth = 80 + +-- Splits and completion. +opt.splitright = true +opt.splitbelow = true +opt.wildmode = { 'longest', 'list', 'full' } +opt.pumheight = 20 +opt.completeopt = { 'menu', 'menuone', 'noselect' } + +-- Performance, responsiveness, and matching pairs. +opt.updatetime = 1000 +opt.matchpairs:append('<:>') + +-- Global configuration for plugins. +g.vsnip_snippet_dir = vim.fn.expand('~/.config/nvim/snippets') +g.vsnip_snippet_dirs = { vim.fn.expand('~/.config/nvim/local/snippets') } +g.jq_highlight_objects = 1 +g.jq_highlight_function_calls = 1 diff --git a/lua/config/01-plugins.lua b/lua/config/01-plugins.lua new file mode 100644 index 0000000..e0acdd0 --- /dev/null +++ b/lua/config/01-plugins.lua @@ -0,0 +1,56 @@ +-- Core plugin list: Neovim 0.12+ built-in package manager. +-- GitHub shorthand (owner/repo) is not supported by vim.pack. +local github = function(repo) + return 'https://github.com/' .. repo .. '.git' +end + +-- Josher projects: local working checkout takes precedence over Git. +-- Change the root on each machine or override this helper locally. +local projects = vim.fn.expand('~/Projects') +local function josher(repo) + local path = projects .. '/' .. repo + if vim.fn.isdirectory(path) == 1 then + -- Place local checkout on runtimepath instead of downloading it. + vim.opt.rtp:prepend(path) + return nil + end + return 'git@git.josher.dev:' .. repo .. '.git' +end + +local plugins = { + -- Searching + github('nvim-lua/plenary.nvim'), + github('nvim-telescope/telescope.nvim'), + + -- Syntax / structural editing + github('nvim-treesitter/nvim-treesitter'), + github('nvim-treesitter/nvim-treesitter-textobjects'), + + -- Editing and navigation + github('lewis6991/gitsigns.nvim'), + github('kylechui/nvim-surround'), + github('smoka7/hop.nvim'), + + -- UI + github('nvim-lualine/lualine.nvim'), + github('nvim-tree/nvim-web-devicons'), + + -- LSP server presets only; native vim.lsp.enable/config is used elsewhere. + github('neovim/nvim-lspconfig'), + + -- Existing personal plugins; delete entries you no longer want. + josher('bulletjava.vim'), + josher('fieldmarshal.vim'), + josher('nvim-color-picker'), + josher('nvim-warp'), + josher('meltdown'), + josher('extregs.nvim'), + -- josher('rneovim-userregs'), -- Optional experimental branch/plugin. +} + +-- Local projects return nil; remove holes before calling vim.pack. +local install = {} +for _, plugin in pairs(plugins) do + if plugin then install[#install + 1] = plugin end +end +vim.pack.add(install) diff --git a/lua/config/02-mappings.lua b/lua/config/02-mappings.lua new file mode 100644 index 0000000..8c4f6e3 --- /dev/null +++ b/lua/config/02-mappings.lua @@ -0,0 +1,148 @@ +-- Global mappings migrated from the original init.vim. +-- Load after setting vim.g.mapleader = " " and after defining required commands. +-- Plugin-owned mappings (LSP, Gitsigns, Tree-sitter, Trouble) remain in their setup files. + +vim.g.mapleader = vim.g.mapleader or " " + +local map = vim.keymap.set +local all = { "n", "x", "s", "o" } -- Vimscript :noremap modes + +local function find_in_dir() + local dir = vim.fn.resolve(vim.fn.expand("%:p:h")) + require("telescope.builtin").find_files({ + search_dirs = { dir }, + path_display = { "shorten" }, + hidden = true, + }) +end + +local function shift_char(delta) + local pos = vim.api.nvim_win_get_cursor(0) + local line = vim.api.nvim_get_current_line() + local col = pos[2] + 1 + local char_index = vim.fn.strchars(line:sub(1, col - 1)) + local char = vim.fn.strcharpart(line, char_index, 1) + if char == "" then return end + local code = vim.fn.char2nr(char) + local replacement = vim.fn.nr2char(code + delta) + vim.api.nvim_set_current_line(line:sub(1, col - 1) .. replacement .. line:sub(col + #char)) +end + +vim.go.operatorfunc = "v:lua.JumpToLastChange" +_G.JumpToLastChange = function() + vim.cmd.normal({ "`[", bang = true }) +end + +map(all, "g.", "set operatorfunc=v:lua.JumpToLastChangeg@", { silent = true }) +map(all, "%", "v") +map(all, "\"", "s") +map(all, "", "sterm") +map(all, "", "sTERM") +map(all, "Y", "y$") +map("x", "ú", ":FormatLines") +map(all, "ú", "lua run_format_code()") +map(all, "p", "(SynStack)") +map(all, "ff", "Telescope find_files") +map(all, "", "Telescope find_files") +map(all, "", "Telescope find_files") +map(all, "", find_in_dir) +map("i", "", "(vsnip-expand)") +map(all, "^", "^zH") +map(all, "to", "Telescope oldfiles") +map(all, "ta", "Telescope autocommands") +map(all, "tbu", "Telescope buffers") +map(all, "T", "Telescope builtin") +map(all, "tcs", "Telescope colorscheme") +map(all, "tch", "Telescope command_history") +map(all, "tco", "Telescope commands") +map(all, "t/", "Telescope current_buffer_fuzzy_find") +map(all, "tcbt", "Telescope current_buffer_tags") +map(all, "td", "Telescope diagnostics") +map(all, "tf", "Telescope find_files") +map(all, "tgb", "Telescope git_branches") +map(all, "tgc", "Telescope git_commits") +map(all, "tgf", "Telescope git_files") +map(all, "tgs", "Telescope git_stash") +map(all, "tgr", "Telescope grep_string") +map(all, "thi", "Telescope highlights") +map(all, "tjl", "Telescope jumplist") +map(all, "tlg", "Telescope live_grep") +map(all, "tll", "Telescope loclist") +map(all, "t[d", "Telescope lsp_definitions") +map(all, "t[s", "Telescope lsp_document_symbols") +map(all, "t[w", "Telescope lsp_dynamic_workspace_symbols") +map(all, "t[i", "Telescope lsp_implementations") +map(all, "t[r", "Telescope lsp_incoming_calls") +map(all, "t[n", "Telescope lsp_outgoing_calls") +map(all, "t[e", "Telescope lsp_references") +map(all, "t[t", "Telescope lsp_type_definitions") +map(all, "t?", "Telescope man_pages") +map(all, "t'", "Telescope marks") +map(all, "tp", "Telescope pickers") +map(all, "tqf", "Telescope quickfix") +map(all, "tqh", "Telescope quickfixhistory") +map(all, "t\"", "Telescope registers") +map(all, "th/", "Telescope search_history") +map(all, "tz", "Telescope spell_suggest") +map(all, "ty", "Telescope symbols") +map(all, "tt", "Telescope tags") +map(all, "tst", "Telescope tagstack") +map(all, "tsit", "Telescope treesitter") +map(all, "tv", "Telescope vim_options") +map("n", "n", "set hlsearchn", { silent = true }) +map("n", "N", "set hlsearchN", { silent = true }) +map(all, "nt", "NvimTreeToggle") +map(all, "", [[:e =escape(expand('%:h'), ' \')/]]) +map(all, ".", "lua require('windownav').goto_terminal()") +map(all, "I", "lua require('windownav').goto_initvim()") +map(all, "'", "lua require('windownav').goto_lastwin()") +map("n", "", "termstartinsert") +map("n", "", "TERMstartinsert") +map("n", "", "") +map("n", "", "Telescope buffers") +map("n", "", "bnext") +map("n", "", "bprev") +map(all, "", "lua vim.diagnostic.open_float()") +map(all, "", "WarpFull") +map(all, "", "WarpGrid") +map("o", "", "vWarpGrid") +map(all, "w", "WarpWords") +map("o", "w", "vWarpWords") +map(all, "", [[/\%V]]) -- Original mapping (encoded as Ctrl-_) +map("n", "œ", "k", { remap = true }) +map("n", "ï", "j", { remap = true }) +map("o", "œ", "k", { remap = true }) +map("o", "ï", "j", { remap = true }) +map("o", "aœ", "ak", { remap = true }) +map("o", "aï", "aj", { remap = true }) +map("o", "iœ", "ik", { remap = true }) +map("o", "iï", "ij", { remap = true }) +map("x", "œ", "k", { remap = true }) +map("x", "ï", "j", { remap = true }) +map(all, "", "TSNodeUnderCursor") +map(all, "", "TSCaptureUnderCursor") +map(all, "Τ", "Inspect") +map(all, "g[r", "Telescope lsp_references path_display={\"shorten\"}") +map(all, "g[d", "Telescope lsp_definitions path_display={\"shorten\"}") +map(all, "g[i", "Telescope lsp_implementations path_display={\"shorten\"}") +map(all, "g[td", "Telescope lsp_type_definitions path_display={\"shorten\"}") +map(all, "g[ci", "Telescope lsp_incoming_calls path_display={\"shorten\"}") +map(all, "g[co", "Telescope lsp_outgoing_calls path_display={\"shorten\"}") +map(all, "g[sd", "Telescope lsp_document_symbols path_display={\"shorten\"}") +map(all, "g[sw", "Telescope lsp_workspace_symbols path_display={\"shorten\"}") +map(all, "g[sdw", "Telescope lsp_dynamic_workspace_symbols path_display={\"shorten\"}") +map(all, "[e", "lua vim.diagnostic.jump({count=-1, severity=vim.diagnostic.severity.ERROR})") +map(all, "]e", "lua vim.diagnostic.jump({count=1, severity=vim.diagnostic.severity.ERROR})") +map(all, "α", function() shift_char(1) end) +map(all, "Α", function() shift_char(-1) end) +map(all, "€", "viba)") +map(all, "", "") +map(all, "<2-X2Mouse>", "") +map(all, "<3-X2Mouse>", "") +map(all, "", "") +map(all, "<2-X1Mouse>", "") +map(all, "<3-X1Mouse>", "") +map(all, "", "") +map(all, "", "Telescope tags") +map(all, "gh", "HopCamelCase") + diff --git a/lua/config/03-commands.lua b/lua/config/03-commands.lua new file mode 100644 index 0000000..f8b0d47 --- /dev/null +++ b/lua/config/03-commands.lua @@ -0,0 +1,85 @@ +-- Commands and command-line abbreviations migrated from the original init.vim. +-- This file intentionally does not register autocommands or keymaps. + +local api = vim.api +local config_dir = vim.fn.stdpath('config') + +-- Edit the new configuration (original commands opened init.vim/local.vim). +api.nvim_create_user_command('Config', function() + vim.cmd.edit(vim.fn.fnameescape(config_dir .. '/init.lua')) +end, { desc = 'Edit Neovim init.lua' }) + +api.nvim_create_user_command('Configl', function() + vim.cmd.edit(vim.fn.fnameescape(config_dir .. '/lua/config/local/00-machine.lua')) +end, { desc = 'Edit machine-local configuration' }) + +-- :W is an easy-to-type alias for :w. Pass through arguments and !. +api.nvim_create_user_command('W', function(opts) + vim.cmd('write' .. (opts.bang and '!' or '') .. (opts.args ~= '' and ' ' .. opts.args or '')) +end, { bang = true, nargs = '*', complete = 'file', desc = 'Write buffer' }) + +-- Open a terminal in the specified directory, defaulting to the current +-- buffer's directory. The original function required a directory argument or +-- a buffer whose name was itself a directory; the default is more useful here. +_G.open_terminal_on_directory = function(dirname) + dirname = dirname or vim.fn.expand('%:p:h') + if vim.fn.isdirectory(dirname) ~= 1 then + vim.notify('Not a directory: ' .. dirname, vim.log.levels.WARN) + return + end + + local shell_cmd = string.format('cd %s && exec "$SHELL"', vim.fn.shellescape(dirname)) + vim.cmd('terminal sh -c ' .. vim.fn.shellescape(shell_cmd)) + vim.cmd.startinsert() + vim.cmd('silent! keepalt file ' .. vim.fn.fnameescape('TERM ' .. dirname)) +end + +api.nvim_create_user_command('TERM', function(opts) + _G.open_terminal_on_directory(opts.args ~= '' and vim.fn.expand(opts.args) or nil) +end, { nargs = '?', complete = 'dir', desc = 'Terminal in current file directory' }) + +-- Kept global because 01-mappings.lua invokes it with :lua run_format_code(). +_G.run_format_code = function() + local clients = vim.lsp.get_clients({ bufnr = 0, method = 'textDocument/formatting' }) + if #clients > 0 then + vim.lsp.buf.format({ async = false }) + elseif vim.fn.exists(':FormatCode') == 2 then + vim.cmd('FormatCode') + else + vim.notify('No LSP formatter or :FormatCode command available', vim.log.levels.WARN) + end +end + +-- Developer conveniences from the original config. +_G.remove_package = function(prefix) + for name in pairs(package.loaded) do + if name:sub(1, #prefix) == prefix then + package.loaded[name] = nil + end + end +end + +_G.reload_package = function(name) + _G.remove_package(name) + return require(name) +end + +-- Original command-line abbreviations. Limit them to a leading command-line +-- token, so they don't unexpectedly expand inside commands or search patterns. +local abbreviations = { + _config = config_dir, + _plugged = vim.fn.stdpath('data') .. '/plugged', -- legacy vim-plug path + _fm = vim.fn.stdpath('data') .. '/plugged/fieldmarshal.vim', -- legacy path + _home = vim.fn.stdpath('data'), + _projects = vim.fn.expand('~/Projects'), +} + +for word, replacement in pairs(abbreviations) do + vim.cmd('cnoreabbrev ' .. word .. ' (getcmdtype() ==# ":" && getcmdline() ==# "' .. word .. '") ? ' .. vim.fn.string(replacement) .. ' : "' .. word .. '"') +end + +vim.cmd([[cnoreabbrev _ (getcmdtype() ==# ':' && getcmdline() ==# '_') ? fnameescape(expand('%:h')) : '_']]) + +-- The old :UpdateInitVimHook belonged to vim-plug and is intentionally omitted. +-- Move the old InitVim/UseTerminal autocommands to a separate 04-autocmds.lua. + diff --git a/lua/config/04-autocmds.lua b/lua/config/04-autocmds.lua new file mode 100644 index 0000000..aff6d27 --- /dev/null +++ b/lua/config/04-autocmds.lua @@ -0,0 +1,67 @@ +-- Autocommands migrated from the original init.vim. +-- Plugin-specific autocommands (LSP, windownav, etc.) stay in their modules. + +local api = vim.api + +local init_group = api.nvim_create_augroup('InitVim', { clear = true }) + +-- Turn search highlighting off when leaving Insert mode; mappings for n/N +-- in 01-mappings.lua re-enable highlighting when navigating results. +api.nvim_create_autocmd('InsertLeave', { + group = init_group, + callback = function() + vim.opt.hlsearch = false + end, + desc = 'Clear search highlighting after insert mode', +}) + +-- Highlight matches whenever entering the / or ? search command line. +api.nvim_create_autocmd('CmdlineEnter', { + group = init_group, + pattern = { '/', '?' }, + callback = function() + vim.opt.hlsearch = true + end, + desc = 'Enable search highlighting on search', +}) + +vim.api.nvim_create_autocmd('TextYankPost', { + group = vim.api.nvim_create_augroup('HighlightYank', { clear = true }), + callback = function() + vim.hl.hl_op({ higroup = 'IncSearch', timeout = 100 }) + end, +}) + +vim.api.nvim_create_autocmd('TextPutPost', { + group = vim.api.nvim_create_augroup('HighlightPut', { clear = true }), + callback = function() + vim.hl.hl_op({ higroup = 'IncSearch', timeout = 100 }) + end, +}) + +api.nvim_create_autocmd({ 'FocusGained', 'BufEnter' }, { + group = init_group, + command = 'checktime', + desc = 'Check for changes to files on disk', +}) + +-- The original UseTerminal group only opened a terminal if the entered buffer +-- *itself* was named after a directory. Guard that explicitly: the new +-- open_terminal_on_directory() helper defaults to the file's parent directory, +-- which would otherwise open a terminal whenever you entered any file. +local terminal_group = api.nvim_create_augroup('UseTerminal', { clear = true }) +api.nvim_create_autocmd({ 'BufNewFile', 'BufEnter' }, { + group = terminal_group, + callback = function(args) + local dirname = api.nvim_buf_get_name(args.buf) + if dirname ~= '' and vim.fn.isdirectory(dirname) == 1 then + -- Defined by 03-commands.lua. Resolve at callback time so our + -- numbered config loader may source this file independently. + if type(_G.open_terminal_on_directory) == 'function' then + _G.open_terminal_on_directory(dirname) + end + end + end, + desc = 'Open a terminal when editing a directory buffer', +}) + diff --git a/lua/config/05-lsp.lua b/lua/config/05-lsp.lua new file mode 100644 index 0000000..64328a9 --- /dev/null +++ b/lua/config/05-lsp.lua @@ -0,0 +1,141 @@ +-- Native LSP and automatic completion for Neovim 0.12/0.13. +-- Requires nvim-lspconfig only for its server presets in runtimepath. +-- No nvim-cmp, cmp-nvim-lsp, lspkind, or vsnip required. + +vim.opt.autocomplete = true +vim.opt.complete = 'o' +vim.opt.completeopt = { 'menu', 'menuone', 'noselect', 'popup' } +vim.opt.shortmess:append('c') + +-- Keep the old LSP bindings; all are buffer-local. +local function map(buf, lhs, rhs, desc) + vim.keymap.set('n', lhs, rhs, { buffer = buf, silent = true, desc = desc }) +end + +local group = vim.api.nvim_create_augroup('UserNativeLsp', { clear = true }) + +vim.api.nvim_create_autocmd('LspAttach', { + group = group, + callback = function(args) + local bufnr = args.buf + local client = vim.lsp.get_client_by_id(args.data.client_id) + if not client then return end + + -- Completion requests are sent automatically while typing, without cmp. + -- C-n/C-p move through suggestions; C-y accepts one; C-e dismisses. + if client:supports_method('textDocument/completion') then + vim.lsp.completion.enable(true, client.id, bufnr, { autotrigger = true }) + end + + map(bufnr, 'rn', vim.lsp.buf.rename, 'LSP rename') + map(bufnr, 'ca', vim.lsp.buf.code_action, 'LSP code action') + map(bufnr, '', vim.lsp.buf.hover, 'LSP hover') + map(bufnr, 'g0', vim.lsp.buf.document_symbol, 'Document symbols') + map(bufnr, 'gW', vim.lsp.buf.workspace_symbol, 'Workspace symbols') + map(bufnr, 'gd', vim.lsp.buf.definition, 'Go to definition') + map(bufnr, 'gD', vim.lsp.buf.declaration, 'Go to declaration') + map(bufnr, 'gi', vim.lsp.buf.implementation, 'Go to implementation') + map(bufnr, 'gr', vim.lsp.buf.references, 'Find references') + map(bufnr, '', vim.lsp.buf.signature_help, 'Signature help') + map(bufnr, 'g', vim.lsp.buf.type_definition, 'Type definition') + map(bufnr, '[d', function() vim.diagnostic.jump({ count = -1 }) end, 'Previous diagnostic') + map(bufnr, ']d', function() vim.diagnostic.jump({ count = 1 }) end, 'Next diagnostic') + + vim.bo[bufnr].omnifunc = 'v:lua.vim.lsp.omnifunc' + vim.opt_local.signcolumn = 'yes' + + if client:supports_method('textDocument/documentHighlight') then + -- Per-buffer autocommands, shared across clients to avoid duplicates. + local hlgroup = vim.api.nvim_create_augroup('UserLspHighlight' .. bufnr, { clear = true }) + vim.api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI' }, { + group = hlgroup, buffer = bufnr, callback = vim.lsp.buf.document_highlight, + }) + vim.api.nvim_create_autocmd({ 'CursorMoved', 'InsertLeave' }, { + group = hlgroup, buffer = bufnr, callback = vim.lsp.buf.clear_references, + }) + end + end, +}) + +vim.diagnostic.config({ + signs = { + text = { + [vim.diagnostic.severity.ERROR] = '✘', + [vim.diagnostic.severity.WARN] = '▲', + [vim.diagnostic.severity.INFO] = '●', + [vim.diagnostic.severity.HINT] = '◆' + } + }, + underline = true, + severity_sort = true, +}) + +-- nvim-lspconfig's modern lsp/*.lua presets supply cmd, filetypes and root detection. +-- Only list your desired servers and their overrides here. +local servers = { + bashls = { filetypes = { 'bash', 'sh' } }, + clangd = { filetypes = { 'c', 'cpp', 'objc', 'objcpp', 'cuda' } }, + mlir = {}, + jqls = {}, + lua_ls = { + settings = { + Lua = { + runtime = { version = 'LuaJIT' }, + workspace = { checkThirdParty = false, library = { vim.env.VIMRUNTIME } }, + }, + }, + }, + ocamllsp = {}, + perlpls = {}, + rust_analyzer = {}, + verible = {}, + vimls = {}, + zls = {}, + texlab = {}, + hls = { settings = { haskell = { plugin = { semanticTokens = { globalOn = true } } } } }, + pylsp = { settings = { pylsp = { plugins = { pycodestyle = { enabled = false } } } } }, +} + +for name, overrides in pairs(servers) do + vim.lsp.config(name, overrides) + vim.lsp.enable(name) +end + +vim.keymap.set('i', '', function() + if vim.fn.pumvisible() == 1 then + return '' + end + return '' +end, { expr = true, desc = 'Accept completion or insert Tab' }) + +local fallbacks = { + Pmenu = 'NormalFloat', + PmenuSel = 'Visual', + PmenuKind = 'Type', + PmenuKindSel = 'Type', + PmenuExtra = 'Comment', + PmenuExtraSel = 'Comment', + PmenuMatch = 'Identifier', + PmenuMatchSel = 'Identifier', + PmenuBorder = 'FloatBorder', +} + +local function apply_fallbacks() + for group, target in pairs(fallbacks) do + local hl = vim.api.nvim_get_hl(0, { name = group, link = true }) + + if vim.tbl_isempty(hl) or hl.link == 'Pmenu' then + vim.api.nvim_set_hl(0, group, { link = target }) + end + end +end + +vim.api.nvim_create_autocmd('ColorScheme', { + callback = apply_fallbacks, +}) + +apply_fallbacks() + +vim.opt.pumheight = 12 +vim.opt.pummaxwidth = 70 +vim.opt.pumblend = 0 diff --git a/lua/config/99-extregs-config.lua b/lua/config/99-extregs-config.lua new file mode 100644 index 0000000..2827091 --- /dev/null +++ b/lua/config/99-extregs-config.lua @@ -0,0 +1,17 @@ +if vim.fn.has('extregs') ~= 1 or not (vim._extregs and vim._extregs.register) then + return false +end + +require('extregs').setup({ + ssh = { + host = "rahm@josher.dev", + } +}) + +vim._extregs.bind('$', 'file.stem') +vim._extregs.bind('&', 'file.backed') +vim._extregs.bind('\\', 'ssh.backed') +vim._extregs.bind('|', 'buffer') +vim._extregs.bind('τ', 'timestamp') +vim._extregs.bind('θ', 'uuid') +vim._extregs.bind(']', 'tmux') diff --git a/lua/config/99-gitsigns-config.lua b/lua/config/99-gitsigns-config.lua new file mode 100644 index 0000000..4687668 --- /dev/null +++ b/lua/config/99-gitsigns-config.lua @@ -0,0 +1,27 @@ +-- Adapted from the original init.vim; mappings are buffer-local. +local gs = require('gitsigns') +gs.setup({ + on_attach = function(bufnr) + local function map(mode, lhs, rhs) + vim.keymap.set(mode, lhs, rhs, { buffer = bufnr, silent = true }) + end + + map({ 'o', 'x' }, 'ih', ':Gitsigns select_hunk') + map('n', ']h', function() + if vim.wo.diff then vim.cmd.normal({ ']c', bang = true }) + else gs.nav_hunk('next') end + end) + map('n', '[h', function() + if vim.wo.diff then vim.cmd.normal({ '[c', bang = true }) + else gs.nav_hunk('prev') end + end) + map('n', 'hs', gs.stage_hunk) + map('n', 'hr', gs.reset_hunk) + map('v', 'hs', function() gs.stage_hunk({ vim.fn.line('.'), vim.fn.line('v') }) end) + map('v', 'hr', function() gs.reset_hunk({ vim.fn.line('.'), vim.fn.line('v') }) end) + map('n', 'hS', gs.stage_buffer) + map('n', 'hR', gs.reset_buffer) + map('n', 'hp', gs.preview_hunk) + map('n', 'hi', gs.preview_hunk_inline) + end, +}) diff --git a/lua/config/99-hop-config.lua b/lua/config/99-hop-config.lua new file mode 100644 index 0000000..91a15f8 --- /dev/null +++ b/lua/config/99-hop-config.lua @@ -0,0 +1,2 @@ +-- Keep Hop's defaults. Your existing Hop mappings live in 01-mappings.lua. +require('hop').setup({}) diff --git a/lua/config/99-lualine-config.lua b/lua/config/99-lualine-config.lua new file mode 100644 index 0000000..37b8821 --- /dev/null +++ b/lua/config/99-lualine-config.lua @@ -0,0 +1,32 @@ +-- Based on the original lua/lualinesetup.lua. +local function clients_lsp() + local clients = vim.lsp.get_clients({ bufnr = 0 }) + if #clients == 0 then return '' end + local names = {} + for _, client in ipairs(clients) do names[#names + 1] = client.name end + return '\u{f085} ' .. table.concat(names, ',') +end + +require('lualine').setup({ + options = { + -- The old 'meltdown' theme needs a lualine/themes/meltdown.lua file. + -- 'auto' works even if that theme is absent. + theme = 'auto', + component_separators = '', + section_separators = { left = '', right = '' }, + }, + sections = { + lualine_a = { 'mode' }, + lualine_b = { 'branch', 'diff', 'diagnostics' }, + lualine_c = { 'filename' }, + lualine_x = {}, + lualine_y = { + { clients_lsp, color = { fg = '#80a0ff' } }, + { 'encoding', color = { fg = '#80a0ff' } }, + { 'fileformat', color = { fg = '#ffb700' } }, + { 'filetype', color = { fg = '#80a0ff' } }, + 'progress', + }, + lualine_z = { 'location' }, + }, +}) diff --git a/lua/config/99-nvim-surround-config.lua b/lua/config/99-nvim-surround-config.lua new file mode 100644 index 0000000..839da9c --- /dev/null +++ b/lua/config/99-nvim-surround-config.lua @@ -0,0 +1,2 @@ +-- Keep nvim-surround defaults as in the original configuration. +require('nvim-surround').setup({}) diff --git a/lua/config/99-nvim-treesitter-config.lua b/lua/config/99-nvim-treesitter-config.lua new file mode 100644 index 0000000..57d84a9 --- /dev/null +++ b/lua/config/99-nvim-treesitter-config.lua @@ -0,0 +1,86 @@ +-- Current nvim-treesitter main branch + nvim-treesitter-textobjects main branch. +-- Requires Neovim >= 0.12, tree-sitter CLI, a C compiler, and both plugins. +-- The old nvim-treesitter.configs module no longer exists on main. + +local ts = require('nvim-treesitter') +local textobjects = require('nvim-treesitter-textobjects') + +-- Install parsers explicitly with :TSInstall or +-- :lua require('nvim-treesitter').install({ 'lua', 'vim', 'c' }) +-- Update installed parsers after plugin updates with :TSUpdate. +-- Unlike the old API, there is no auto_install/sync_install option. + +-- Enable highlighting for buffers with installed parsers, except for large +-- files. Core Neovim supplies highlighting; nvim-treesitter supplies parsers. +local max_filesize = 100 * 1024 +local group = vim.api.nvim_create_augroup('UserTreesitterHighlight', { clear = true }) +vim.api.nvim_create_autocmd('FileType', { + group = group, + callback = function(args) + local buf = args.buf + local name = vim.api.nvim_buf_get_name(buf) + if name ~= '' then + local stat = vim.uv.fs_stat(name) + if stat and stat.size > max_filesize then + return + end + end + -- Don't error for filetypes without an installed parser. + pcall(vim.treesitter.start, buf) + end, +}) + +textobjects.setup({ + select = { + lookahead = true, + selection_modes = { + ['@parameter.outer'] = 'v', + ['@function.outer'] = 'V', + ['@function.inner'] = 'V', + ['@block.inner'] = 'V', + ['@block.outer'] = 'V', + }, + -- Corrected from the original pattern '^function', which could never + -- match a capture name beginning with '@'. + include_surrounding_whitespace = function(q) + return q.query_string:match('%.inner$') == nil + and q.query_string:match('^@function') ~= nil + end, + }, + move = { set_jumps = true }, +}) + +local select = require('nvim-treesitter-textobjects.select') +local move = require('nvim-treesitter-textobjects.move') +local selections = { + am = '@function.outer', im = '@function.inner', + af = '@function.outer', ['if'] = '@function.inner', + aC = '@class.outer', iC = '@class.inner', + aa = '@parameter.outer', ia = '@parameter.inner', + iq = '@block.inner', aq = '@block.outer', + ['a;'] = '@statement.outer', ['i;'] = '@statement.inner', +} +for lhs, query in pairs(selections) do + vim.keymap.set({ 'x', 'o' }, lhs, function() + select.select_textobject(query, 'textobjects') + end, { desc = 'Tree-sitter select ' .. query }) +end + +-- The original mapping references a custom capture; it only works when +-- you provide a matching textobjects query for the current language. +for lhs, query in pairs({ + [']C'] = '@injectable_constructor.params.inner', + [']c'] = '@class.outer', +}) do + vim.keymap.set({ 'n', 'x', 'o' }, lhs, function() + move.goto_next_start(query, 'textobjects') + end) +end +for lhs, query in pairs({ + ['[C'] = '@injectable_constructor.params.inner', + ['[c'] = '@class.outer', +}) do + vim.keymap.set({ 'n', 'x', 'o' }, lhs, function() + move.goto_previous_start(query, 'textobjects') + end) +end diff --git a/lua/config/99-nvim-web-devicons-config.lua b/lua/config/99-nvim-web-devicons-config.lua new file mode 100644 index 0000000..6450096 --- /dev/null +++ b/lua/config/99-nvim-web-devicons-config.lua @@ -0,0 +1,2 @@ +-- Optional: setup isn't required for default devicons behavior. +require('nvim-web-devicons').setup({ default = true }) diff --git a/lua/config/99-telescope-config.lua b/lua/config/99-telescope-config.lua new file mode 100644 index 0000000..054c701 --- /dev/null +++ b/lua/config/99-telescope-config.lua @@ -0,0 +1,19 @@ +-- Telescope configuration migrated from init.vim. +-- Keybindings remain in 01-mappings.lua. +require('telescope').setup({ + defaults = { + title = '', + borderchars = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }, + prompt_prefix = '🞂 ', + selection_caret = '🞂 ', + layout_strategy = 'horizontal', + results_title = false, + sorting_strategy = 'ascending', + layout_config = { + horizontal = { width = 0.66, height = 0.66 }, + }, + }, + pickers = { + find_files = { hidden = true }, + }, +}) diff --git a/lua/config/99-treesitter-config.lua b/lua/config/99-treesitter-config.lua new file mode 100644 index 0000000..5b81c7e --- /dev/null +++ b/lua/config/99-treesitter-config.lua @@ -0,0 +1,87 @@ +-- Current nvim-treesitter main branch + nvim-treesitter-textobjects main branch. +-- Requires Neovim >= 0.12, tree-sitter CLI, a C compiler, and both plugins. +-- The old nvim-treesitter.configs module no longer exists on main. + +local ts = require('nvim-treesitter') +local textobjects = require('nvim-treesitter-textobjects') + +-- Install parsers explicitly with :TSInstall or +-- :lua require('nvim-treesitter').install({ 'lua', 'vim', 'c' }) +-- Update installed parsers after plugin updates with :TSUpdate. +-- Unlike the old API, there is no auto_install/sync_install option. + +-- Enable highlighting for buffers with installed parsers, except for large +-- files. Core Neovim supplies highlighting; nvim-treesitter supplies parsers. +local max_filesize = 100 * 1024 +local group = vim.api.nvim_create_augroup('UserTreesitterHighlight', { clear = true }) +vim.api.nvim_create_autocmd('FileType', { + group = group, + callback = function(args) + local buf = args.buf + local name = vim.api.nvim_buf_get_name(buf) + if name ~= '' then + local stat = vim.uv.fs_stat(name) + if stat and stat.size > max_filesize then + return + end + end + -- Don't error for filetypes without an installed parser. + pcall(vim.treesitter.start, buf) + end, +}) + +textobjects.setup({ + select = { + lookahead = true, + selection_modes = { + ['@parameter.outer'] = 'v', + ['@function.outer'] = 'V', + ['@function.inner'] = 'V', + ['@block.inner'] = 'V', + ['@block.outer'] = 'V', + }, + -- Corrected from the original pattern '^function', which could never + -- match a capture name beginning with '@'. + include_surrounding_whitespace = function(q) + return q.query_string:match('%.inner$') == nil + and q.query_string:match('^@function') ~= nil + end, + }, + move = { set_jumps = true }, +}) + +local select = require('nvim-treesitter-textobjects.select') +local move = require('nvim-treesitter-textobjects.move') +local selections = { + am = '@function.outer', im = '@function.inner', + af = '@function.outer', ['if'] = '@function.inner', + aC = '@class.outer', iC = '@class.inner', + aa = '@parameter.outer', ia = '@parameter.inner', + iq = '@block.inner', aq = '@block.outer', + ['a;'] = '@statement.outer', ['i;'] = '@statement.inner', +} +for lhs, query in pairs(selections) do + vim.keymap.set({ 'x', 'o' }, lhs, function() + select.select_textobject(query, 'textobjects') + end, { desc = 'Tree-sitter select ' .. query }) +end + +-- The original mapping references a custom capture; it only works when +-- you provide a matching textobjects query for the current language. +for lhs, query in pairs({ + [']C'] = '@injectable_constructor.params.inner', + [']c'] = '@class.outer', +}) do + vim.keymap.set({ 'n', 'x', 'o' }, lhs, function() + move.goto_next_start(query, 'textobjects') + end) +end +for lhs, query in pairs({ + ['[C'] = '@injectable_constructor.params.inner', + ['[c'] = '@class.outer', +}) do + vim.keymap.set({ 'n', 'x', 'o' }, lhs, function() + move.goto_previous_start(query, 'textobjects') + end) +end + -- cgit