neovim-confs/lua/plex/config/keymaps.lua

361 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
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' })
-- go to next diagnostics entry
map('n', '<C-j>', function ()
vim.diagnostic.goto_next()
end, { desc = 'go to next diagnostic'})
map('n', '<C-k>', function ()
vim.diagnostic.goto_prev()
end, { desc = 'go to last diagnostic'})
-- 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')
-- move to spelling mistake
map('n', 'zn', ']s')
map('n', 'zN', '[s')
-- Clipboard
-- ===
-- Yank to system clipboard
map({'n', 'v', 'x'}, '<leader>y', '"+y"', { desc = "yank to system"})
map({'n', 'v', 'x'}, '<leader>yy', '"+yy"', { desc = "yank line to system"})
map({'n', 'v', 'x'}, '<leader>Y', '"+Y"', { desc = "yank to system until line end"})
-- Yank buffer's relative path to clipboard
map('n', '<Leader>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', '<Leader>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<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>jj', ':join<CR>g$', { desc = 'Join lines', silent = true })
map('n', '<leader>jJ', 'k:join<CR>j', { desc = 'Join lines', silent = true })
map('n', '<leader>ss', 'i<CR><ESC>', { desc = 'Split lines', silent = true })
map('n', '<leader>sS', 'i<CR><ESC>kg$', { desc = 'Split 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' })
-- Remove spaces at the end of lines
map('n', '<Leader>fw', '<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>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>tabprevious<CR>', { desc = 'Previous Tab' })
map('n', '<A-k>', '<cmd>tabnext<CR>', { desc = 'Next 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' })
-- 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>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' })