- Restructure mappings.lua for better organization - Refactor autocmds.lua with improved event handling
174 lines
4.8 KiB
Lua
Executable file
174 lines
4.8 KiB
Lua
Executable file
local function augroup(name)
|
|
return vim.api.nvim_create_augroup('nvim_' .. name, { clear = true })
|
|
end
|
|
|
|
local autocmd = vim.api.nvim_create_autocmd
|
|
|
|
-- Remove trailing whitespace (runs before LSP format)
|
|
autocmd('BufWritePre', {
|
|
group = augroup('trailing_whitespace'),
|
|
pattern = '*',
|
|
command = [[%s/\s\+$//e]],
|
|
desc = 'Remove trailing whitespace on save',
|
|
})
|
|
|
|
-- Ensure file ends with a newline (skip binary/yaml)
|
|
autocmd('BufWritePre', {
|
|
group = augroup('fix_eol'),
|
|
pattern = '*',
|
|
callback = function()
|
|
local ft = vim.bo.filetype
|
|
local skip = { yaml = true, yaml_frontmatter = true }
|
|
if not skip[ft] and not vim.bo.eol and not vim.bo.binary then
|
|
vim.bo.eol = true
|
|
end
|
|
end,
|
|
desc = 'Ensure newline at EOF',
|
|
})
|
|
|
|
-- Auto-format on save via LSP
|
|
autocmd('BufWritePre', {
|
|
group = augroup('format_on_save'),
|
|
pattern = '*',
|
|
callback = function()
|
|
-- Only format if an LSP with formatting support is attached
|
|
local clients = vim.lsp.get_clients({ bufnr = 0 })
|
|
for _, client in ipairs(clients) do
|
|
if client:supports_method('textDocument/formatting', 0) then
|
|
vim.lsp.buf.format({ async = false, id = client.id })
|
|
return
|
|
end
|
|
end
|
|
end,
|
|
desc = 'Auto-format on save with LSP',
|
|
})
|
|
|
|
-- Highlight yanked text
|
|
autocmd('TextYankPost', {
|
|
group = augroup('highlight_yank'),
|
|
callback = function()
|
|
vim.hl.on_yank()
|
|
end,
|
|
desc = 'Highlight yanked text',
|
|
})
|
|
|
|
local function get_project_root()
|
|
local markers = { '.git', '.hg', 'pyproject.toml', 'Cargo.toml', 'go.mod', 'package.json' }
|
|
local buf_dir = vim.fn.expand('%:p:h')
|
|
return vim.fs.root(buf_dir, markers) or buf_dir
|
|
end
|
|
|
|
local function make_relative(paths, root)
|
|
return vim.tbl_map(function(p)
|
|
return vim.fn.fnamemodify(p, ':~:.') -- strip home + make relative to cwd
|
|
end, paths)
|
|
end
|
|
|
|
local function fuzzy_find(text, _)
|
|
local root = get_project_root()
|
|
|
|
if vim.fn.executable('fd') == 1 then
|
|
local result = vim.fn.systemlist(
|
|
string.format(
|
|
'fd --type f --hidden --exclude .git -g "*%s*" %s',
|
|
vim.fn.escape(text, '"\\'),
|
|
vim.fn.shellescape(root)
|
|
)
|
|
)
|
|
if vim.v.shell_error == 0 and #result > 0 then
|
|
return make_relative(result, root)
|
|
end
|
|
end
|
|
|
|
if vim.fn.executable('rg') == 1 then
|
|
local result = vim.fn.systemlist(string.format("rg --files --hidden --glob '!.git' %s", vim.fn.shellescape(root)))
|
|
if vim.v.shell_error == 0 then
|
|
return make_relative(vim.fn.matchfuzzy(result, text), root)
|
|
end
|
|
end
|
|
|
|
local saved = vim.fn.getcwd()
|
|
vim.cmd('lcd ' .. vim.fn.fnameescape(root))
|
|
local files = vim.fn.glob('**/*', false, true)
|
|
vim.cmd('lcd ' .. vim.fn.fnameescape(saved))
|
|
return vim.fn.matchfuzzy(files, text)
|
|
end
|
|
|
|
_G.fuzzy_find = fuzzy_find
|
|
vim.opt.findfunc = 'v:lua.fuzzy_find'
|
|
|
|
-- Also anchor path to project root so gf works correctly
|
|
vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWinEnter' }, {
|
|
callback = function()
|
|
local root = get_project_root()
|
|
if root then
|
|
vim.opt_local.path = { root .. '/**' }
|
|
end
|
|
end,
|
|
desc = 'Anchor path to project root for gf/:find',
|
|
})
|
|
|
|
-- Restore last cursor position when reopening a file
|
|
autocmd('BufReadPost', {
|
|
group = augroup('restore_cursor'),
|
|
callback = function()
|
|
local mark = vim.api.nvim_buf_get_mark(0, '"')
|
|
local line_count = vim.api.nvim_buf_line_count(0)
|
|
if mark[1] > 0 and mark[1] <= line_count then
|
|
pcall(vim.api.nvim_win_set_cursor, 0, mark)
|
|
vim.cmd('normal! zz') -- centre the restored position
|
|
end
|
|
end,
|
|
desc = 'Restore last cursor position on open',
|
|
})
|
|
|
|
-- Keep splits balanced when Neovim is resized
|
|
autocmd('VimResized', {
|
|
group = augroup('resize_splits'),
|
|
callback = function()
|
|
vim.cmd('tabdo wincmd =')
|
|
end,
|
|
desc = 'Equalise splits on terminal resize',
|
|
})
|
|
|
|
vim.opt.wildmode = { 'longest:full', 'full' }
|
|
vim.opt.wildoptions = 'pum'
|
|
|
|
autocmd('CmdlineLeave', {
|
|
group = augroup('cmdline_completion'),
|
|
pattern = ':',
|
|
callback = function()
|
|
vim.opt.wildmode = {}
|
|
end,
|
|
desc = 'Disable wildmenu popup outside cmdline',
|
|
})
|
|
|
|
autocmd('TermOpen', {
|
|
group = augroup('terminal'),
|
|
pattern = '*',
|
|
callback = function()
|
|
vim.opt_local.number = false
|
|
vim.opt_local.relativenumber = false
|
|
vim.opt_local.signcolumn = 'no'
|
|
vim.cmd('startinsert')
|
|
vim.keymap.set('t', '<Esc><Esc>', '<C-\\><C-n>', {
|
|
buffer = true,
|
|
desc = 'Exit terminal mode',
|
|
})
|
|
end,
|
|
desc = 'Terminal: no line numbers, start in insert, bind Esc',
|
|
})
|
|
|
|
autocmd('VimEnter', {
|
|
group = augroup('lazy_autoupdate'),
|
|
callback = function()
|
|
-- Defer so the UI is fully ready before the update check runs
|
|
vim.schedule(function()
|
|
local ok, lazy_status = pcall(require, 'lazy.status')
|
|
if ok and lazy_status.has_updates() then
|
|
require('lazy').update({ show = false })
|
|
end
|
|
end)
|
|
end,
|
|
desc = 'Auto-update plugins on startup if updates are available',
|
|
})
|