refactor: move defaults into separate module (#25929)

Move default mappings and autocommands into a separate module and add
comments and docstrings to document each of the defaults.
This commit is contained in:
Gregory Anders 2023-11-08 09:33:37 -06:00 committed by GitHub
parent 1b0fd377ab
commit 08847a9ea1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 184 additions and 158 deletions

View File

@ -0,0 +1,166 @@
--- Default mappings
do
--- Default maps for * and # in visual mode.
---
--- See |v_star-default| and |v_#-default|
do
local function region_chunks(region)
local chunks = {}
local maxcol = vim.v.maxcol
for line, cols in vim.spairs(region) do
local endcol = cols[2] == maxcol and -1 or cols[2]
local chunk = vim.api.nvim_buf_get_text(0, line, cols[1], line, endcol, {})[1]
table.insert(chunks, chunk)
end
return chunks
end
local function _visual_search(cmd)
assert(cmd == '/' or cmd == '?')
local region = vim.region(
0,
'.',
'v',
vim.api.nvim_get_mode().mode:sub(1, 1),
vim.o.selection == 'inclusive'
)
local chunks = region_chunks(region)
local esc_chunks = vim
.iter(chunks)
:map(function(v)
return vim.fn.escape(v, cmd == '/' and [[/\]] or [[?\]])
end)
:totable()
local esc_pat = table.concat(esc_chunks, [[\n]])
local search_cmd = ([[%s\V%s%s]]):format(cmd, esc_pat, '\n')
return '\27' .. search_cmd
end
vim.keymap.set('x', '*', function()
return _visual_search('/')
end, { desc = ':help v_star-default', expr = true, silent = true })
vim.keymap.set('x', '#', function()
return _visual_search('?')
end, { desc = ':help v_#-default', expr = true, silent = true })
end
--- Map Y to y$. This mimics the behavior of D and C. See |Y-default|
vim.keymap.set('n', 'Y', 'y$', { desc = ':help Y-default' })
--- Use normal! <C-L> to prevent inserting raw <C-L> when using i_<C-O>. #17473
---
--- See |CTRL-L-default|
vim.keymap.set('n', '<C-L>', '<Cmd>nohlsearch<Bar>diffupdate<Bar>normal! <C-L><CR>', {
desc = ':help CTRL-L-default',
})
--- Set undo points when deleting text in insert mode.
---
--- See |i_CTRL-U-default| and |i_CTRL-W-default|
vim.keymap.set('i', '<C-U>', '<C-G>u<C-U>', { desc = ':help i_CTRL-U-default' })
vim.keymap.set('i', '<C-W>', '<C-G>u<C-W>', { desc = ':help i_CTRL-W-default' })
--- Use the same flags as the previous substitution with &.
---
--- Use : instead of <Cmd> so that ranges are supported. #19365
---
--- See |&-default|
vim.keymap.set('n', '&', ':&&<CR>', { desc = ':help &-default' })
--- Map |gx| to call |vim.ui.open| on the identifier under the cursor
do
-- TODO: use vim.region() when it lands... #13896 #16843
local function get_visual_selection()
local save_a = vim.fn.getreginfo('a')
vim.cmd([[norm! "ay]])
local selection = vim.fn.getreg('a', 1)
vim.fn.setreg('a', save_a)
return selection
end
local function do_open(uri)
local _, err = vim.ui.open(uri)
if err then
vim.notify(err, vim.log.levels.ERROR)
end
end
local gx_desc =
'Opens filepath or URI under cursor with the system handler (file explorer, web browser, …)'
vim.keymap.set({ 'n' }, 'gx', function()
do_open(vim.fn.expand('<cfile>'))
end, { desc = gx_desc })
vim.keymap.set({ 'x' }, 'gx', function()
do_open(get_visual_selection())
end, { desc = gx_desc })
end
end
--- Default menus
do
--- Right click popup menu
-- TODO VimScript, no l10n
vim.cmd([[
aunmenu *
vnoremenu PopUp.Cut "+x
vnoremenu PopUp.Copy "+y
anoremenu PopUp.Paste "+gP
vnoremenu PopUp.Paste "+P
vnoremenu PopUp.Delete "_x
nnoremenu PopUp.Select\ All ggVG
vnoremenu PopUp.Select\ All gg0oG$
inoremenu PopUp.Select\ All <C-Home><C-O>VG
anoremenu PopUp.-1- <Nop>
anoremenu PopUp.How-to\ disable\ mouse <Cmd>help disable-mouse<CR>
]])
end
--- Default autocommands. See |default-autocmds|
do
local nvim_terminal_augroup = vim.api.nvim_create_augroup('nvim_terminal', {})
vim.api.nvim_create_autocmd('BufReadCmd', {
pattern = 'term://*',
group = nvim_terminal_augroup,
desc = 'Treat term:// buffers as terminal buffers',
nested = true,
command = "if !exists('b:term_title')|call termopen(matchstr(expand(\"<amatch>\"), '\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), {'cwd': expand(get(matchlist(expand(\"<amatch>\"), '\\c\\mterm://\\(.\\{-}\\)//'), 1, ''))})",
})
vim.api.nvim_create_autocmd({ 'TermClose' }, {
group = nvim_terminal_augroup,
desc = 'Automatically close terminal buffers when started with no arguments and exiting without an error',
callback = function(args)
if vim.v.event.status == 0 then
local info = vim.api.nvim_get_chan_info(vim.bo[args.buf].channel)
local argv = info.argv or {}
if #argv == 1 and argv[1] == vim.o.shell then
vim.cmd({ cmd = 'bdelete', args = { args.buf }, bang = true })
end
end
end,
})
vim.api.nvim_create_autocmd('CmdwinEnter', {
pattern = '[:>]',
desc = 'Limit syntax sync to maxlines=1 in the command window',
group = vim.api.nvim_create_augroup('nvim_cmdwin', {}),
command = 'syntax sync minlines=1 maxlines=1',
})
vim.api.nvim_create_autocmd('SwapExists', {
pattern = '*',
desc = 'Skip the swapfile prompt when the swapfile is owned by a running Nvim process',
group = vim.api.nvim_create_augroup('nvim_swapfile', {}),
callback = function()
local info = vim.fn.swapinfo(vim.v.swapname)
local user = vim.uv.os_get_passwd().username
local iswin = 1 == vim.fn.has('win32')
if info.error or info.pid <= 0 or (not iswin and info.user ~= user) then
vim.v.swapchoice = '' -- Show the prompt.
return
end
vim.v.swapchoice = 'e' -- Choose "(E)dit".
vim.notify(('W325: Ignoring swapfile from Nvim process %d'):format(info.pid))
end,
})
end

View File

@ -1031,156 +1031,6 @@ function vim.deprecate(name, alternative, version, plugin, backtrace)
return displayed and msg or nil
end
--- Creates builtin mappings (incl. menus).
--- Called once on startup.
function vim._init_default_mappings()
-- mappings
local function region_chunks(region)
local chunks = {}
local maxcol = vim.v.maxcol
for line, cols in vim.spairs(region) do
local endcol = cols[2] == maxcol and -1 or cols[2]
local chunk = vim.api.nvim_buf_get_text(0, line, cols[1], line, endcol, {})[1]
table.insert(chunks, chunk)
end
return chunks
end
local function _visual_search(cmd)
assert(cmd == '/' or cmd == '?')
local region = vim.region(
0,
'.',
'v',
vim.api.nvim_get_mode().mode:sub(1, 1),
vim.o.selection == 'inclusive'
)
local chunks = region_chunks(region)
local esc_chunks = vim
.iter(chunks)
:map(function(v)
return vim.fn.escape(v, cmd == '/' and [[/\]] or [[?\]])
end)
:totable()
local esc_pat = table.concat(esc_chunks, [[\n]])
local search_cmd = ([[%s\V%s%s]]):format(cmd, esc_pat, '\n')
return '\27' .. search_cmd
end
local function map(mode, lhs, rhs)
vim.keymap.set(mode, lhs, rhs, { desc = 'Nvim builtin' })
end
map('n', 'Y', 'y$')
-- Use normal! <C-L> to prevent inserting raw <C-L> when using i_<C-O>. #17473
map('n', '<C-L>', '<Cmd>nohlsearch<Bar>diffupdate<Bar>normal! <C-L><CR>')
map('i', '<C-U>', '<C-G>u<C-U>')
map('i', '<C-W>', '<C-G>u<C-W>')
vim.keymap.set('x', '*', function()
return _visual_search('/')
end, { desc = ':help v_star-default', expr = true, silent = true })
vim.keymap.set('x', '#', function()
return _visual_search('?')
end, { desc = ':help v_#-default', expr = true, silent = true })
-- Use : instead of <Cmd> so that ranges are supported. #19365
map('n', '&', ':&&<CR>')
-- gx
-- TODO: use vim.region() when it lands... #13896 #16843
local function get_visual_selection()
local save_a = vim.fn.getreginfo('a')
vim.cmd([[norm! "ay]])
local selection = vim.fn.getreg('a', 1)
vim.fn.setreg('a', save_a)
return selection
end
local function do_open(uri)
local _, err = vim.ui.open(uri)
if err then
vim.notify(err, vim.log.levels.ERROR)
end
end
local gx_desc =
'Opens filepath or URI under cursor with the system handler (file explorer, web browser, …)'
vim.keymap.set({ 'n' }, 'gx', function()
do_open(vim.fn.expand('<cfile>'))
end, { desc = gx_desc })
vim.keymap.set({ 'x' }, 'gx', function()
do_open(get_visual_selection())
end, { desc = gx_desc })
-- menus
-- TODO VimScript, no l10n
vim.cmd([[
aunmenu *
vnoremenu PopUp.Cut "+x
vnoremenu PopUp.Copy "+y
anoremenu PopUp.Paste "+gP
vnoremenu PopUp.Paste "+P
vnoremenu PopUp.Delete "_x
nnoremenu PopUp.Select\ All ggVG
vnoremenu PopUp.Select\ All gg0oG$
inoremenu PopUp.Select\ All <C-Home><C-O>VG
anoremenu PopUp.-1- <Nop>
anoremenu PopUp.How-to\ disable\ mouse <Cmd>help disable-mouse<CR>
]])
end
function vim._init_default_autocmds()
local nvim_terminal_augroup = vim.api.nvim_create_augroup('nvim_terminal', {})
vim.api.nvim_create_autocmd({ 'BufReadCmd' }, {
pattern = 'term://*',
group = nvim_terminal_augroup,
nested = true,
command = "if !exists('b:term_title')|call termopen(matchstr(expand(\"<amatch>\"), '\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), {'cwd': expand(get(matchlist(expand(\"<amatch>\"), '\\c\\mterm://\\(.\\{-}\\)//'), 1, ''))})",
})
vim.api.nvim_create_autocmd({ 'TermClose' }, {
group = nvim_terminal_augroup,
desc = 'Automatically close terminal buffers when started with no arguments and exiting without an error',
callback = function(args)
if vim.v.event.status == 0 then
local info = vim.api.nvim_get_chan_info(vim.bo[args.buf].channel)
local argv = info.argv or {}
if #argv == 1 and argv[1] == vim.o.shell then
vim.cmd({ cmd = 'bdelete', args = { args.buf }, bang = true })
end
end
end,
})
vim.api.nvim_create_autocmd({ 'CmdwinEnter' }, {
pattern = '[:>]',
group = vim.api.nvim_create_augroup('nvim_cmdwin', {}),
command = 'syntax sync minlines=1 maxlines=1',
})
vim.api.nvim_create_autocmd({ 'SwapExists' }, {
pattern = '*',
group = vim.api.nvim_create_augroup('nvim_swapfile', {}),
callback = function()
local info = vim.fn.swapinfo(vim.v.swapname)
local user = vim.uv.os_get_passwd().username
local iswin = 1 == vim.fn.has('win32')
if info.error or info.pid <= 0 or (not iswin and info.user ~= user) then
vim.v.swapchoice = '' -- Show the prompt.
return
end
vim.v.swapchoice = 'e' -- Choose "(E)dit".
vim.notify(('W325: Ignoring swapfile from Nvim process %d'):format(info.pid))
end,
})
end
function vim._init_defaults()
vim._init_default_mappings()
vim._init_default_autocmds()
end
require('vim._options')
-- Remove at Nvim 1.0

View File

@ -290,6 +290,7 @@ set(LUA_LOADER_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/loader.lua)
set(LUA_INSPECT_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/inspect.lua)
set(LUA_FS_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/fs.lua)
set(LUA_F_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/F.lua)
set(LUA_DEFAULTS_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/_defaults.lua)
set(LUA_OPTIONS_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/_options.lua)
set(LUA_FILETYPE_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/filetype.lua)
set(LUA_INIT_PACKAGES_MODULE_SOURCE ${NVIM_RUNTIME_DIR}/lua/vim/_init_packages.lua)
@ -535,6 +536,7 @@ add_custom_command(
${LUA_F_MODULE_SOURCE} "vim.F"
${LUA_KEYMAP_MODULE_SOURCE} "vim.keymap"
${LUA_LOADER_MODULE_SOURCE} "vim.loader"
${LUA_DEFAULTS_MODULE_SOURCE} "vim._defaults"
${LUA_OPTIONS_MODULE_SOURCE} "vim._options"
${LUA_SHARED_MODULE_SOURCE} "vim.shared"
DEPENDS
@ -547,6 +549,7 @@ add_custom_command(
${LUA_F_MODULE_SOURCE}
${LUA_KEYMAP_MODULE_SOURCE}
${LUA_LOADER_MODULE_SOURCE}
${LUA_DEFAULTS_MODULE_SOURCE}
${LUA_OPTIONS_MODULE_SOURCE}
${LUA_SHARED_MODULE_SOURCE}
VERBATIM

View File

@ -2291,3 +2291,17 @@ plain:
kv_printf(str, "<Lua %d>", ref);
return str.items;
}
/// Execute the vim._defaults module to set up default mappings and autocommands
void nlua_init_defaults(void)
{
lua_State *const L = global_lstate;
assert(L);
lua_getglobal(L, "require");
lua_pushstring(L, "vim._defaults");
if (nlua_pcall(L, 1, 0)) {
os_errmsg(lua_tostring(L, -1));
os_errmsg("\n");
}
}

View File

@ -413,14 +413,7 @@ int main(int argc, char **argv)
open_script_files(&params);
// Default mappings (incl. menus) & autocommands
Error err = ERROR_INIT;
Object o = NLUA_EXEC_STATIC("return vim._init_defaults()",
(Array)ARRAY_DICT_INIT, &err);
assert(!ERROR_SET(&err));
api_clear_error(&err);
assert(o.type == kObjectTypeNil);
api_free_object(o);
nlua_init_defaults();
TIME_MSG("init default mappings & autocommands");