-- Set as the leader key -- See `:help mapleader` -- NOTE: Must happen before plugins are loaded (otherwise wrong leader will be used) vim.g.mapleader = ' ' vim.g.maplocalleader = ' ' -- Core Neovim keymaps (non-plugin) local core_keymaps = { -- Clear highlights on search when pressing in normal mode { mode = 'n', lhs = '', rhs = 'nohlsearch', opts = { desc = 'Clear search highlights' } }, -- Exit terminal mode with a more discoverable shortcut { mode = 't', lhs = '', rhs = '', opts = { desc = 'Exit terminal mode' } }, -- Window navigation (Ctrl + hjkl) { mode = 'n', lhs = '', rhs = '', opts = { desc = 'Focus: Window left' } }, { mode = 'n', lhs = '', rhs = '', opts = { desc = 'Focus: Window right' } }, { mode = 'n', lhs = '', rhs = '', opts = { desc = 'Focus: Window down' } }, { mode = 'n', lhs = '', rhs = '', opts = { desc = 'Focus: Window up' } }, -- Window resizing (Ctrl + Arrow keys) { mode = 'n', lhs = '', rhs = 'resize +2', opts = { desc = 'Window: Increase height' } }, { mode = 'n', lhs = '', rhs = 'resize -2', opts = { desc = 'Window: Decrease height' } }, { mode = 'n', lhs = '', rhs = 'vertical resize -2', opts = { desc = 'Window: Decrease width' } }, { mode = 'n', lhs = '', rhs = 'vertical resize +2', opts = { desc = 'Window: Increase width' } }, -- Move lines up and down (Alt + jk) { mode = 'n', lhs = '', rhs = ':m .+1==', opts = { desc = 'Move line down', silent = true } }, { mode = 'n', lhs = '', rhs = ':m .-2==', opts = { desc = 'Move line up', silent = true } }, { mode = 'v', lhs = '', rhs = ":m '>+1gv=gv", opts = { desc = 'Move selection down', silent = true } }, { mode = 'v', lhs = '', rhs = ":m '<-2gv=gv", opts = { desc = 'Move selection up', silent = true } }, -- Quick save and quit { mode = 'n', lhs = 'w', rhs = 'w', opts = { desc = 'Save file' } }, { mode = 'n', lhs = 'W', rhs = 'wa', opts = { desc = 'Save all files' } }, { mode = 'n', lhs = 'Q', rhs = 'qa', opts = { desc = 'Quit all' } }, } -- LSP keymaps - these will be set up when LSP attaches to a buffer local M = {} function M.setup_lsp_keymaps(bufnr) local keymaps = { -- Go to definitions { mode = 'n', lhs = 'gd', rhs = function() require('telescope.builtin').lsp_definitions() end, opts = { desc = 'LSP: Go to definition' } }, { mode = 'n', lhs = 'gr', rhs = function() require('telescope.builtin').lsp_references() end, opts = { desc = 'LSP: Find references' } }, { mode = 'n', lhs = 'gI', rhs = function() require('telescope.builtin').lsp_implementations() end, opts = { desc = 'LSP: Go to implementation' } }, { mode = 'n', lhs = 'gy', rhs = function() require('telescope.builtin').lsp_type_definitions() end, opts = { desc = 'LSP: Go to type definition' } }, -- Symbol navigation { mode = 'n', lhs = 'ls', rhs = function() require('telescope.builtin').lsp_document_symbols() end, opts = { desc = 'LSP: Document symbols' } }, { mode = 'n', lhs = 'lS', rhs = function() require('telescope.builtin').lsp_dynamic_workspace_symbols() end, opts = { desc = 'LSP: Workspace symbols' } }, -- Code actions { mode = 'n', lhs = 'lr', rhs = vim.lsp.buf.rename, opts = { desc = 'LSP: Rename symbol' } }, { mode = 'n', lhs = 'la', rhs = vim.lsp.buf.code_action, opts = { desc = 'LSP: Code action' } }, { mode = 'n', lhs = 'lf', rhs = function() vim.lsp.buf.format({ async = true }) end, opts = { desc = 'LSP: Format code' } }, -- Documentation { mode = 'n', lhs = 'K', rhs = vim.lsp.buf.hover, opts = { desc = 'LSP: Show documentation' } }, } -- First clear any existing LSP keymaps for this buffer local lsp_maps = { 'gd', 'gr', 'gI', 'gy', 'K', 'ls', 'lS', 'lr', 'la', 'lf' } for _, lhs in ipairs(lsp_maps) do pcall(vim.keymap.del, 'n', lhs, { buffer = bufnr }) end -- Then set our keymaps with buffer local for _, mapping in ipairs(keymaps) do vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, vim.tbl_extend('force', mapping.opts, { buffer = bufnr, replace_keycodes = false, nowait = true, silent = true, })) end end -- Telescope keymaps (all under s for Search) M.telescope_keymaps = { -- Help { mode = 'n', lhs = 'sh', rhs = function() require('telescope.builtin').help_tags() end, opts = { desc = 'Search: Help' } }, { mode = 'n', lhs = 'sk', rhs = function() require('telescope.builtin').keymaps() end, opts = { desc = 'Search: Keymaps' } }, -- Files { mode = 'n', lhs = 'sf', rhs = function() require('telescope.builtin').find_files() end, opts = { desc = 'Search: Files' } }, { mode = 'n', lhs = 'sr', rhs = function() require('telescope.builtin').oldfiles() end, opts = { desc = 'Search: Recent files' } }, -- Text { mode = 'n', lhs = 'sg', rhs = function() require('telescope.builtin').live_grep() end, opts = { desc = 'Search: Text in workspace' } }, { mode = 'n', lhs = 'sw', rhs = function() require('telescope.builtin').grep_string() end, opts = { desc = 'Search: Word under cursor' } }, { mode = 'n', lhs = '/', rhs = function() require('telescope.builtin').current_buffer_fuzzy_find(require('telescope.themes').get_dropdown { winblend = 10, previewer = false, }) end, opts = { desc = 'Search: Text in buffer' } }, { mode = 'n', lhs = 's/', rhs = function() require('telescope.builtin').live_grep { grep_open_files = true, prompt_title = 'Live Grep in Open Files', } end, opts = { desc = 'Search: Text in open files' } }, -- Workspace { mode = 'n', lhs = 'sd', rhs = function() require('telescope.builtin').diagnostics() end, opts = { desc = 'Search: Diagnostics' } }, { mode = 'n', lhs = 'sb', rhs = function() require('telescope.builtin').buffers() end, opts = { desc = 'Search: Buffers' } }, } -- Diagnostic keymaps (navigation with [d and ]d, details with t for trouble) M.diagnostic_keymaps = { -- Navigation { mode = 'n', lhs = '[d', rhs = vim.diagnostic.goto_prev, opts = { desc = 'Diagnostic: Previous' } }, { mode = 'n', lhs = ']d', rhs = vim.diagnostic.goto_next, opts = { desc = 'Diagnostic: Next' } }, -- Viewing diagnostics (using t for Trouble) { mode = 'n', lhs = 'tt', rhs = vim.diagnostic.open_float, opts = { desc = 'Trouble: Show details' } }, { mode = 'n', lhs = 'tl', rhs = vim.diagnostic.setloclist, opts = { desc = 'Trouble: Show list' } }, } -- Database keymaps (all under D for Database, to avoid conflicts with diagnostics) M.dadbod_keymaps = { { mode = 'n', lhs = 'Dt', rhs = 'DBUIToggle', opts = { desc = 'Database: Toggle UI' } }, { mode = 'n', lhs = 'Df', rhs = 'DBUIFindBuffer', opts = { desc = 'Database: Find buffer' } }, { mode = 'n', lhs = 'Dr', rhs = 'DBUIRenameBuffer', opts = { desc = 'Database: Rename buffer' } }, { mode = 'n', lhs = 'Dl', rhs = 'DBUILastQueryInfo', opts = { desc = 'Database: Last query' } }, } -- Session management keymaps (all under m for Memory) M.session_keymaps = { { mode = 'n', lhs = 'mw', rhs = function() require('mini.sessions').write() end, opts = { desc = 'Memory: Write session' } }, { mode = 'n', lhs = 'mr', rhs = function() require('mini.sessions').read() end, opts = { desc = 'Memory: Read session' } }, { mode = 'n', lhs = 'md', rhs = function() require('mini.sessions').delete() end, opts = { desc = 'Memory: Delete session' } }, } -- Setup function for dadbod keymaps function M.setup_dadbod_keymaps() for _, mapping in ipairs(M.dadbod_keymaps) do vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, mapping.opts) end end -- Setup function for session keymaps function M.setup_session_keymaps() -- Session keymaps will be overridden by mini.lua local keymaps = M.session_keymaps or {} for _, mapping in ipairs(keymaps) do vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, mapping.opts) end end -- Scratch buffer keymaps M.scratch_keymaps = { { mode = 'n', lhs = '.', rhs = function() require("snacks").scratch() end, opts = { desc = 'Toggle scratch buffer' } }, { mode = 'n', lhs = 'S', rhs = function() require("snacks").scratch.select() end, opts = { desc = 'Select scratch buffer' } }, { mode = 'n', lhs = 'nh', rhs = function() require("snacks.notifier").show_history() end, opts = { desc = 'Show notification history' } }, } -- Git signs keymaps (all under g for Git) M.gitsigns_keymaps = { -- Navigation { mode = 'n', lhs = ']c', rhs = function() if vim.wo.diff then return ']c' end vim.schedule(function() require('gitsigns').next_hunk() end) return '' end, opts = { desc = 'Git: Next hunk', expr = true } }, { mode = 'n', lhs = '[c', rhs = function() if vim.wo.diff then return '[c' end vim.schedule(function() require('gitsigns').prev_hunk() end) return '' end, opts = { desc = 'Git: Previous hunk', expr = true } }, -- Actions { mode = 'n', lhs = 'gh', rhs = function() require('gitsigns').preview_hunk() end, opts = { desc = 'Git: Preview hunk' } }, { mode = 'n', lhs = 'gb', rhs = function() require('gitsigns').blame_line() end, opts = { desc = 'Git: Blame line' } }, { mode = 'n', lhs = 'gd', rhs = function() require('gitsigns').diffthis() end, opts = { desc = 'Git: Show diff' } }, { mode = { 'n', 'v' }, lhs = 'gs', rhs = function() require('gitsigns').stage_hunk() end, opts = { desc = 'Git: Stage hunk' } }, { mode = { 'n', 'v' }, lhs = 'gr', rhs = function() require('gitsigns').reset_hunk() end, opts = { desc = 'Git: Reset hunk' } }, { mode = 'n', lhs = 'gS', rhs = function() require('gitsigns').stage_buffer() end, opts = { desc = 'Git: Stage buffer' } }, { mode = 'n', lhs = 'gu', rhs = function() require('gitsigns').undo_stage_hunk() end, opts = { desc = 'Git: Undo stage' } }, { mode = 'n', lhs = 'gR', rhs = function() require('gitsigns').reset_buffer() end, opts = { desc = 'Git: Reset buffer' } }, } -- Setup function for git signs keymaps function M.setup_gitsigns_keymaps() for _, mapping in ipairs(M.gitsigns_keymaps or {}) do vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, mapping.opts) end end -- Leap keymaps M.leap_keymaps = { -- 's' for bidirectional search (both forward and backward) { mode = { 'n', 'x', 'o' }, lhs = 's', rhs = function() require('leap').leap {} end, opts = { desc = 'Leap: Search bidirectional' } }, -- 'S' for searching in all windows { mode = { 'n', 'x', 'o' }, lhs = 'S', rhs = function() require('leap').leap { target_windows = vim.tbl_filter( function (win) return vim.api.nvim_win_get_config(win).focusable end, vim.api.nvim_tabpage_list_wins(0) )} end, opts = { desc = 'Leap: Search across windows' } }, } -- Setup function for leap keymaps function M.setup_leap_keymaps() for _, mapping in ipairs(M.leap_keymaps) do vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, mapping.opts) end end -- Initialize keymaps local function init_keymaps() -- Create an autocmd group for our keymaps local keymap_group = vim.api.nvim_create_augroup('custom_keymaps', { clear = true }) -- Function to set up snacks explorer keymaps local function setup_explorer_keymaps() -- First remove any existing mappings for _, key in ipairs({ 'e', 'E' }) do pcall(vim.keymap.del, 'n', key) pcall(vim.keymap.del, 'n', key, { buffer = true }) end -- Set our mappings with high priority for _, mapping in ipairs(M.snacks_keymaps or {}) do if mapping.lhs == 'e' or mapping.lhs == 'E' then vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, vim.tbl_extend('force', mapping.opts, { replace_keycodes = false, nowait = true, silent = true, })) end end end -- Set up autocmds to maintain our keymaps vim.api.nvim_create_autocmd({ 'VimEnter', 'BufEnter' }, { group = keymap_group, callback = setup_explorer_keymaps, }) -- Set up LSP keymaps on attach vim.api.nvim_create_autocmd('LspAttach', { group = keymap_group, callback = function(args) M.setup_lsp_keymaps(args.buf) end, }) -- Set up core keymaps for _, mapping in ipairs(core_keymaps) do vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, mapping.opts) end -- Set up plugin keymaps for _, mapping in ipairs(M.telescope_keymaps or {}) do vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, mapping.opts) end for _, mapping in ipairs(M.diagnostic_keymaps or {}) do vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, mapping.opts) end for _, mapping in ipairs(M.buffer_keymaps or {}) do vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, mapping.opts) end -- Set up snacks keymaps for _, mapping in ipairs(M.snacks_keymaps or {}) do -- Skip explorer keymaps as they're handled by setup_explorer_keymaps if mapping.lhs ~= 'e' and mapping.lhs ~= 'E' then vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, mapping.opts) end end -- Set up scratch buffer keymaps for _, mapping in ipairs(M.scratch_keymaps or {}) do vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, mapping.opts) end -- Set up database keymaps M.setup_dadbod_keymaps() -- Set up session keymaps M.setup_session_keymaps() -- Set up session keymaps for _, mapping in ipairs(M.session_keymaps) do vim.keymap.set(mapping.mode, mapping.lhs, mapping.rhs, mapping.opts) end -- Set up git signs keymaps M.setup_gitsigns_keymaps() -- Set up leap keymaps M.setup_leap_keymaps() end -- Initialize all keymaps init_keymaps() -- Return the module return M