keymaps.lua
1 -- Keymaps are automatically loaded on the VeryLazy event 2 -- Default keymaps that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/keymaps.lua 3 -- Add any additional keymaps here 4 5 -- Wrapper for vim.keymap.set function 6 local function map(mode, lhs, rhs, opts) 7 vim.keymap.set(mode, lhs, rhs, opts) 8 end 9 10 -- Primary Leader keybindings 11 map('n', '<leader><tab><tab>', '<cmd>enew<cr>', { desc = 'New empty buffer' }) 12 map('n', '<leader><tab>v', '<cmd>vnew<cr>', { desc = 'New vertically split pane' }) 13 map('n', '<leader>k', '<cmd>WhichKey<cr>', { desc = 'Whichkey' }) 14 map('n', '<leader>t', '', { desc = 'NeoTest' }) 15 16 -- LazyVim-style find/search (FzfLua) - space+space = find files, leader+ff/fw for files/grep 17 map('n', '<leader><space>', '<cmd>FzfLua files<cr>', { desc = 'Find files (FzfLua)' }) 18 map('n', '<leader>ff', '<cmd>FzfLua files<cr>', { desc = 'Find files' }) 19 map('n', '<leader>fw', '<cmd>FzfLua live_grep<cr>', { desc = 'Find word (live grep)' }) 20 map('n', '<leader>fg', '<cmd>FzfLua live_grep<cr>', { desc = 'Live grep' }) 21 map('n', '<leader>fb', '<cmd>FzfLua buffers<cr>', { desc = 'Find buffers' }) 22 map('n', '<leader>fh', '<cmd>FzfLua helptags<cr>', { desc = 'Find help' }) 23 map('n', '<leader>fk', '<cmd>FzfLua keymaps<cr>', { desc = 'Find keymaps' }) 24 map('n', '<leader>fc', '<cmd>FzfLua lsp_code_actions<cr>', { desc = 'LSP code actions' }) 25 -- Search group: leader+s = search, leader+sk = which-key, leader+s/ = search (grep) 26 map('n', '<leader>s', '<cmd>FzfLua live_grep<cr>', { desc = 'Search in project (grep)' }) 27 map('n', '<leader>sk', '<cmd>WhichKey<cr>', { desc = 'Which key (keymaps)' }) 28 map('n', '<leader>s/', '<cmd>FzfLua live_grep<cr>', { desc = 'Search (grep)' }) 29 map('n', '<leader>sw', '<cmd>FzfLua live_grep<cr>', { desc = 'Search word' }) 30 -- Find and replace in all files (Spectre) 31 map('n', '<leader>sr', function() 32 local ok, spectre = pcall(require, 'spectre') 33 if ok and spectre and spectre.open then 34 spectre.open() 35 end 36 end, { desc = 'Search and replace (Spectre)' }) 37 map('n', '<leader>sR', function() 38 local ok, spectre = pcall(require, 'spectre') 39 if ok and spectre and spectre.open_visual then 40 spectre.open_visual({ select_word = true }) 41 end 42 end, { desc = 'Search replace (word under cursor)' }) 43 map('v', '<leader>sR', function() 44 local ok, spectre = pcall(require, 'spectre') 45 if ok and spectre and spectre.open_visual then 46 spectre.open_visual() 47 end 48 end, { desc = 'Search replace (selection)' }) 49 50 -- Buffers 51 map('n', '<leader>bp', '<cmd>bprevious<cr>', { desc = 'Prev buffer' }) 52 map('n', '<leader>bn', '<cmd>bnext<cr>', { desc = 'Next buffer' }) 53 map('n', '<S-h>', '<cmd>bprevious<cr>', { desc = 'Prev buffer' }) 54 map('n', '<S-l>', '<cmd>bnext<cr>', { desc = 'Next buffer' }) 55 map('n', '[b', '<cmd>bprevious<cr>', { desc = 'Prev buffer' }) 56 map('n', ']b', '<cmd>bnext<cr>', { desc = 'Next buffer' }) 57 map('n', '<leader>bb', '<cmd>e #<cr>', { desc = 'Switch to other buffer' }) 58 map('n', '<leader>bd', '<cmd>bd<cr>', { desc = 'Delete buffer' }) 59 60 -- Quickfix / location list 61 map('n', '<leader>xq', function() 62 if vim.fn.getqflist({ winid = 0 }).winid ~= 0 then 63 vim.cmd.cclose() 64 else 65 vim.cmd.copen() 66 end 67 end, { desc = 'Toggle quickfix list' }) 68 map('n', '<leader>xl', function() 69 local winid = vim.fn.getloclist(0, { winid = 0 }).winid 70 if winid and winid ~= 0 then 71 vim.cmd.lclose() 72 else 73 pcall(vim.cmd.lopen) -- E776 when no location list; avoid error 74 end 75 end, { desc = 'Toggle location list' }) 76 map('n', '[q', '<cmd>cprev<cr>', { desc = 'Previous quickfix' }) 77 map('n', ']q', '<cmd>cnext<cr>', { desc = 'Next quickfix' }) 78 79 -- Diagnostics (vim.diagnostic.jump() + on_jump; goto_next/goto_prev and float= are deprecated in 0.13/0.14) 80 local function diag_jump(count, severity) 81 local opts = { 82 count = count, 83 on_jump = function(_, bufnr) 84 vim.diagnostic.open_float({ bufnr = bufnr, scope = 'cursor', focus = false }) 85 end, 86 } 87 if severity then 88 opts.severity = severity 89 end 90 vim.diagnostic.jump(opts) 91 end 92 map('n', '<leader>cd', vim.diagnostic.open_float, { desc = 'Line diagnostics' }) 93 map('n', ']d', function() 94 diag_jump(1) 95 end, { desc = 'Next diagnostic' }) 96 map('n', '[d', function() 97 diag_jump(-1) 98 end, { desc = 'Prev diagnostic' }) 99 map('n', ']e', function() 100 diag_jump(1, vim.diagnostic.severity.ERROR) 101 end, { desc = 'Next error' }) 102 map('n', '[e', function() 103 diag_jump(-1, vim.diagnostic.severity.ERROR) 104 end, { desc = 'Prev error' }) 105 map('n', ']w', function() 106 diag_jump(1, vim.diagnostic.severity.WARN) 107 end, { desc = 'Next warning' }) 108 map('n', '[w', function() 109 diag_jump(-1, vim.diagnostic.severity.WARN) 110 end, { desc = 'Prev warning' }) 111 112 -- Trouble keymaps are registered in lua/plugins/trouble.lua after the plugin is loaded and set up. 113 114 -- Format (conform) 115 map('n', '<leader>cf', function() 116 local ok, conform = pcall(require, 'conform') 117 if ok and conform and conform.format then 118 conform.format({ async = false }) 119 end 120 end, { desc = 'Format' }) 121 map('x', '<leader>cf', function() 122 local ok, conform = pcall(require, 'conform') 123 if ok and conform and conform.format then 124 conform.format({ async = false }) 125 end 126 end, { desc = 'Format' }) 127 128 -- Windows 129 map('n', '<leader>-', '<cmd>split<cr>', { desc = 'Split below' }) 130 map('n', '<leader>|', '<cmd>vsplit<cr>', { desc = 'Split right' }) 131 map('n', '<leader>wd', '<cmd>close<cr>', { desc = 'Close window' }) 132 -- Ctrl+W + Left/Right/Up/Down: move focus between windows 133 map('n', '<C-w><Left>', '<C-w>h', { desc = 'Focus left window' }) 134 map('n', '<C-w><Right>', '<C-w>l', { desc = 'Focus right window' }) 135 map('n', '<C-w><Up>', '<C-w>k', { desc = 'Focus upper window' }) 136 map('n', '<C-w><Down>', '<C-w>j', { desc = 'Focus lower window' }) 137 -- Ctrl+W + Ctrl+arrow: resize current window 138 map('n', '<C-w><C-Left>', '<cmd>vertical resize -5<cr>', { desc = 'Resize window left' }) 139 map('n', '<C-w><C-Right>', '<cmd>vertical resize +5<cr>', { desc = 'Resize window right' }) 140 map('n', '<C-w><C-Up>', '<cmd>resize +5<cr>', { desc = 'Resize window up' }) 141 map('n', '<C-w><C-Down>', '<cmd>resize -5<cr>', { desc = 'Resize window down' }) 142 143 -- UI Toggles (leader+u): mirrors LazyVim <leader>u* - autoformat flag is checked in conform.lua BufWritePre 144 map('n', '<leader>uf', function() 145 vim.g.autoformat = vim.g.autoformat == false 146 vim.notify('Autoformat (global): ' .. (vim.g.autoformat and 'on' or 'off')) 147 end, { desc = 'Toggle autoformat (global)' }) 148 map('n', '<leader>uF', function() 149 vim.b.autoformat = vim.b.autoformat == false 150 vim.notify('Autoformat (buffer): ' .. (vim.b.autoformat and 'on' or 'off')) 151 end, { desc = 'Toggle autoformat (buffer)' }) 152 map('n', '<leader>us', function() 153 vim.wo.spell = not vim.wo.spell 154 vim.notify('Spell: ' .. (vim.wo.spell and 'on' or 'off')) 155 end, { desc = 'Toggle spell' }) 156 map('n', '<leader>uw', function() 157 vim.wo.wrap = not vim.wo.wrap 158 vim.notify('Wrap: ' .. (vim.wo.wrap and 'on' or 'off')) 159 end, { desc = 'Toggle wrap' }) 160 map('n', '<leader>ul', function() 161 vim.wo.number = not vim.wo.number 162 vim.notify('Line numbers: ' .. (vim.wo.number and 'on' or 'off')) 163 end, { desc = 'Toggle line numbers' }) 164 map('n', '<leader>uL', function() 165 vim.wo.relativenumber = not vim.wo.relativenumber 166 vim.notify('Relative numbers: ' .. (vim.wo.relativenumber and 'on' or 'off')) 167 end, { desc = 'Toggle relative number' }) 168 map('n', '<leader>ud', function() 169 local enabled = vim.diagnostic.is_enabled() 170 vim.diagnostic.enable(not enabled) 171 vim.notify('Diagnostics: ' .. (not enabled and 'on' or 'off')) 172 end, { desc = 'Toggle diagnostics' }) 173 map('n', '<leader>uc', function() 174 vim.wo.conceallevel = vim.wo.conceallevel == 0 and 2 or 0 175 vim.notify('Conceallevel: ' .. vim.wo.conceallevel) 176 end, { desc = 'Toggle conceallevel' }) 177 map('n', '<leader>uh', function() 178 local enabled = vim.lsp.inlay_hint.is_enabled({ bufnr = 0 }) 179 vim.lsp.inlay_hint.enable(not enabled, { bufnr = 0 }) 180 vim.notify('Inlay hints: ' .. (not enabled and 'on' or 'off')) 181 end, { desc = 'Toggle inlay hints' }) 182 map('n', '<leader>uT', function() 183 if vim.b.ts_highlight then 184 vim.treesitter.stop() 185 vim.notify('Treesitter highlight: off') 186 else 187 vim.treesitter.start() 188 vim.notify('Treesitter highlight: on') 189 end 190 end, { desc = 'Toggle treesitter highlight' }) 191 map('n', '<leader>ub', function() 192 vim.o.background = vim.o.background == 'dark' and 'light' or 'dark' 193 vim.notify('Background: ' .. vim.o.background) 194 end, { desc = 'Toggle background' }) 195 map('n', '<leader>ug', function() 196 local ok, ibl = pcall(require, 'ibl') 197 if not ok then 198 return 199 end 200 vim.g.ibl_enabled = vim.g.ibl_enabled == false 201 ibl.update({ enabled = vim.g.ibl_enabled }) 202 vim.notify('Indent guides: ' .. (vim.g.ibl_enabled and 'on' or 'off')) 203 end, { desc = 'Toggle indent guides' }) 204 205 -- Git (leader+g): diffview; gitsigns hunk keys are under \g (buffer-local in git buffers) 206 map('n', '<leader>gh', '<cmd>DiffviewOpen<cr>', { desc = 'Diffview open' }) 207 map('n', '<leader>gc', '<cmd>DiffviewClose<cr>', { desc = 'Diffview close' }) 208 map('n', '<leader>gH', '<cmd>DiffviewFileHistory %<cr>', { desc = 'File history (current)' }) 209 210 -- LSP bindings (K and gd are set buffer-local in LspAttach; these are global fallbacks/alternatives) 211 map('n', '@d', '<cmd>lua vim.lsp.buf.definition()<cr>', { desc = 'lsp buf definition' }) 212 map('n', '@D', '<cmd>lua vim.lsp.buf.declaration()<cr>', { desc = 'lsp buf declaration' }) 213 map('n', '@i', '<cmd>lua vim.lsp.buf.implementation()<cr>', { desc = 'lsp buf implementation' }) 214 map('n', '@o', '<cmd>lua vim.lsp.buf.type_definition()<cr>', { desc = 'lsp type definition' }) 215 map('n', '@r', '<cmd>lua vim.lsp.buf.references()<cr>', { desc = 'lsp buf references' }) 216 map('n', '@s', '<cmd>lua vim.lsp.buf.signature_help()<cr>', { desc = 'lsp buf signature help' }) 217 map('n', '@R', '<cmd>lua vim.lsp.buf.rename()<cr>', { desc = 'lsp buf rename' }) 218 map('n', '@f', '<cmd>lua vim.lsp.buf.format({ async = false })<cr>', { desc = 'lsp buf format' }) 219 map('n', '@a', '<cmd>lua vim.lsp.buf.code_action()<cr>', { desc = 'lsp buf code action' }) 220 221 -- Custom Leaders keybindings 222 map('n', '\\K', '<cmd>Kustomize<cr>', { desc = 'Kustomize' }) 223 224 -- FzfLua 225 map('n', '\\z', '<cmd>FzfLua<cr>', { desc = 'FuzzyLuaFinder' }) 226 map('n', '\\zb', '<cmd>FzfLua buffers<cr>', { desc = 'FuzzyLuaFinder buffers' }) 227 map('n', '\\zg', '<cmd>FzfLua live_grep<cr>', { desc = 'FuzzyLuaFinder livegrep' }) 228 map('n', '\\zf', '<cmd>FzfLua files<cr>', { desc = 'FuzzyLuaFinder files' }) 229 map('n', '\\zk', '<cmd>FzfLua builtin<cr>', { desc = 'Which Key' }) 230 map('n', '\\zc', '<cmd>FzfLua lsp_code_actions<cr>', { desc = 'LSP Code Actions' }) 231 map('n', '\\zt', '<cmd>FzfLua builtin<cr>', { desc = 'Tmux Buffers' }) 232 233 -- GitSigns 234 map('n', '\\g', '', { desc = 'GitSigns' }) 235 map('v', '\\g', '', { desc = 'GitSigns' }) 236 map('n', '\\gtD', '<cmd>diffthis<cr>', { desc = 'Toggle select for diff' }) 237 238 -- Lazygit floating terminal (from git root) 239 map('n', '\\gg', function() 240 local root = vim.fn.systemlist('git -C ' .. vim.fn.shellescape(vim.fn.getcwd()) .. ' rev-parse --show-toplevel')[1] 241 if vim.v.shell_error ~= 0 then 242 root = vim.fn.getcwd() 243 end 244 local buf = vim.api.nvim_create_buf(false, true) 245 local ui = vim.api.nvim_list_uis()[1] 246 local width = math.floor(ui.width * 0.9) 247 local height = math.floor(ui.height * 0.9) 248 local row = math.floor((ui.height - height) / 2) 249 local col = math.floor((ui.width - width) / 2) 250 vim.api.nvim_open_win(buf, true, { 251 relative = 'editor', 252 width = width, 253 height = height, 254 row = row, 255 col = col, 256 style = 'minimal', 257 border = 'rounded', 258 title = ' lazygit ', 259 title_pos = 'center', 260 }) 261 vim.fn.jobstart('lazygit -p ' .. vim.fn.shellescape(root), { 262 term = true, 263 on_exit = function() 264 vim.api.nvim_buf_delete(buf, { force = true }) 265 end, 266 }) 267 vim.cmd('startinsert') 268 end, { desc = 'Lazygit (root)' }) 269 270 -- ModelMate 271 map('n', '\\m', '', { desc = 'ModelMate' }) 272 map('n', '\\mo', '<cmd>ModelLlama<cr>', { desc = 'ModelLlama-chat' }) 273 274 -- CursorAgent 275 map('n', '\\c', '', { desc = 'CursorAgent' }) 276 map('n', '\\ca', '<cmd>CursorOpen<cr>', { desc = 'Open (if needed), switch to Cursor Window' }) 277 map('n', '\\cc', '<cmd>CursorClose<cr>', { desc = 'Close Cursor Window' }) 278 map('n', '\\ct', '<cmd>CursorToggle<cr>', { desc = 'Toggle Cursor Window' }) 279 -- EOF