-- Rafi's Neovim keymaps -- github.com/rafi/vim-config -- === -- This file is automatically loaded by plex.config.init local Util = require('plex.lib.utils') local map = vim.keymap.set local function augroup(name) return vim.api.nvim_create_augroup('plex_' .. name, {}) end -- enable elite mode, for people that are more used to vim than non-vim vim.g.plex_elite_mode = true -- Elite-mode: ctrl + Arrow-keys resize window if vim.g.plex_elite_mode then map('n', '', 'resize +1', { desc = 'Resize Window' }) map('n', '', 'resize -1', { desc = 'Resize Window' }) map('n', '', 'vertical resize +1', { desc = 'Resize Window' }) map('n', '', 'vertical resize -1', { desc = 'Resize Window' }) end -- Package-manager map('n', 'l', 'Lazy', { desc = 'Open Lazy UI' }) -- stylua: ignore start -- Navigation -- === -- Move faster between lines map({ 'n', 'x' }, 'K', "") map({ 'n', 'x' }, 'J', "") -- Easier line-wise movement map({'n', 'v'}, 'H', 'g^') map({'n', 'v'}, 'L', 'g$') -- Toggle fold or select option from popup menu ---@return string map('n', '', function() return vim.fn.pumvisible() == 1 and '' or 'za' end, { expr = true, desc = 'Toggle Fold' }) -- Focus the current fold by closing all others map('n', '', 'zMzv', { remap = true, desc = 'Focus Fold' }) -- Location/quickfix list movement if not Util.has('mini.bracketed') and not Util.has('trouble.nvim') then map('n', ']q', 'cnext', { desc = 'Next Quickfix Item' }) map('n', '[q', 'cprev', { desc = 'Previous Quickfix Item' }) end map('n', '', 'lnext', { desc = 'Next Loclist Item' }) map('n', '', 'lprev', { desc = 'Previous Loclist Item' }) -- Whitespace jump (see plugin/whitespace.vim) map('n', ']s', function() require('plex.lib.edit').whitespace_jump(1) end, { desc = 'Next Whitespace' }) map('n', '[s', function() require('plex.lib.edit').whitespace_jump(-1) end, { desc = 'Previous Whitespace' }) -- Navigation in command line map('c', '', '') map('c', '', '') -- Scroll step sideways map('n', 'zl', 'z4l') map('n', 'zh', 'z4h') -- move to spelling mistake map('n', 'zn', ']s') map('n', 'zN', '[s') -- Clipboard -- === -- Yank to system clipboard map({'n', 'v', 'x'}, 'y', '"+y"', { desc = "yank to system"}) map({'n', 'v', 'x'}, 'yy', '"+yy"', { desc = "yank line to system"}) map({'n', 'v', 'x'}, 'Y', '"+Y"', { desc = "yank to system until line end"}) -- Yank buffer's relative path to clipboard map('n', 'yp', function() local path = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ':~:.') vim.fn.setreg('+', path) vim.notify(path, vim.log.levels.INFO, { title = 'Yanked relative path' }) end, { silent = true, desc = 'Yank relative path' }) -- Yank absolute path map('n', 'Yp', function() local path = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ':p') vim.fn.setreg('+', path) vim.notify(path, vim.log.levels.INFO, { title = 'Yanked absolute path' }) end, { silent = true, desc = 'Yank absolute path' }) -- Paste in visual-mode without pushing to register map('x', 'p', 'p:let @+=@0:let @"=@0', { silent = true, desc = 'Paste' }) map('x', 'P', 'P:let @+=@0:let @"=@0', { silent = true, desc = 'Paste In-place' }) -- Edit -- === -- Macros map('n', '', 'q', { desc = 'Macro Prefix' }) -- Insert new lines above and below map('n', 'oo', 'o', { desc = 'Insert newline below' }) map('n', 'OO', 'O', { desc = 'Insert newline below' }) -- keep regular behavior map('n', 'O', 'O', { desc = 'Insert newline below' }) map('n', 'o', 'o', { desc = 'Insert newline below' }) -- Start new line from any cursor position in insert-mode map('i', '', 'o', { desc = 'Start Newline' }) -- Split and join lines map('n', 'jj', ':joing$', { desc = 'Join lines', silent = true }) map('n', 'jJ', 'k:joinj', { desc = 'Join lines', silent = true }) map('n', 'ss', 'i', { desc = 'Split lines', silent = true }) map('n', 'sS', 'ik', { desc = 'Split lines', silent = true }) -- Re-select blocks after indenting in visual/select mode map('x', '<', '', '>gv|', { desc = 'Indent Left and Re-select' }) -- Arrows to move identation in normal mode map('n', '', '<<', { desc = 'Indent Right and Re-select' }) map('n', '', '>>', { desc = 'Indent Left and Re-select' }) map('v', '', '<', { desc = 'Indent Right and Re-select' }) map('v', '', '>', { desc = 'Indent Left and Re-select' }) -- Use tab for indenting in visual/select mode map('x', '', '>gv|', { desc = 'Indent Left' }) map('x', '', '', 'move-2==', { desc = 'Move line up' }) map('n', '', 'move+==', { desc = 'Move line down' }) map('x', '', ":move'<-2gv=gv", { desc = 'Move selection up' }) map('x', '', ":move'>+gv=gv", { desc = 'Move selection down' }) -- Remove spaces at the end of lines map('n', 'fw', 'lua MiniTrailspace.trim()', { desc = 'Erase Whitespace' }) -- Search & Replace -- === -- Make marks with m[some key] -- go to mark with #[some key] map('n', '#', '\'') -- Clear search with map('n', '', 'noh', { desc = 'Clear Search Highlight' }) -- Clear search, diff update and redraw taken from runtime/lua/_editor.lua map( 'n', 'ur', 'nohlsearchdiffupdatenormal! ', { desc = 'Redraw / clear hlsearch / diff update' } ) -- Use backspace key for matching parens map({ 'n', 'x' }, '', '%', { remap = true, desc = 'Jump to Paren' }) -- Select last paste map('n', 'gpp', "'`['.strpart(getregtype(), 0, 1).'`]'", { expr = true, desc = 'Select Paste' }) -- Quick substitute within selected area map('x', 'Sub', ':s//gc', { desc = 'Substitute Within Selection' }) -- Command & History -- === -- Start an external command with a single bang map('n', '!', ':!', { desc = 'Execute Shell Command' }) -- Put vim command output into buffer --map('n', 'g!', ":put=execute('')", { desc = 'Paste Command' }) -- Allow misspellings --vim.cmd.cnoreabbrev('qw', 'wq') --vim.cmd.cnoreabbrev('Wq', 'wq') --vim.cmd.cnoreabbrev('WQ', 'wq') --vim.cmd.cnoreabbrev('Qa', 'qa') --vim.cmd.cnoreabbrev('Bd', 'bd') --vim.cmd.cnoreabbrev('bD', 'bd') -- File operations -- === -- Switch (window) to the directory of the current opened buffer map('n', 'cd', function() local bufdir = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ':p:h') if bufdir ~= nil and vim.loop.fs_stat(bufdir) then vim.cmd.tcd(bufdir) vim.notify(bufdir) end end, { desc = 'Change Local Directory' }) -- Fast saving from all modes --map('n', 'w', 'write', { desc = 'Save' }) --map({ 'n', 'i', 'v' }, '', 'write', { desc = 'Save' }) -- Editor UI -- === -- Toggle editor's visual effects map('n', 'us', 'setlocal spell!', { desc = 'Toggle Spellcheck' }) map('n', 'ul', 'setlocal nonumber!', { desc = 'Toggle Line Numbers' }) map('n', 'ulr', 'setlocal nornu!', { desc = 'Toggle Relative Line Numbers' }) map('n', 'uo', 'setlocal nolist!', { desc = 'Toggle Whitespace Symbols' }) if vim.lsp.inlay_hint then map('n', 'uh', function() vim.lsp.inlay_hint(0, nil) end, { desc = 'Toggle Inlay Hints' }) end -- Smart wrap toggle (breakindent and colorcolumn toggle as-well) map('n', 'uw', function() vim.opt_local.wrap = not vim.wo.wrap vim.opt_local.breakindent = not vim.wo.breakindent if vim.wo.colorcolumn == '' then vim.opt_local.colorcolumn = tostring(vim.bo.textwidth) else vim.opt_local.colorcolumn = '' end end, { desc = 'Toggle Wrap' }) -- Tabs: Many ways to navigate them map('n', '', 'tabprevious', { desc = 'Previous Tab' }) map('n', '', 'tabnext', { desc = 'Next Tab' }) -- New and Close map('n', '', 'tabnew', { desc = 'New Tab' }) map('n', '', 'tabclose', { desc = 'Close Tab' }) -- Moving tabs map('n', '', '-tabmove', { desc = 'Tab Move Backwards' }) map('n', '', '+tabmove', { desc = 'Tab Move Forwards' }) -- Custom Tools -- === -- Append mode-line to current buffer map('n', 'ml', function() require('plex.lib.edit').append_modeline() end, { desc = 'Append Modeline' }) -- Jump entire buffers throughout jumplist --map('n', 'g', function() -- require('plex.lib.edit').jump_buffer(1) --end, { desc = 'Jump to newer buffer' }) --map('n', 'g', function() -- require('plex.lib.edit').jump_buffer(-1) --end, { desc = 'Jump to older buffer' }) -- Context aware menu. See lua/lib/contextmenu.lua map('n', 'c', function() require('plex.lib.contextmenu').show() end, { desc = 'Content-aware menu' }) -- Lazygit map('n', 'tg', function() Util.float_term({ 'lazygit' }, { cwd = Util.get_root(), esc_esc = false }) end, { desc = 'Lazygit (root dir)' }) -- Floating terminal map('t', '', '', { desc = 'Enter Normal Mode' }) map('t', '', '', { desc = 'Enter Normal Mode' }) map('n', '', function() Util.float_term(nil, { cwd = Util.get_root() }) end, { desc = 'Terminal (root dir)' }) map('t', '', function() Util.float_term(nil, { cwd = Util.get_root() }) end, { desc = 'Terminal (root dir)' }) if vim.fn.has('mac') then -- Open the macOS dictionary on current word map('n', '?', 'silent !open dict://', { desc = 'Dictionary' }) -- Use Marked for real-time Markdown preview -- See: https://marked2app.com/ if vim.fn.executable('/Applications/Marked 2.app') then vim.api.nvim_create_autocmd('FileType', { group = augroup('marked_preview'), pattern = 'markdown', callback = function() local cmd = "silent !open -a Marked\\ 2.app '%:p'" map('n', 'P', cmd, { desc = 'Markdown Preview' }) end, }) end end -- Windows, buffers and tabs -- === -- Ultimatus Quitos -- Use Q to quit whatever if vim.F.if_nil(vim.g.plex_window_q_mapping, true) then vim.api.nvim_create_autocmd({ 'BufWinEnter', 'VimEnter' }, { group = augroup('quit_mapping'), callback = function(event) if vim.bo.buftype == '' and vim.fn.maparg('Q', 'n') == '' then local args = { buffer = event.buf, desc = 'Quit' } map('n', 'Q', 'quit', args) end end, }) end -- Toggle quickfix window map('n', 'q', function() require('plex.lib.edit').toggle_list('quickfix') end, { desc = 'Open Quickfix' }) -- Set locations with diagnostics and open the list. map('n', 'a', function() if vim.bo.filetype ~= 'qf' then vim.diagnostic.setloclist({ open = false }) end require('plex.lib.edit').toggle_list('loclist') end, { desc = 'Open Location List' }) map('n', '%', 'split', { desc = 'Split window horizontally' }) map('n', '"', 'vsplit', { desc = 'Split window vertically' }) map('n', 'wb', 'buffer#', { desc = 'Alternate buffer' }) map('n', 'wc', 'close', { desc = 'Close window' }) map('n', 'wd', 'bdelete', { desc = 'Buffer delete' }) map('n', 'wv', 'split', { desc = 'Split window horizontally' }) map('n', 'wg', 'vsplit', { desc = 'Split window vertically' }) map('n', 'wt', 'tabnew', { desc = 'New tab' }) map('n', 'wtp', 'tabprevious', { desc = 'Previous tab' }) map('n', 'wtn', 'tabnext', { desc = 'Next tab' }) map('n', 'wtc', 'tabclose', { desc = 'Close tab' }) map('n', 'wo', 'only', { desc = 'Close other windows' }) map('n', 'wq', 'quit', { desc = 'Quit' }) map('n', 'wz', 'vertical resize | resize | normal! ze', { desc = 'Maximize' }) map('n', 'wx', function() require('mini.bufremove').delete(0, false) vim.cmd.enew() end, { desc = 'Delete buffer and open new' }) -- Background dark/light toggle map('n', 'uh', function() if vim.o.background == 'dark' then vim.o.background = 'light' else vim.o.background = 'dark' end end, { desc = 'Toggle background dark/light' })