358 lines
12 KiB
Lua
358 lines
12 KiB
Lua
-- 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', '<C-Up>', '<cmd>resize +1<cr>', { desc = 'Resize Window' })
|
|
map('n', '<C-Down>', '<cmd>resize -1<cr>', { desc = 'Resize Window' })
|
|
map('n', '<C-Left>', '<cmd>vertical resize +1<cr>', { desc = 'Resize Window' })
|
|
map('n', '<C-Right>', '<cmd>vertical resize -1<cr>', { desc = 'Resize Window' })
|
|
end
|
|
|
|
-- Package-manager
|
|
map('n', '<leader>l', '<cmd>Lazy<cr>', { desc = 'Open Lazy UI' })
|
|
|
|
-- stylua: ignore start
|
|
|
|
-- Navigation
|
|
-- ===
|
|
|
|
-- Move faster between lines
|
|
-- See vim-smoothie
|
|
--map({ 'n', 'x' }, 'K', "<C-u>")
|
|
--map({ 'n', 'x' }, 'J', "<C-d>")
|
|
|
|
-- 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', '<CR>', function()
|
|
return vim.fn.pumvisible() == 1 and '<CR>' or 'za'
|
|
end, { expr = true, desc = 'Toggle Fold' })
|
|
|
|
-- Focus the current fold by closing all others
|
|
map('n', '<S-Return>', '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', '<cmd>cnext<CR>', { desc = 'Next Quickfix Item' })
|
|
map('n', '[q', '<cmd>cprev<CR>', { desc = 'Previous Quickfix Item' })
|
|
end
|
|
map('n', '<A-k>', '<cmd>lnext<CR>', { desc = 'Next Loclist Item' })
|
|
map('n', '<A-j>', '<cmd>lprev<CR>', { 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', '<C-h>', '<Home>')
|
|
map('c', '<C-l>', '<End>')
|
|
|
|
-- Scroll step sideways
|
|
map('n', 'zl', 'z4l')
|
|
map('n', 'zh', 'z4h')
|
|
|
|
-- Clipboard
|
|
-- ===
|
|
|
|
-- TODO: make <leader>y copy to system
|
|
|
|
-- Yank buffer's relative path to clipboard
|
|
map('n', '<Leader>y', 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', '<Leader>Y', 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<CR>:let @"=@0<CR>', { silent = true, desc = 'Paste' })
|
|
map('x', 'P', 'P:let @+=@0<CR>:let @"=@0<CR>', { silent = true, desc = 'Paste In-place' })
|
|
|
|
-- Edit
|
|
-- ===
|
|
|
|
-- Macros
|
|
map('n', '<C-q>', 'q', { desc = 'Macro Prefix' })
|
|
|
|
-- Insert new lines above and below
|
|
map('n', 'oo', 'o<ESC>', { desc = 'Insert newline below' })
|
|
map('n', 'OO', 'O<ESC>', { 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', '<S-Return>', '<C-o>o', { desc = 'Start Newline' })
|
|
|
|
-- Split and join lines
|
|
map('n', '<leader>j', ':join<CR>', { desc = 'Join lines', silent = true })
|
|
map('n', '<leader>s', 'i<CR><ESC>k', { desc = 'Join lines', silent = true })
|
|
|
|
-- Re-select blocks after indenting in visual/select mode
|
|
map('x', '<', '<gv', { desc = 'Indent Right and Re-select' })
|
|
map('x', '>', '>gv|', { desc = 'Indent Left and Re-select' })
|
|
|
|
-- Arrows to move identation in normal mode
|
|
map('n', '<LEFT>', '<<', { desc = 'Indent Right and Re-select' })
|
|
map('n', '<RIGHT>', '>>', { desc = 'Indent Left and Re-select' })
|
|
map('v', '<LEFT>', '<', { desc = 'Indent Right and Re-select' })
|
|
map('v', '<RIGHT>', '>', { desc = 'Indent Left and Re-select' })
|
|
|
|
-- Use tab for indenting in visual/select mode
|
|
map('x', '<Tab>', '>gv|', { desc = 'Indent Left' })
|
|
map('x', '<S-Tab>', '<gv', { desc = 'Indent Right' })
|
|
|
|
-- Drag current line/s vertically and auto-indent with Arrow up/down
|
|
map('n', '<UP>', '<cmd>move-2<CR>==', { desc = 'Move line up' })
|
|
map('n', '<DOWN>', '<cmd>move+<CR>==', { desc = 'Move line down' })
|
|
map('x', '<UP>', ":move'<-2<CR>gv=gv", { desc = 'Move selection up' })
|
|
map('x', '<DOWN>', ":move'>+<CR>gv=gv", { desc = 'Move selection down' })
|
|
|
|
-- Duplicate lines without affecting PRIMARY and CLIPBOARD selections.
|
|
map('n', 'yyp', 'm`""Y""P``', { desc = 'Duplicate line' })
|
|
map('x', 'yyp', '""Y""Pgv', { desc = 'Duplicate selection' })
|
|
|
|
-- Remove spaces at the end of lines
|
|
map('n', '<Leader>cw', '<cmd>lua MiniTrailspace.trim()<CR>', { desc = 'Erase Whitespace' })
|
|
|
|
-- Search & Replace
|
|
-- ===
|
|
|
|
-- Make marks with m[some key]
|
|
-- go to mark with #[some key]
|
|
map('n', '#', '\'')
|
|
|
|
-- Clear search with <Esc>
|
|
map('n', '<Esc>', '<cmd>noh<CR>', { desc = 'Clear Search Highlight' })
|
|
|
|
-- Clear search, diff update and redraw taken from runtime/lua/_editor.lua
|
|
map(
|
|
'n',
|
|
'<leader>ur',
|
|
'<cmd>nohlsearch<bar>diffupdate<bar>normal! <C-L><CR>',
|
|
{ desc = 'Redraw / clear hlsearch / diff update' }
|
|
)
|
|
|
|
-- Use backspace key for matching parens
|
|
map({ 'n', 'x' }, '<BS>', '%', { 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', '<leader>Sub', ':s//gc<Left><Left><Left>', { 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('')<Left><Left>", { 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', '<Leader>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', '<Leader>w', '<cmd>write<CR>', { desc = 'Save' })
|
|
--map({ 'n', 'i', 'v' }, '<C-s>', '<cmd>write<CR>', { desc = 'Save' })
|
|
|
|
-- Editor UI
|
|
-- ===
|
|
|
|
-- Toggle editor's visual effects
|
|
map('n', '<leader>uf', require('plex.plugins.lsp.format').toggle, { desc = 'Toggle format on Save' })
|
|
map('n', '<Leader>us', '<cmd>setlocal spell!<CR>', { desc = 'Toggle Spellcheck' })
|
|
map('n', '<Leader>ul', '<cmd>setlocal nonumber!<CR>', { desc = 'Toggle Line Numbers' })
|
|
map('n', '<Leader>ulr', '<cmd>setlocal nornu!<CR>', { desc = 'Toggle Relative Line Numbers' })
|
|
map('n', '<Leader>uo', '<cmd>setlocal nolist!<CR>', { desc = 'Toggle Whitespace Symbols' })
|
|
|
|
if vim.lsp.inlay_hint then
|
|
map('n', '<leader>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', '<Leader>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', '<A-j>', '<cmd>tabnext<CR>', { desc = 'Next Tab' })
|
|
map('n', '<A-k>', '<cmd>tabprevious<CR>', { desc = 'Previous Tab' })
|
|
|
|
-- New and Close
|
|
map('n', '<A-t>', '<cmd>tabnew<CR>', { desc = 'New Tab' })
|
|
map('n', '<A-c>', '<cmd>tabclose<CR>', { desc = 'Close Tab' })
|
|
|
|
-- Moving tabs
|
|
map('n', '<A-S-j>', '<cmd>-tabmove<CR>', { desc = 'Tab Move Backwards' })
|
|
map('n', '<A-S-k>', '<cmd>+tabmove<CR>', { desc = 'Tab Move Forwards' })
|
|
|
|
-- Show treesitter nodes under cursor
|
|
-- highlights under cursor
|
|
if vim.fn.has('nvim-0.9') == 1 then
|
|
map('n', '<Leader>ui', vim.show_pos, { desc = 'Show Treesitter Node' })
|
|
end
|
|
|
|
-- Custom Tools
|
|
-- ===
|
|
|
|
-- Append mode-line to current buffer
|
|
map('n', '<Leader>ml', function()
|
|
require('plex.lib.edit').append_modeline()
|
|
end, { desc = 'Append Modeline' })
|
|
|
|
-- Jump entire buffers throughout jumplist
|
|
--map('n', 'g<C-i>', function()
|
|
-- require('plex.lib.edit').jump_buffer(1)
|
|
--end, { desc = 'Jump to newer buffer' })
|
|
--map('n', 'g<C-o>', function()
|
|
-- require('plex.lib.edit').jump_buffer(-1)
|
|
--end, { desc = 'Jump to older buffer' })
|
|
|
|
-- Context aware menu. See lua/lib/contextmenu.lua
|
|
map('n', '<LocalLeader>c', function()
|
|
require('plex.lib.contextmenu').show()
|
|
end, { desc = 'Content-aware menu' })
|
|
|
|
-- Lazygit
|
|
map('n', '<leader>gg', function() Util.float_term({ 'lazygit' }, { cwd = Util.get_root(), esc_esc = false }) end, { desc = 'Lazygit (root dir)' })
|
|
map('n', '<leader>tg', function() Util.float_term({ 'lazygit' }, { cwd = Util.get_root(), esc_esc = false }) end, { desc = 'Lazygit (root dir)' })
|
|
|
|
-- Floating terminal
|
|
map('t', '<Esc><Esc>', '<C-\\><C-n>', { desc = 'Enter Normal Mode' })
|
|
map('t', '<C-w>', '<C-\\><C-n>', { desc = 'Enter Normal Mode' })
|
|
map('n', '<F12>', function() Util.float_term(nil, { cwd = Util.get_root() }) end, { desc = 'Terminal (root dir)' })
|
|
map('t', '<F12>', 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', '<Leader>?', '<cmd>silent !open dict://<cword><CR>', { 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 = "<cmd>silent !open -a Marked\\ 2.app '%:p'<CR>"
|
|
map('n', '<Leader>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', '<cmd>quit<CR>', args)
|
|
end
|
|
end,
|
|
})
|
|
end
|
|
|
|
-- Toggle quickfix window
|
|
map('n', '<Leader>q', function()
|
|
require('plex.lib.edit').toggle_list('quickfix')
|
|
end, { desc = 'Open Quickfix' })
|
|
|
|
-- Set locations with diagnostics and open the list.
|
|
map('n', '<Leader>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', '<leader>%', '<cmd>split<CR>', { desc = 'Split window horizontally' })
|
|
map('n', '<leader>"', '<cmd>vsplit<CR>', { desc = 'Split window vertically' })
|
|
|
|
map('n', '<leader>wb', '<cmd>buffer#<CR>', { desc = 'Alternate buffer' })
|
|
map('n', '<leader>wc', '<cmd>close<CR>', { desc = 'Close window' })
|
|
map('n', '<leader>wd', '<cmd>bdelete<CR>', { desc = 'Buffer delete' })
|
|
map('n', '<leader>wv', '<cmd>split<CR>', { desc = 'Split window horizontally' })
|
|
map('n', '<leader>wg', '<cmd>vsplit<CR>', { desc = 'Split window vertically' })
|
|
map('n', '<leader>wt', '<cmd>tabnew<CR>', { desc = 'New tab' })
|
|
map('n', '<leader>wtp', '<cmd>tabprevious<CR>', { desc = 'Previous tab' })
|
|
map('n', '<leader>wtn', '<cmd>tabnext<CR>', { desc = 'Next tab' })
|
|
map('n', '<leader>wtc', '<cmd>tabclose<CR>', { desc = 'Close tab' })
|
|
map('n', '<leader>wo', '<cmd>only<CR>', { desc = 'Close other windows' })
|
|
map('n', '<leader>wq', '<cmd>quit<CR>', { desc = 'Quit' })
|
|
map('n', '<leader>wz', '<cmd>vertical resize | resize | normal! ze<CR>', { desc = 'Maximize' })
|
|
map('n', '<leader>wx', function()
|
|
require('mini.bufremove').delete(0, false)
|
|
vim.cmd.enew()
|
|
end, { desc = 'Delete buffer and open new' })
|
|
|
|
-- Background dark/light toggle
|
|
map('n', '<leader>uh', function()
|
|
if vim.o.background == 'dark' then
|
|
vim.o.background = 'light'
|
|
else
|
|
vim.o.background = 'dark'
|
|
end
|
|
end, { desc = 'Toggle background dark/light' })
|
|
-- vim: set ts=2 sw=2 tw=80 noet :
|