refactor: introduce nvim_on internally #39883

Problem:
`nvim_create_autocmd` is too verbose and its `callback` requires extra
"nesting".

Solution:
Introduce `nvim_on`. Start using it internally. Then we can get a feel
for how it should look before making it public.
This commit is contained in:
Justin M. Keyes
2026-05-20 17:33:01 -04:00
committed by GitHub
parent 799cbfff85
commit 9aa4608401
32 changed files with 842 additions and 1040 deletions

View File

@@ -57,6 +57,8 @@
---
--- The following properties are supported by default:
local nvim_on = require('vim._core.util').nvim_on
--- @type table<string,fun(bufnr: integer, val: string, opts?: table)>
local properties = {}
@@ -163,16 +165,12 @@ function properties.trim_trailing_whitespace(bufnr, val)
'trim_trailing_whitespace must be either "true" or "false"'
)
if val == 'true' then
vim.api.nvim_create_autocmd('BufWritePre', {
group = 'nvim.editorconfig',
buf = bufnr,
callback = function()
local view = vim.fn.winsaveview()
vim.api.nvim_command('silent! undojoin')
vim.api.nvim_command('silent keepjumps keeppatterns %s/\\s\\+$//e')
vim.fn.winrestview(view)
end,
})
nvim_on('BufWritePre', 'nvim.editorconfig', { buf = bufnr }, function()
local view = vim.fn.winsaveview()
vim.api.nvim_command('silent! undojoin')
vim.api.nvim_command('silent keepjumps keeppatterns %s/\\s\\+$//e')
vim.fn.winrestview(view)
end)
else
vim.api.nvim_clear_autocmds({
event = 'BufWritePre',
@@ -192,14 +190,9 @@ function properties.insert_final_newline(bufnr, val)
-- so only change 'endofline' right before writing the file
local endofline = val == 'true'
if vim.bo[bufnr].endofline ~= endofline then
vim.api.nvim_create_autocmd('BufWritePre', {
group = 'nvim.editorconfig',
buf = bufnr,
once = true,
callback = function()
vim.bo[bufnr].endofline = endofline
end,
})
nvim_on('BufWritePre', 'nvim.editorconfig', { buf = bufnr, once = true }, function()
vim.bo[bufnr].endofline = endofline
end)
end
end

View File

@@ -1,5 +1,6 @@
local api = vim.api
local fn = vim.fn
local nvim_on = require('vim._core.util').nvim_on
local M = {}
@@ -42,41 +43,21 @@ function M.enable()
local group = api.nvim_create_augroup('matchparen', { clear = true })
-- Replace all matchparen autocommands
api.nvim_create_autocmd({
'CursorMoved',
'CursorMovedI',
'WinEnter',
'WinScrolled',
'TextChanged',
'TextChangedI',
}, {
group = group,
callback = function()
nvim_on(
{ 'CursorMoved', 'CursorMovedI', 'WinEnter', 'WinScrolled', 'TextChanged', 'TextChangedI' },
group,
function()
M.highlight_matching_pair()
end,
})
api.nvim_create_autocmd('BufWinEnter', {
group = group,
callback = function()
api.nvim_create_autocmd('SafeState', {
group = group,
once = true,
callback = function()
M.highlight_matching_pair()
end,
})
end,
})
api.nvim_create_autocmd({
'WinLeave',
'BufLeave',
'TextChangedP',
}, {
group = group,
callback = function()
M.remove_matches()
end,
})
end
)
nvim_on('BufWinEnter', group, function()
nvim_on('SafeState', group, { once = true }, function()
M.highlight_matching_pair()
end)
end)
nvim_on({ 'WinLeave', 'BufLeave', 'TextChangedP' }, group, function()
M.remove_matches()
end)
-- Define commands that will disable and enable the plugin.
api.nvim_create_user_command('DoMatchParen', function()

View File

@@ -1,5 +1,7 @@
-- Default user-commands, autocmds, mappings, menus.
local nvim_on = require('vim._core.util').nvim_on
--- Default user commands
do
vim.api.nvim_create_user_command('Inspect', function(cmd)
@@ -547,15 +549,13 @@ do
end
local nvim_popupmenu_augroup = vim.api.nvim_create_augroup('nvim.popupmenu', {})
vim.api.nvim_create_autocmd('MenuPopup', {
nvim_on('MenuPopup', nvim_popupmenu_augroup, {
pattern = '*',
group = nvim_popupmenu_augroup,
desc = 'Mouse popup menu',
-- nested = true,
callback = function()
enable_ctx_menu()
end,
})
}, function()
enable_ctx_menu()
end)
end
--- Default autocommands. See |default-autocmds|
@@ -564,12 +564,9 @@ do
if vim.v.vim_did_enter then
require('vim._core.log').check_log_file()
else
vim.api.nvim_create_autocmd('VimEnter', {
once = true,
callback = function()
require('vim._core.log').check_log_file()
end,
})
nvim_on('VimEnter', nil, { once = true }, function()
require('vim._core.log').check_log_file()
end)
end
local nvim_terminal_augroup = vim.api.nvim_create_augroup('nvim.terminal', {})
@@ -581,21 +578,19 @@ do
command = "if !exists('b:term_title')|call jobstart(matchstr(expand(\"<amatch>\"), '\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), {'term': v:true, 'cwd': expand(get(matchlist(expand(\"<amatch>\"), '\\c\\mterm://\\(.\\{-}\\)//'), 1, ''))})",
})
vim.api.nvim_create_autocmd({ 'TermClose' }, {
group = nvim_terminal_augroup,
nvim_on({ 'TermClose' }, nvim_terminal_augroup, {
nested = true,
desc = 'Automatically close terminal buffers when started with no arguments and exiting without an error',
callback = function(ev)
if vim.v.event.status ~= 0 then
return
end
local info = vim.api.nvim_get_chan_info(vim.bo[ev.buf].channel)
local argv = info.argv or {}
if table.concat(argv, ' ') == vim.o.shell then
vim.api.nvim_buf_delete(ev.buf, { force = true })
end
end,
})
}, function(ev)
if vim.v.event.status ~= 0 then
return
end
local info = vim.api.nvim_get_chan_info(vim.bo[ev.buf].channel)
local argv = info.argv or {}
if table.concat(argv, ' ') == vim.o.shell then
vim.api.nvim_buf_delete(ev.buf, { force = true })
end
end)
local nvim_terminal_exitmsg_ns = vim.api.nvim_create_namespace('nvim.terminal.exitmsg')
@@ -609,95 +604,82 @@ do
})
end
vim.api.nvim_create_autocmd('TermClose', {
group = nvim_terminal_augroup,
nvim_on('TermClose', nvim_terminal_augroup, {
nested = true,
desc = 'Displays the "[Process exited]" virtual text',
callback = function(ev)
if not vim.api.nvim_buf_is_valid(ev.buf) then
return
end
}, function(ev)
if not vim.api.nvim_buf_is_valid(ev.buf) then
return
end
local buf = vim.bo[ev.buf]
local pos = ev.data.pos ---@type integer
local buf_has_exitmsg = #(
vim.api.nvim_buf_get_extmarks(ev.buf, nvim_terminal_exitmsg_ns, 0, -1, {})
) > 0
local buf = vim.bo[ev.buf]
local pos = ev.data.pos ---@type integer
local buf_has_exitmsg = #(
vim.api.nvim_buf_get_extmarks(ev.buf, nvim_terminal_exitmsg_ns, 0, -1, {})
) > 0
-- `nvim_open_term` buffers do not have an attached 'channel'.
local msg = buf.channel == 0 and '[Terminal closed]'
or ('[Process exited %d]'):format(vim.v.event.status)
-- `nvim_open_term` buffers do not have an attached 'channel'.
local msg = buf.channel == 0 and '[Terminal closed]'
or ('[Process exited %d]'):format(vim.v.event.status)
if buf.buftype ~= 'terminal' or buf_has_exitmsg then
-- TermClose may be queued before TermOpen if process exits before `terminal_open` is called.
-- Don't display the msg now, let TermOpen display it.
vim.api.nvim_create_autocmd('TermOpen', {
buf = ev.buf,
once = true,
callback = function()
set_terminal_exitmsg(ev.buf, msg, pos)
end,
})
return
end
set_terminal_exitmsg(ev.buf, msg, pos)
end,
})
if buf.buftype ~= 'terminal' or buf_has_exitmsg then
-- TermClose may be queued before TermOpen if process exits before `terminal_open` is called.
-- Don't display the msg now, let TermOpen display it.
nvim_on('TermOpen', nil, {
buf = ev.buf,
once = true,
}, function()
set_terminal_exitmsg(ev.buf, msg, pos)
end)
return
end
set_terminal_exitmsg(ev.buf, msg, pos)
end)
vim.api.nvim_create_autocmd('TermRequest', {
group = nvim_terminal_augroup,
nvim_on('TermRequest', nvim_terminal_augroup, {
desc = 'Handles OSC foreground/background color requests',
callback = function(ev)
--- @type integer
local channel = vim.bo[ev.buf].channel
if channel == 0 then
return
}, function(ev)
--- @type integer
local channel = vim.bo[ev.buf].channel
if channel == 0 then
return
end
local fg_request = ev.data.sequence == '\027]10;?'
local bg_request = ev.data.sequence == '\027]11;?'
if fg_request or bg_request then
-- WARN: This does not return the actual foreground/background color,
-- but rather returns:
-- - fg=white/bg=black when Nvim option 'background' is 'dark'
-- - fg=black/bg=white when Nvim option 'background' is 'light'
local red, green, blue = 0, 0, 0
local bg_option_dark = vim.o.background == 'dark'
if (fg_request and bg_option_dark) or (bg_request and not bg_option_dark) then
red, green, blue = 65535, 65535, 65535
end
local fg_request = ev.data.sequence == '\027]10;?'
local bg_request = ev.data.sequence == '\027]11;?'
if fg_request or bg_request then
-- WARN: This does not return the actual foreground/background color,
-- but rather returns:
-- - fg=white/bg=black when Nvim option 'background' is 'dark'
-- - fg=black/bg=white when Nvim option 'background' is 'light'
local red, green, blue = 0, 0, 0
local bg_option_dark = vim.o.background == 'dark'
if (fg_request and bg_option_dark) or (bg_request and not bg_option_dark) then
red, green, blue = 65535, 65535, 65535
end
local command = fg_request and 10 or 11
local data = string.format(
'\027]%d;rgb:%04x/%04x/%04x%s',
command,
red,
green,
blue,
ev.data.terminator
)
vim.api.nvim_chan_send(channel, data)
end
end,
})
local command = fg_request and 10 or 11
local data =
string.format('\027]%d;rgb:%04x/%04x/%04x%s', command, red, green, blue, ev.data.terminator)
vim.api.nvim_chan_send(channel, data)
end
end)
local nvim_terminal_prompt_ns = vim.api.nvim_create_namespace('nvim.terminal.prompt')
vim.api.nvim_create_autocmd('TermRequest', {
group = nvim_terminal_augroup,
nvim_on('TermRequest', nvim_terminal_augroup, {
desc = 'Mark shell prompts indicated by OSC 133 sequences for navigation',
callback = function(ev)
if string.match(ev.data.sequence, '^\027]133;A') then
local lnum = ev.data.cursor[1] ---@type integer
if lnum >= 1 then
vim.api.nvim_buf_set_extmark(
ev.buf,
nvim_terminal_prompt_ns,
lnum - 1,
0,
{ right_gravity = false }
)
end
}, function(ev)
if string.match(ev.data.sequence, '^\027]133;A') then
local lnum = ev.data.cursor[1] ---@type integer
if lnum >= 1 then
vim.api.nvim_buf_set_extmark(
ev.buf,
nvim_terminal_prompt_ns,
lnum - 1,
0,
{ right_gravity = false }
)
end
end,
})
end
end)
---@param ns integer
---@param buf integer
@@ -732,39 +714,37 @@ do
end
end
vim.api.nvim_create_autocmd('TermOpen', {
group = nvim_terminal_augroup,
nvim_on('TermOpen', nvim_terminal_augroup, {
desc = 'Default settings for :terminal buffers',
callback = function(ev)
vim.bo[ev.buf].modifiable = false
vim.bo[ev.buf].undolevels = -1
vim.bo[ev.buf].scrollback = vim.o.scrollback < 0 and 10000 or math.max(1, vim.o.scrollback)
vim.bo[ev.buf].textwidth = 0
vim.wo[0][0].wrap = false
vim.wo[0][0].list = false
vim.wo[0][0].number = false
vim.wo[0][0].relativenumber = false
vim.wo[0][0].signcolumn = 'no'
vim.wo[0][0].foldcolumn = '0'
}, function(ev)
vim.bo[ev.buf].modifiable = false
vim.bo[ev.buf].undolevels = -1
vim.bo[ev.buf].scrollback = vim.o.scrollback < 0 and 10000 or math.max(1, vim.o.scrollback)
vim.bo[ev.buf].textwidth = 0
vim.wo[0][0].wrap = false
vim.wo[0][0].list = false
vim.wo[0][0].number = false
vim.wo[0][0].relativenumber = false
vim.wo[0][0].signcolumn = 'no'
vim.wo[0][0].foldcolumn = '0'
-- This is gross. Proper list options support when?
local winhl = vim.o.winhighlight
if winhl ~= '' then
winhl = winhl .. ','
end
vim.wo[0][0].winhighlight = winhl .. 'StatusLine:StatusLineTerm,StatusLineNC:StatusLineTermNC'
-- This is gross. Proper list options support when?
local winhl = vim.o.winhighlight
if winhl ~= '' then
winhl = winhl .. ','
end
vim.wo[0][0].winhighlight = winhl .. 'StatusLine:StatusLineTerm,StatusLineNC:StatusLineTermNC'
vim.keymap.set({ 'n', 'x', 'o' }, '[[', function()
jump_to_prompt(nvim_terminal_prompt_ns, 0, ev.buf, -vim.v.count1)
end, { buf = ev.buf, desc = 'Jump [count] shell prompts backward' })
vim.keymap.set({ 'n', 'x', 'o' }, ']]', function()
jump_to_prompt(nvim_terminal_prompt_ns, 0, ev.buf, vim.v.count1)
end, { buf = ev.buf, desc = 'Jump [count] shell prompts forward' })
vim.keymap.set({ 'n', 'x', 'o' }, '[[', function()
jump_to_prompt(nvim_terminal_prompt_ns, 0, ev.buf, -vim.v.count1)
end, { buf = ev.buf, desc = 'Jump [count] shell prompts backward' })
vim.keymap.set({ 'n', 'x', 'o' }, ']]', function()
jump_to_prompt(nvim_terminal_prompt_ns, 0, ev.buf, vim.v.count1)
end, { buf = ev.buf, desc = 'Jump [count] shell prompts forward' })
-- If the terminal buffer is being reused, clear the previous exit msg
vim.api.nvim_buf_clear_namespace(ev.buf, nvim_terminal_exitmsg_ns, 0, -1)
end,
})
-- If the terminal buffer is being reused, clear the previous exit msg
vim.api.nvim_buf_clear_namespace(ev.buf, nvim_terminal_exitmsg_ns, 0, -1)
end)
vim.api.nvim_create_autocmd('CmdwinEnter', {
pattern = '[:>]',
@@ -773,26 +753,24 @@ do
command = 'syntax sync minlines=1 maxlines=1',
})
vim.api.nvim_create_autocmd('SwapExists', {
nvim_on('SwapExists', vim.api.nvim_create_augroup('nvim.swapfile', {}), {
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),
vim.log.levels.WARN,
{ _truncate = true }
)
end,
})
}, 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),
vim.log.levels.WARN,
{ _truncate = true }
)
end)
-- Check if a TTY is attached
local tty = nil
@@ -824,14 +802,12 @@ do
--- @diagnostic disable-next-line:no-unknown
vim.o[option] = value
else
vim.api.nvim_create_autocmd('VimEnter', {
group = group,
nvim_on('VimEnter', group, {
once = true,
nested = true,
callback = function()
setoption(option, value, force)
end,
})
}, function()
setoption(option, value, force)
end)
end
end
@@ -950,22 +926,20 @@ do
end
end)
vim.api.nvim_create_autocmd('VimEnter', {
group = group,
nvim_on('VimEnter', group, {
nested = true,
once = true,
callback = function()
local optinfo = vim.api.nvim_get_option_info2('background', {})
local sid_lua = -8
if
optinfo.was_set
and optinfo.last_set_sid ~= sid_lua
and next(vim.api.nvim_get_autocmds({ id = id })) ~= nil
then
vim.api.nvim_del_autocmd(id)
end
end,
})
}, function()
local optinfo = vim.api.nvim_get_option_info2('background', {})
local sid_lua = -8
if
optinfo.was_set
and optinfo.last_set_sid ~= sid_lua
and next(vim.api.nvim_get_autocmds({ id = id })) ~= nil
then
vim.api.nvim_del_autocmd(id)
end
end)
-- Wait until detection of OSC 11 capabilities is complete to
-- ensure background is automatically set before user config.
@@ -1068,22 +1042,20 @@ do
if tty then
-- Show progress bars in supporting terminals
vim.api.nvim_create_autocmd('Progress', {
group = vim.api.nvim_create_augroup('nvim.progress', {}),
nvim_on('Progress', vim.api.nvim_create_augroup('nvim.progress', {}), {
desc = 'Display native progress bars',
callback = function(ev)
if ev.data.status == 'running' then
if ev.data.percent ~= nil then
vim.api.nvim_ui_send(string.format('\027]9;4;1;%d\027\\', ev.data.percent))
else
-- "Indeterminate" progress (unknown percent).
vim.api.nvim_ui_send(string.format('\027]9;4;3\027\\'))
end
}, function(ev)
if ev.data.status == 'running' then
if ev.data.percent ~= nil then
vim.api.nvim_ui_send(string.format('\027]9;4;1;%d\027\\', ev.data.percent))
else
vim.api.nvim_ui_send('\027]9;4;0;0\027\\')
-- "Indeterminate" progress (unknown percent).
vim.api.nvim_ui_send(string.format('\027]9;4;3\027\\'))
end
end,
})
else
vim.api.nvim_ui_send('\027]9;4;0;0\027\\')
end
end)
end
end

View File

@@ -49,6 +49,7 @@
--- - |g<| at any time.
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local M = {
ns = api.nvim_create_namespace('nvim.ui2'),
augroup = api.nvim_create_augroup('nvim.ui2', {}),
@@ -229,30 +230,26 @@ function M.enable(opts)
end)
end
api.nvim_create_autocmd('OptionSet', {
group = M.augroup,
nvim_on('OptionSet', M.augroup, {
pattern = { 'cmdheight', 'laststatus' },
callback = function(ev)
if ev.match == 'cmdheight' then
check_cmdheight(vim.v.option_new)
end
M.msg.set_pos()
end,
desc = 'Set cmdline and message window dimensions for changed option values.',
})
}, function(ev)
if ev.match == 'cmdheight' then
check_cmdheight(vim.v.option_new)
end
M.msg.set_pos()
end)
api.nvim_create_autocmd({ 'VimResized', 'TabEnter' }, {
group = M.augroup,
callback = function(ev)
M.check_targets()
-- After a tabpage was closed unhide the msg window on the current tabpage.
if ev.event == 'TabEnter' and next(M.msg.msg.ids) ~= nil then
api.nvim_win_set_config(M.wins.msg, { hide = false, width = M.msg.msg.width })
end
M.msg.set_pos()
end,
nvim_on({ 'VimResized', 'TabEnter' }, M.augroup, {
desc = 'Set cmdline and message window dimensions after shell resize or tabpage change.',
})
}, function(ev)
M.check_targets()
-- After a tabpage was closed unhide the msg window on the current tabpage.
if ev.event == 'TabEnter' and next(M.msg.msg.ids) ~= nil then
api.nvim_win_set_config(M.wins.msg, { hide = false, width = M.msg.msg.width })
end
M.msg.set_pos()
end)
end
return M

View File

@@ -1,4 +1,5 @@
local api, fn, o = vim.api, vim.fn, vim.o
local nvim_on = require('vim._core.util').nvim_on
local ui = require('vim._core.ui2')
---@alias Msg { extid: integer, timer: uv.uv_timer_t? }
@@ -634,35 +635,33 @@ local function enter_pager()
local height, id = api.nvim_win_get_height(ui.wins.pager), 0
api.nvim_set_option_value('eiw', '', { scope = 'local', win = ui.wins.pager })
api.nvim_set_current_win(ui.wins.pager)
id = api.nvim_create_autocmd({ 'WinEnter', 'CmdwinEnter', 'WinResized' }, {
group = ui.augroup,
callback = function(ev)
if fn.getcmdtype() ~= '' then
-- WinEnter fires before we can detect cmdwin will be entered: keep open.
return
elseif ev.event == 'WinResized' and fn.getcmdwintype() == '' then
-- Remember height to be restored when cmdwin is closed.
height = api.nvim_win_get_height(ui.wins.pager)
elseif ev.event == 'WinEnter' then
-- Close when no longer current window.
in_pager = api.nvim_get_current_win() == ui.wins.pager
end
in_pager = in_pager and api.nvim_win_is_valid(ui.wins.pager)
local cfg = in_pager and { relative = 'laststatus', col = 0 } or { hide = true }
if in_pager then
cfg.row, cfg.height, cfg.border = win_row_height_border('pager', height)
else
pcall(api.nvim_set_option_value, 'eiw', 'all', { scope = 'local', win = ui.wins.pager })
api.nvim_del_autocmd(id)
if was_cmdwin ~= '' then
api.nvim_feedkeys('q' .. was_cmdwin, 'n', false)
was_cmdwin = ''
end
end
pcall(api.nvim_win_set_config, ui.wins.pager, cfg)
end,
id = nvim_on({ 'WinEnter', 'CmdwinEnter', 'WinResized' }, ui.augroup, {
desc = 'Hide or reposition pager window.',
})
}, function(ev)
if fn.getcmdtype() ~= '' then
-- WinEnter fires before we can detect cmdwin will be entered: keep open.
return
elseif ev.event == 'WinResized' and fn.getcmdwintype() == '' then
-- Remember height to be restored when cmdwin is closed.
height = api.nvim_win_get_height(ui.wins.pager)
elseif ev.event == 'WinEnter' then
-- Close when no longer current window.
in_pager = api.nvim_get_current_win() == ui.wins.pager
end
in_pager = in_pager and api.nvim_win_is_valid(ui.wins.pager)
local cfg = in_pager and { relative = 'laststatus', col = 0 } or { hide = true }
if in_pager then
cfg.row, cfg.height, cfg.border = win_row_height_border('pager', height)
else
pcall(api.nvim_set_option_value, 'eiw', 'all', { scope = 'local', win = ui.wins.pager })
api.nvim_del_autocmd(id)
if was_cmdwin ~= '' then
api.nvim_feedkeys('q' .. was_cmdwin, 'n', false)
was_cmdwin = ''
end
end
pcall(api.nvim_win_set_config, ui.wins.pager, cfg)
end)
end)
end

View File

@@ -172,4 +172,33 @@ function M.echo_err(msg)
vim.api.nvim_echo({ { msg } }, true, { err = true })
end
--- Define event-handlers (autocmds) ergonomically.
---
--- Examples:
--- ```lua
--- local nvim_on = require('vim._core.util').nvim_on
--- nvim_on('BufWritePost', group, function(ev) print(ev.file) end)
--- nvim_on({ 'BufRead', 'BufNew' }, group, { pattern = '*.lua' }, function(ev) end)
--- nvim_on('VimLeavePre', nil, function() end)
--- ```
---
--- @param events vim.api.keyset.events|vim.api.keyset.events[] Event(s) to watch. See |autocmd-events|.
--- @param group string|integer? Group name or id, or `nil`.
--- @param opts_or_fn vim.api.keyset.create_autocmd Options.
--- @param fn fun(ev: vim.api.keyset.create_autocmd.callback_args): boolean? Event handler.
--- @return integer # Autocmd id (see |nvim_create_autocmd()|).
--- @overload fun(events: vim.api.keyset.events|vim.api.keyset.events[], group: string|integer?, fn: fun(ev: vim.api.keyset.create_autocmd.callback_args): boolean?): integer
function M.nvim_on(events, group, opts_or_fn, fn)
vim.validate('opts_or_fn', opts_or_fn, { 'function', 'table' })
local opts --- @type vim.api.keyset.create_autocmd
if type(opts_or_fn) == 'function' then
fn, opts = opts_or_fn, {}
else
opts = opts_or_fn
end
opts.group = group
opts.callback = fn
return vim.api.nvim_create_autocmd(events, opts)
end
return M

View File

@@ -1,4 +1,5 @@
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
-- TODO(lewis6991): deprecate some top level functions in favour of the submodule version
-- e.g. vim.diagnostic.get_namespace() -> vim.diagnostic.namespace.get()
@@ -1139,14 +1140,12 @@ function M.status(buf)
return result_str
end
api.nvim_create_autocmd('DiagnosticChanged', {
group = api.nvim_create_augroup('nvim.diagnostic.status', {}),
callback = function(ev)
if api.nvim_buf_is_loaded(ev.buf) then
api.nvim__redraw({ buf = ev.buf, statusline = true })
end
end,
nvim_on('DiagnosticChanged', api.nvim_create_augroup('nvim.diagnostic.status', {}), {
desc = 'diagnostics component for the statusline',
})
}, function(ev)
if api.nvim_buf_is_loaded(ev.buf) then
api.nvim__redraw({ buf = ev.buf, statusline = true })
end
end)
return M

View File

@@ -1,4 +1,5 @@
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local diagnostic_modules = vim._defer_require('vim.diagnostic', {
_config = ..., --- @module 'vim.diagnostic._config'
@@ -62,14 +63,12 @@ local function schedule_display(namespace, bufnr, args)
local key = make_augroup_key(namespace, bufnr)
if not registered_autocmds[key] then
local group = api.nvim_create_augroup(key, { clear = true })
api.nvim_create_autocmd(insert_leave_auto_cmds, {
group = group,
nvim_on(insert_leave_auto_cmds, group, {
buf = bufnr,
callback = function()
execute_scheduled_display(namespace, bufnr)
end,
desc = 'vim.diagnostic: display diagnostics',
})
}, function()
execute_scheduled_display(namespace, bufnr)
end)
registered_autocmds[key] = true
end
end

View File

@@ -1,4 +1,5 @@
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local diagnostic = vim.diagnostic
local diagnostic_shared = require('vim.diagnostic._shared')
@@ -123,13 +124,9 @@ local function once_buf_loaded(bufnr, fn)
if api.nvim_buf_is_loaded(bufnr) then
fn()
else
return api.nvim_create_autocmd('BufRead', {
buf = bufnr,
once = true,
callback = function()
fn()
end,
})
return nvim_on('BufRead', nil, { buf = bufnr, once = true }, function()
fn()
end)
end
end
@@ -436,13 +433,9 @@ function M.virtual_text.show(namespace, bufnr, diagnostics, opts)
local line_diagnostics = diagnostic_shared.diagnostic_lines(diagnostics, true)
if vopts.current_line ~= nil then
api.nvim_create_autocmd('CursorMoved', {
buf = bufnr,
group = ns.user_data.virt_text_augroup,
callback = function()
render_virtual_text(ns.user_data.virt_text_ns, bufnr, line_diagnostics, vopts)
end,
})
nvim_on('CursorMoved', ns.user_data.virt_text_augroup, { buf = bufnr }, function()
render_virtual_text(ns.user_data.virt_text_ns, bufnr, line_diagnostics, vopts)
end)
end
render_virtual_text(ns.user_data.virt_text_ns, bufnr, line_diagnostics, vopts)
@@ -690,17 +683,13 @@ function M.virtual_lines.show(namespace, bufnr, diagnostics, opts)
-- Create a mapping from line -> diagnostics so that we can quickly get the
-- diagnostics we need when the cursor line doesn't change.
local line_diagnostics = diagnostic_shared.diagnostic_lines(diagnostics, true)
api.nvim_create_autocmd('CursorMoved', {
buf = bufnr,
group = ns.user_data.virt_lines_augroup,
callback = function()
render_virtual_lines(
ns.user_data.virt_lines_ns,
bufnr,
diagnostic_shared.diagnostics_at_cursor(line_diagnostics)
)
end,
})
nvim_on('CursorMoved', ns.user_data.virt_lines_augroup, { buf = bufnr }, function()
render_virtual_lines(
ns.user_data.virt_lines_ns,
bufnr,
diagnostic_shared.diagnostics_at_cursor(line_diagnostics)
)
end)
-- Also show diagnostics for the current line before the first CursorMoved event.
render_virtual_lines(

View File

@@ -1,4 +1,5 @@
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local severity_module = require('vim.diagnostic._severity')
@@ -14,13 +15,9 @@ setmetatable(diagnostic_cache, {
--- @param bufnr integer
__index = function(t, bufnr)
assert(bufnr > 0, 'Invalid buffer number')
api.nvim_create_autocmd('BufWipeout', {
group = group,
buf = bufnr,
callback = function()
rawset(t, bufnr, nil)
end,
})
nvim_on('BufWipeout', group, { buf = bufnr }, function()
rawset(t, bufnr, nil)
end)
t[bufnr] = {}
return t[bufnr]
end,
@@ -50,13 +47,12 @@ local function once_buf_loaded(bufnr, fn)
if api.nvim_buf_is_loaded(bufnr) then
fn()
else
return api.nvim_create_autocmd('BufRead', {
return nvim_on('BufRead', nil, {
buf = bufnr,
once = true,
callback = function()
fn()
end,
})
}, function()
fn()
end)
end
end

View File

@@ -1,3 +1,5 @@
local nvim_on = require('vim._core.util').nvim_on
local M = {}
local health = require('vim.health')
@@ -707,31 +709,27 @@ local function check_sysinfo()
)
)
vim.api.nvim_create_autocmd('FileType', {
pattern = 'checkhealth',
once = true,
callback = function(ev)
local buf = ev.buf
local win = vim.fn.bufwinid(buf)
if win == -1 then
return
end
local encoded_body = vim.uri_encode(body) --- @type string
local issue_url = 'https://github.com/neovim/neovim/issues/new?type=Bug&body=' .. encoded_body
nvim_on('FileType', nil, { pattern = 'checkhealth', once = true }, function(ev)
local buf = ev.buf
local win = vim.fn.bufwinid(buf)
if win == -1 then
return
end
local encoded_body = vim.uri_encode(body) --- @type string
local issue_url = 'https://github.com/neovim/neovim/issues/new?type=Bug&body=' .. encoded_body
_G.nvim_health_bugreport_open = function()
vim.ui.open(issue_url)
end
vim.wo[win].winbar =
'%#WarningMsg#%@v:lua.nvim_health_bugreport_open@▶ Create Bug Report on GitHub%X%*'
_G.nvim_health_bugreport_open = function()
vim.ui.open(issue_url)
end
vim.wo[win].winbar =
'%#WarningMsg#%@v:lua.nvim_health_bugreport_open@▶ Create Bug Report on GitHub%X%*'
vim.api.nvim_create_autocmd('BufDelete', {
buf = buf,
once = true,
command = 'lua _G.nvim_health_bugreport_open = nil',
})
end,
})
vim.api.nvim_create_autocmd('BufDelete', {
buf = buf,
once = true,
command = 'lua _G.nvim_health_bugreport_open = nil',
})
end)
end
function M.check()

View File

@@ -1,5 +1,6 @@
local api = vim.api
local validate = vim.validate
local nvim_on = require('vim._core.util').nvim_on
local lsp = vim._defer_require('vim.lsp', {
_capability = ..., --- @module 'vim.lsp._capability'
@@ -643,12 +644,9 @@ function lsp.enable(name, enable)
else
-- Only ever create autocmd once to reuse computation of config merging.
lsp_enable_autocmd_id = lsp_enable_autocmd_id
or api.nvim_create_autocmd('FileType', {
group = api.nvim_create_augroup('nvim.lsp.enable', {}),
callback = function(ev)
lsp_enable_callback(ev.buf)
end,
})
or nvim_on('FileType', api.nvim_create_augroup('nvim.lsp.enable', {}), function(ev)
lsp_enable_callback(ev.buf)
end)
end
-- Ensure any pre-existing buffers start/stop their LSP clients.
@@ -909,41 +907,37 @@ local function buf_attach(bufnr)
local uri = vim.uri_from_bufnr(bufnr)
local augroup = ('nvim.lsp.b_%d_save'):format(bufnr)
local group = api.nvim_create_augroup(augroup, { clear = true })
api.nvim_create_autocmd('BufWritePre', {
group = group,
nvim_on('BufWritePre', group, {
buf = bufnr,
desc = 'vim.lsp: textDocument/willSave',
callback = function(ctx)
for _, client in ipairs(lsp.get_clients({ bufnr = ctx.buf })) do
local params = {
textDocument = {
uri = uri,
},
reason = protocol.TextDocumentSaveReason.Manual, ---@type integer
}
if client:supports_method('textDocument/willSave') then
client:notify('textDocument/willSave', params)
end
if client:supports_method('textDocument/willSaveWaitUntil') then
local result, err =
client:request_sync('textDocument/willSaveWaitUntil', params, 1000, ctx.buf)
if result and result.result then
util.apply_text_edits(result.result, ctx.buf, client.offset_encoding)
elseif err then
log.error(vim.inspect(err))
end
}, function(ctx)
for _, client in ipairs(lsp.get_clients({ bufnr = ctx.buf })) do
local params = {
textDocument = {
uri = uri,
},
reason = protocol.TextDocumentSaveReason.Manual, ---@type integer
}
if client:supports_method('textDocument/willSave') then
client:notify('textDocument/willSave', params)
end
if client:supports_method('textDocument/willSaveWaitUntil') then
local result, err =
client:request_sync('textDocument/willSaveWaitUntil', params, 1000, ctx.buf)
if result and result.result then
util.apply_text_edits(result.result, ctx.buf, client.offset_encoding)
elseif err then
log.error(vim.inspect(err))
end
end
end,
})
api.nvim_create_autocmd('BufWritePost', {
group = group,
end
end)
nvim_on('BufWritePost', group, {
buf = bufnr,
desc = 'vim.lsp: textDocument/didSave handler',
callback = function(ctx)
text_document_did_save_handler(ctx.buf)
end,
})
}, function(ctx)
text_document_did_save_handler(ctx.buf)
end)
-- First time, so attach and set up stuff.
api.nvim_buf_attach(bufnr, false, {
on_lines = function(_, _, changedtick, firstline, lastline, new_lastline)
@@ -1169,42 +1163,36 @@ end
-- Minimum time before warning about LSP exit_timeout on Nvim exit.
local min_warn_exit_timeout = 100
api.nvim_create_autocmd('VimLeavePre', {
desc = 'vim.lsp: exit handler',
callback = function()
local active_clients = lsp.get_clients()
log.info('exit_handler', active_clients)
nvim_on('VimLeavePre', nil, { desc = 'vim.lsp: exit handler' }, function()
local active_clients = lsp.get_clients()
log.info('exit_handler', active_clients)
local max_timeout = 0
for _, client in pairs(active_clients) do
max_timeout = math.max(max_timeout, vim._tointeger(client.exit_timeout) or 0)
client:stop(client.exit_timeout)
end
local max_timeout = 0
for _, client in pairs(active_clients) do
max_timeout = math.max(max_timeout, vim._tointeger(client.exit_timeout) or 0)
client:stop(client.exit_timeout)
end
local exit_warning_timer = max_timeout > min_warn_exit_timeout
and vim.defer_fn(function()
api.nvim_echo({
{
string.format(
'Waiting %ss for LSP exit (Press Ctrl-C to force exit)',
max_timeout / 1e3
),
'WarningMsg',
},
}, true, {})
end, min_warn_exit_timeout)
local exit_warning_timer = max_timeout > min_warn_exit_timeout
and vim.defer_fn(function()
api.nvim_echo({
{
string.format('Waiting %ss for LSP exit (Press Ctrl-C to force exit)', max_timeout / 1e3),
'WarningMsg',
},
}, true, {})
end, min_warn_exit_timeout)
vim.wait(max_timeout, function()
return vim.iter(active_clients):all(function(client)
return client.rpc.is_closing()
end)
vim.wait(max_timeout, function()
return vim.iter(active_clients):all(function(client)
return client.rpc.is_closing()
end)
end)
if exit_warning_timer and not exit_warning_timer:is_closing() then
exit_warning_timer:close()
end
end,
})
if exit_warning_timer and not exit_warning_timer:is_closing() then
exit_warning_timer:close()
end
end)
---@nodoc
--- Sends an async request for all active clients attached to the

View File

@@ -1,6 +1,7 @@
local util = require('vim.lsp.util')
local log = require('vim.lsp.log')
local tableclear = require('vim._core.table').clear
local nvim_on = require('vim._core.util').nvim_on
local api = vim.api
---@type table<lsp.FoldingRangeKind, true>
@@ -116,14 +117,10 @@ local scheduled_foldupdate = {}
local function schedule_foldupdate(bufnr)
if not scheduled_foldupdate[bufnr] then
scheduled_foldupdate[bufnr] = true
api.nvim_create_autocmd('InsertLeave', {
buf = bufnr,
once = true,
callback = function()
foldupdate(bufnr)
scheduled_foldupdate[bufnr] = nil
end,
})
nvim_on('InsertLeave', nil, { buf = bufnr, once = true }, function()
foldupdate(bufnr)
scheduled_foldupdate[bufnr] = nil
end)
end
end
@@ -238,35 +235,23 @@ function State:new(bufnr)
end
end,
})
api.nvim_create_autocmd('LspNotify', {
group = self.augroup,
buf = bufnr,
callback = function(ev)
local client = assert(vim.lsp.get_client_by_id(ev.data.client_id))
if
client:supports_method('textDocument/foldingRange', bufnr)
and (ev.data.method == 'textDocument/didChange' or ev.data.method == 'textDocument/didOpen')
then
self:refresh(client)
end
end,
})
api.nvim_create_autocmd('OptionSet', {
group = self.augroup,
pattern = 'foldexpr',
callback = function()
if vim.v.option_type == 'global' or api.nvim_get_current_buf() == bufnr then
vim.lsp._capability.enable('folding_range', false, { bufnr = bufnr })
end
end,
})
api.nvim_create_autocmd('FileType', {
group = self.augroup,
buf = bufnr,
callback = function()
self:reset()
end,
})
nvim_on('LspNotify', self.augroup, { buf = bufnr }, function(ev)
local client = assert(vim.lsp.get_client_by_id(ev.data.client_id))
if
client:supports_method('textDocument/foldingRange', bufnr)
and (ev.data.method == 'textDocument/didChange' or ev.data.method == 'textDocument/didOpen')
then
self:refresh(client)
end
end)
nvim_on('OptionSet', self.augroup, { pattern = 'foldexpr' }, function()
if vim.v.option_type == 'global' or api.nvim_get_current_buf() == bufnr then
vim.lsp._capability.enable('folding_range', false, { bufnr = bufnr })
end
end)
nvim_on('FileType', self.augroup, { buf = bufnr }, function()
self:reset()
end)
return self
end

View File

@@ -2,6 +2,7 @@
--- The `vim.lsp.buf_…` functions perform operations for LSP clients attached to the current buffer.
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local lsp = vim.lsp
local validate = vim.validate
local util = require('vim.lsp.util')
@@ -188,14 +189,10 @@ function M.hover(config)
local _, winid = lsp.util.open_floating_preview(contents, format, config)
api.nvim_create_autocmd('WinClosed', {
pattern = tostring(winid),
once = true,
callback = function()
api.nvim_buf_clear_namespace(bufnr, hover_ns, 0, -1)
return true
end,
})
nvim_on('WinClosed', nil, { pattern = tostring(winid), once = true }, function()
api.nvim_buf_clear_namespace(bufnr, hover_ns, 0, -1)
return true
end)
end)
end
@@ -1509,13 +1506,9 @@ function M.selection_range(direction, timeout_ms)
end
-- Clear selection ranges when leaving visual mode.
api.nvim_create_autocmd('ModeChanged', {
once = true,
pattern = 'v*:*',
callback = function()
selection_ranges = nil
end,
})
nvim_on('ModeChanged', nil, { once = true, pattern = 'v*:*' }, function()
selection_ranges = nil
end)
if #ranges > 0 then
local index = math.min(#ranges, math.max(1, direction))

View File

@@ -34,6 +34,7 @@
local M = {}
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local lsp = vim.lsp
local protocol = lsp.protocol
@@ -789,55 +790,53 @@ end
--- Defines a CompleteChanged handler to request and display LSP completion item documentation
--- via completionItem/resolve
local function on_completechanged(group, bufnr)
api.nvim_create_autocmd('CompleteChanged', {
group = group,
nvim_on('CompleteChanged', group, {
buf = bufnr,
callback = function(ev)
local completed_item = vim.v.event.completed_item or {}
local lsp_item = vim.tbl_get(completed_item, 'user_data', 'nvim', 'lsp', 'completion_item')
local data = vim.fn.complete_info({ 'selected' })
if (completed_item.info or '') ~= '' then
local kind = vim.tbl_get(lsp_item or {}, 'documentation', 'kind')
update_popup_window(
data.preview_winid,
data.preview_bufnr,
kind or protocol.MarkupKind.Markdown
)
return
end
if
#lsp.get_clients({
id = vim.tbl_get(completed_item, 'user_data', 'nvim', 'lsp', 'client_id'),
method = 'completionItem/resolve',
bufnr = ev.buf,
}) == 0
then
if
has_completeopt('popup')
and lsp_item
and lsp_item.insertTextFormat == protocol.InsertTextFormat.Snippet
then
-- Shows snippet preview in doc popup if completeopt=popup.
local text = parse_snippet(lsp_item.insertText or lsp_item.textEdit.newText)
local windata = api.nvim__complete_set(
data.selected,
{ info = ('```%s\n%s\n```'):format(vim.bo.filetype, text) }
)
update_popup_window(windata.winid, windata.bufnr, protocol.MarkupKind.Markdown)
end
return
end
-- Retrieve the raw LSP completionItem from completed_item as the parameter for
-- the completionItem/resolve request
if lsp_item then
Context.resolve_handler = Context.resolve_handler or CompletionResolver.new()
Context.resolve_handler:request(ev.buf, lsp_item, completed_item.word)
end
end,
desc = 'Request and display LSP completion item documentation via completionItem/resolve',
})
}, function(ev)
local completed_item = vim.v.event.completed_item or {}
local lsp_item = vim.tbl_get(completed_item, 'user_data', 'nvim', 'lsp', 'completion_item')
local data = vim.fn.complete_info({ 'selected' })
if (completed_item.info or '') ~= '' then
local kind = vim.tbl_get(lsp_item or {}, 'documentation', 'kind')
update_popup_window(
data.preview_winid,
data.preview_bufnr,
kind or protocol.MarkupKind.Markdown
)
return
end
if
#lsp.get_clients({
id = vim.tbl_get(completed_item, 'user_data', 'nvim', 'lsp', 'client_id'),
method = 'completionItem/resolve',
bufnr = ev.buf,
}) == 0
then
if
has_completeopt('popup')
and lsp_item
and lsp_item.insertTextFormat == protocol.InsertTextFormat.Snippet
then
-- Shows snippet preview in doc popup if completeopt=popup.
local text = parse_snippet(lsp_item.insertText or lsp_item.textEdit.newText)
local windata = api.nvim__complete_set(
data.selected,
{ info = ('```%s\n%s\n```'):format(vim.bo.filetype, text) }
)
update_popup_window(windata.winid, windata.bufnr, protocol.MarkupKind.Markdown)
end
return
end
-- Retrieve the raw LSP completionItem from completed_item as the parameter for
-- the completionItem/resolve request
if lsp_item then
Context.resolve_handler = Context.resolve_handler or CompletionResolver.new()
Context.resolve_handler:request(ev.buf, lsp_item, completed_item.word)
end
end)
end
local function on_complete_done()
@@ -941,16 +940,12 @@ local function register_completedone(bufnr)
return group
end
api.nvim_create_autocmd('CompleteDone', {
group = group,
buf = bufnr,
callback = function()
local reason = api.nvim_get_vvar('event').reason ---@type string
if reason == 'accept' then
on_complete_done()
end
end,
})
nvim_on('CompleteDone', group, { buf = bufnr }, function()
local reason = api.nvim_get_vvar('event').reason ---@type string
if reason == 'accept' then
on_complete_done()
end
end)
return group
end
@@ -1167,28 +1162,18 @@ local function enable_completions(client_id, bufnr, opts)
-- Set up autocommands.
local group = register_completedone(bufnr)
api.nvim_create_autocmd('LspDetach', {
group = group,
nvim_on('LspDetach', group, {
buf = bufnr,
desc = 'vim.lsp.completion: clean up client on detach',
callback = function(ev)
disable_completions(ev.data.client_id, ev.buf)
end,
})
}, function(ev)
disable_completions(ev.data.client_id, ev.buf)
end)
if opts.autotrigger then
api.nvim_create_autocmd('InsertCharPre', {
group = group,
buf = bufnr,
callback = function()
on_insert_char_pre(buf_handles[bufnr])
end,
})
api.nvim_create_autocmd('InsertLeave', {
group = group,
buf = bufnr,
callback = on_insert_leave,
})
nvim_on('InsertCharPre', group, { buf = bufnr }, function()
on_insert_char_pre(buf_handles[bufnr])
end)
nvim_on('InsertLeave', group, { buf = bufnr }, on_insert_leave)
end
end

View File

@@ -8,6 +8,7 @@ local protocol = lsp.protocol
local util = lsp.util
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local M = {}
@@ -416,22 +417,18 @@ function M._enable(bufnr)
bufstates[bufnr] = { pull_kind = 'document', client_result_id = {} }
end
api.nvim_create_autocmd('LspNotify', {
buf = bufnr,
callback = function(opts)
if
opts.data.method ~= 'textDocument/didChange'
and opts.data.method ~= 'textDocument/didOpen'
then
return
end
if bufstates[bufnr] and bufstates[bufnr].pull_kind == 'document' then
local client_id = opts.data.client_id --- @type integer?
M._refresh(bufnr, client_id, true)
end
end,
group = augroup,
})
nvim_on('LspNotify', augroup, { buf = bufnr }, function(opts)
if
opts.data.method ~= 'textDocument/didChange'
and opts.data.method ~= 'textDocument/didOpen'
then
return
end
if bufstates[bufnr] and bufstates[bufnr].pull_kind == 'document' then
local client_id = opts.data.client_id --- @type integer?
M._refresh(bufnr, client_id, true)
end
end)
api.nvim_buf_attach(bufnr, false, {
on_reload = function()
@@ -444,21 +441,15 @@ function M._enable(bufnr)
end,
})
api.nvim_create_autocmd('LspDetach', {
buf = bufnr,
callback = function(ev)
local clients = lsp.get_clients({ bufnr = bufnr, method = 'textDocument/diagnostic' })
nvim_on('LspDetach', augroup, { buf = bufnr }, function(ev)
local clients = lsp.get_clients({ bufnr = bufnr, method = 'textDocument/diagnostic' })
if
not vim.iter(clients):any(function(c)
return c.id ~= ev.data.client_id
end)
then
disable(bufnr)
end
end,
group = augroup,
})
if not vim.iter(clients):any(function(c)
return c.id ~= ev.data.client_id
end) then
disable(bufnr)
end
end)
end
--- Returns the result IDs from the reports provided by the given client.

View File

@@ -2,6 +2,7 @@
--- Highlighting is enabled by default.
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local lsp = vim.lsp
local util = lsp.util
local Capability = require('vim.lsp._capability')
@@ -144,19 +145,15 @@ function Provider:new(bufnr)
end,
})
api.nvim_create_autocmd('ColorScheme', {
group = self.augroup,
desc = 'Refresh document_color',
callback = function()
color_cache = {}
n_color_cache = 0
local provider = Provider.active[bufnr]
if provider then
provider:clear()
provider:request()
end
end,
})
nvim_on('ColorScheme', self.augroup, { desc = 'Refresh document_color' }, function()
color_cache = {}
n_color_cache = 0
local provider = Provider.active[bufnr]
if provider then
provider:clear()
provider:request()
end
end)
return self
end

View File

@@ -1,5 +1,6 @@
local util = require('vim.lsp.util')
local log = require('vim.lsp.log')
local nvim_on = require('vim._core.util').nvim_on
local api = vim.api
local M = {}
@@ -263,55 +264,46 @@ local function _enable(bufnr)
refresh(bufnr)
end
api.nvim_create_autocmd('LspNotify', {
callback = function(ev)
---@type integer
local bufnr = ev.buf
nvim_on('LspNotify', augroup, function(ev)
---@type integer
local bufnr = ev.buf
if ev.data.method ~= 'textDocument/didChange' and ev.data.method ~= 'textDocument/didOpen' then
return
end
if bufstates[bufnr].enabled then
refresh(bufnr, ev.data.client_id)
end
end,
group = augroup,
})
api.nvim_create_autocmd('LspAttach', {
callback = function(ev)
---@type integer
local bufnr = ev.buf
if ev.data.method ~= 'textDocument/didChange' and ev.data.method ~= 'textDocument/didOpen' then
return
end
if bufstates[bufnr].enabled then
refresh(bufnr, ev.data.client_id)
end
end)
nvim_on('LspAttach', augroup, function(ev)
---@type integer
local bufnr = ev.buf
api.nvim_buf_attach(bufnr, false, {
on_reload = function(_, cb_bufnr)
clear(cb_bufnr)
if bufstates[cb_bufnr] and bufstates[cb_bufnr].enabled then
bufstates[cb_bufnr].applied = {}
refresh(cb_bufnr)
end
end,
on_detach = function(_, cb_bufnr)
_disable(cb_bufnr)
bufstates[cb_bufnr] = nil
end,
})
end,
group = augroup,
})
api.nvim_create_autocmd('LspDetach', {
callback = function(ev)
---@type integer
local bufnr = ev.buf
local clients = vim.lsp.get_clients({ bufnr = bufnr, method = 'textDocument/inlayHint' })
api.nvim_buf_attach(bufnr, false, {
on_reload = function(_, cb_bufnr)
clear(cb_bufnr)
if bufstates[cb_bufnr] and bufstates[cb_bufnr].enabled then
bufstates[cb_bufnr].applied = {}
refresh(cb_bufnr)
end
end,
on_detach = function(_, cb_bufnr)
_disable(cb_bufnr)
bufstates[cb_bufnr] = nil
end,
})
end)
nvim_on('LspDetach', augroup, function(ev)
---@type integer
local bufnr = ev.buf
local clients = vim.lsp.get_clients({ bufnr = bufnr, method = 'textDocument/inlayHint' })
if not vim.iter(clients):any(function(c)
return c.id ~= ev.data.client_id
end) then
_disable(bufnr)
end
end,
group = augroup,
})
if not vim.iter(clients):any(function(c)
return c.id ~= ev.data.client_id
end) then
_disable(bufnr)
end
end)
api.nvim_set_decoration_provider(namespace, {
on_win = function(_, _, bufnr, topline, botline)
---@type vim.lsp.inlay_hint.bufstate

View File

@@ -40,6 +40,7 @@ local log = require('vim.lsp.log')
local protocol = require('vim.lsp.protocol')
local grammar = require('vim.lsp._snippet_grammar')
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local Capability = require('vim.lsp._capability')
@@ -78,20 +79,12 @@ Capability.all[Completor.name] = Completor
function Completor:new(buf)
self = Capability.new(self, buf)
self.client_state = {}
api.nvim_create_autocmd({ 'InsertEnter', 'CursorMovedI', 'TextChangedP' }, {
group = self.augroup,
buf = buf,
callback = function()
self:automatic_request()
end,
})
api.nvim_create_autocmd({ 'InsertLeave' }, {
group = self.augroup,
buf = buf,
callback = function()
self:abort()
end,
})
nvim_on({ 'InsertEnter', 'CursorMovedI', 'TextChangedP' }, self.augroup, { buf = buf }, function()
self:automatic_request()
end)
nvim_on({ 'InsertLeave' }, self.augroup, { buf = buf }, function()
self:abort()
end)
return self
end

View File

@@ -9,6 +9,7 @@
local util = require('vim.lsp.util')
local log = require('vim.lsp.log')
local nvim_on = require('vim._core.util').nvim_on
local lsp = vim.lsp
local method = 'textDocument/linkedEditingRange'
local Range = require('vim.treesitter._range')
@@ -201,30 +202,18 @@ function LinkedEditor.new(buf)
self.augroup = augroup
self.client_states = {}
api.nvim_create_autocmd({ 'TextChanged', 'TextChangedI' }, {
buf = buf,
group = augroup,
callback = function()
for _, client_state in pairs(self.client_states) do
update_ranges(buf, client_state)
end
self:refresh()
end,
})
api.nvim_create_autocmd('CursorMoved', {
group = augroup,
buf = buf,
callback = function()
self:refresh()
end,
})
api.nvim_create_autocmd('LspDetach', {
group = augroup,
buf = buf,
callback = function(ev)
self:detach(ev.data.client_id)
end,
})
nvim_on({ 'TextChanged', 'TextChangedI' }, augroup, { buf = buf }, function()
for _, client_state in pairs(self.client_states) do
update_ranges(buf, client_state)
end
self:refresh()
end)
nvim_on('CursorMoved', augroup, { buf = buf }, function()
self:refresh()
end)
nvim_on('LspDetach', augroup, { buf = buf }, function(ev)
self:detach(ev.data.client_id)
end)
LinkedEditor.active[buf] = self
return self
@@ -263,20 +252,19 @@ local function detach_linked_editor(bufnr, client)
linked_editor:detach(client.id)
end
api.nvim_create_autocmd('LspAttach', {
nvim_on('LspAttach', nil, {
desc = 'Enable linked editing ranges for all buffers this client attaches to, if enabled',
callback = function(ev)
local client = assert(lsp.get_client_by_id(ev.data.client_id))
if
not client._enabled_capabilities['linked_editing_range']
or not client:supports_method(method, ev.buf)
then
return
end
}, function(ev)
local client = assert(lsp.get_client_by_id(ev.data.client_id))
if
not client._enabled_capabilities['linked_editing_range']
or not client:supports_method(method, ev.buf)
then
return
end
attach_linked_editor(ev.buf, client)
end,
})
attach_linked_editor(ev.buf, client)
end)
---@param enable boolean
---@param client vim.lsp.Client
@@ -302,16 +290,14 @@ local function toggle_linked_editing_globally(enable)
-- If disabling, only clear the attachment autocmd. If enabling, create it.
local group = api.nvim_create_augroup('nvim.lsp.linked_editing_range', { clear = true })
if enable then
api.nvim_create_autocmd('LspAttach', {
group = group,
nvim_on('LspAttach', group, {
desc = 'Enable linked editing ranges for all clients',
callback = function(ev)
local client = assert(lsp.get_client_by_id(ev.data.client_id))
if client:supports_method(method, ev.buf) then
attach_linked_editor(ev.buf, client)
end
end,
})
}, function(ev)
local client = assert(lsp.get_client_by_id(ev.data.client_id))
if client:supports_method(method, ev.buf) then
attach_linked_editor(ev.buf, client)
end
end)
end
end

View File

@@ -1,4 +1,5 @@
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local lsp = vim.lsp
local util = lsp.util
local method = 'textDocument/onTypeFormatting'
@@ -157,27 +158,24 @@ local function attach(client, bufnr)
end
api.nvim_clear_autocmds({ group = augroup, buf = bufnr })
api.nvim_create_autocmd('LspDetach', {
nvim_on('LspDetach', augroup, {
buf = bufnr,
desc = 'Detach on-type formatting module when the client detaches',
group = augroup,
callback = function(ev)
local detached_client = assert(lsp.get_client_by_id(ev.data.client_id))
detach(detached_client, bufnr)
end,
})
}, function(ev)
local detached_client = assert(lsp.get_client_by_id(ev.data.client_id))
detach(detached_client, bufnr)
end)
end
api.nvim_create_autocmd('LspAttach', {
nvim_on('LspAttach', nil, {
desc = 'Enable on-type formatting for all buffers with individually-enabled clients.',
callback = function(ev)
local buf = ev.buf
local client = assert(lsp.get_client_by_id(ev.data.client_id))
if client._otf_enabled then
attach(client, buf)
end
end,
})
}, function(ev)
local buf = ev.buf
local client = assert(lsp.get_client_by_id(ev.data.client_id))
if client._otf_enabled then
attach(client, buf)
end
end)
---@param enable boolean
---@param client vim.lsp.Client
@@ -203,16 +201,14 @@ local function toggle_globally(enable)
-- If disabling, only clear the attachment autocmd. If enabling, create it as well.
local group = api.nvim_create_augroup('nvim.lsp.on_type_formatting', { clear = true })
if enable then
api.nvim_create_autocmd('LspAttach', {
group = group,
nvim_on('LspAttach', group, {
desc = 'Enable on-type formatting for ALL clients by default.',
callback = function(ev)
local client = assert(lsp.get_client_by_id(ev.data.client_id))
if client._otf_enabled ~= false then
attach(client, ev.buf)
end
end,
})
}, function(ev)
local client = assert(lsp.get_client_by_id(ev.data.client_id))
if client._otf_enabled ~= false then
attach(client, ev.buf)
end
end)
end
end

View File

@@ -1,4 +1,5 @@
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local bit = require('bit')
local util = require('vim.lsp.util')
local Range = require('vim.treesitter._range')
@@ -257,22 +258,14 @@ function STHighlighter:on_attach(client_id)
self.client_state[client_id] = state
end
api.nvim_create_autocmd({ 'BufWinEnter', 'InsertLeave' }, {
buf = self.bufnr,
group = self.augroup,
callback = function()
self:send_request()
end,
})
nvim_on({ 'BufWinEnter', 'InsertLeave' }, self.augroup, { buf = self.bufnr }, function()
self:send_request()
end)
if state.supports_range then
api.nvim_create_autocmd('WinScrolled', {
buf = self.bufnr,
group = self.augroup,
callback = function()
self:on_change()
end,
})
nvim_on('WinScrolled', self.augroup, { buf = self.bufnr }, function()
self:on_change()
end)
end
self:send_request()

View File

@@ -1,6 +1,7 @@
local protocol = require('vim.lsp.protocol')
local validate = vim.validate
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local list_extend = vim.list_extend
local uv = vim.uv
@@ -1320,28 +1321,20 @@ local function close_preview_autocmd(events, winnr, floating_bufnr, bufnr)
-- close the preview window when entered a buffer that is not
-- the floating window buffer or the buffer that spawned it
api.nvim_create_autocmd('BufLeave', {
group = augroup,
buf = bufnr,
callback = function()
vim.schedule(function()
-- When jumping to the quickfix window from the preview window,
-- do not close the preview window.
if api.nvim_get_option_value('filetype', { buf = 0 }) ~= 'qf' then
close_preview_window(winnr, { floating_bufnr, bufnr })
end
end)
end,
})
nvim_on('BufLeave', augroup, { buf = bufnr }, function()
vim.schedule(function()
-- When jumping to the quickfix window from the preview window,
-- do not close the preview window.
if api.nvim_get_option_value('filetype', { buf = 0 }) ~= 'qf' then
close_preview_window(winnr, { floating_bufnr, bufnr })
end
end)
end)
if #events > 0 then
api.nvim_create_autocmd(events, {
group = augroup,
buf = bufnr,
callback = function()
close_preview_window(winnr)
end,
})
nvim_on(events, augroup, { buf = bufnr }, function()
close_preview_window(winnr)
end)
end
end
@@ -1593,9 +1586,10 @@ function M.open_floating_preview(contents, syntax, opts)
api.nvim_win_set_var(floating_winnr, 'lsp_floating_bufnr', bufnr)
end
api.nvim_create_autocmd('WinClosed', {
group = api.nvim_create_augroup('nvim.closing_floating_preview', { clear = true }),
callback = function(args)
nvim_on(
'WinClosed',
api.nvim_create_augroup('nvim.closing_floating_preview', { clear = true }),
function(args)
local winid = vim._tointeger(args.match)
local preview_bufnr = vim.w[winid].lsp_floating_bufnr
if
@@ -1606,8 +1600,8 @@ function M.open_floating_preview(contents, syntax, opts)
vim.b[bufnr].lsp_floating_preview = nil
return true
end
end,
})
end
)
vim.wo[floating_winnr].foldenable = false -- Disable folding.
vim.wo[floating_winnr].wrap = opts.wrap -- Soft wrapping.

View File

@@ -232,6 +232,7 @@ local api = vim.api
local uv = vim.uv
local async = require('vim._async')
local util = require('vim._core.util')
local nvim_on = util.nvim_on
local N_ = vim.fn.gettext
local M = {}
@@ -1199,7 +1200,7 @@ local function show_confirm_buf(lines, on_finish)
delete_buffer()
end
-- - Use `nested` to allow other events (useful for statuslines)
api.nvim_create_autocmd('BufWriteCmd', { buf = bufnr, nested = true, callback = finish })
nvim_on('BufWriteCmd', nil, { buf = bufnr, nested = true }, finish)
-- Define action to cancel confirm
--- @type integer
@@ -1211,7 +1212,7 @@ local function show_confirm_buf(lines, on_finish)
pcall(api.nvim_del_autocmd, cancel_au_id)
delete_buffer()
end
cancel_au_id = api.nvim_create_autocmd('WinClosed', { nested = true, callback = on_cancel })
cancel_au_id = nvim_on('WinClosed', nil, { nested = true }, on_cancel)
-- Set buffer-local options last (so that user autocmmands could override)
vim.bo[bufnr].modified = false

View File

@@ -399,103 +399,97 @@ end
---
--- @param bufnr integer
local function setup_autocmds(bufnr)
vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
group = snippet_group,
local nvim_on = require('vim._core.util').nvim_on
nvim_on({ 'CursorMoved', 'CursorMovedI' }, snippet_group, {
desc = 'Update snippet state when the cursor moves',
buf = bufnr,
callback = function()
-- Just update the tabstop in insert and select modes.
if not vim.fn.mode():match('^[isS]') then
return
end
}, function()
-- Just update the tabstop in insert and select modes.
if not vim.fn.mode():match('^[isS]') then
return
end
local cursor_row, cursor_col = cursor_pos()
local cursor_row, cursor_col = cursor_pos()
-- The cursor left the snippet region.
local snippet_range = get_extmark_range(bufnr, M._session.extmark_id)
if
cursor_row < snippet_range[1]
or (cursor_row == snippet_range[1] and cursor_col < snippet_range[2])
or cursor_row > snippet_range[3]
or (cursor_row == snippet_range[3] and cursor_col > snippet_range[4])
then
M.stop()
return true
end
-- The cursor left the snippet region.
local snippet_range = get_extmark_range(bufnr, M._session.extmark_id)
if
cursor_row < snippet_range[1]
or (cursor_row == snippet_range[1] and cursor_col < snippet_range[2])
or cursor_row > snippet_range[3]
or (cursor_row == snippet_range[3] and cursor_col > snippet_range[4])
then
M.stop()
return true
end
for tabstop_index, tabstops in pairs(M._session.tabstops) do
for _, tabstop in ipairs(tabstops) do
local range = tabstop:get_range()
if
(cursor_row > range[1] or (cursor_row == range[1] and cursor_col >= range[2]))
and (cursor_row < range[3] or (cursor_row == range[3] and cursor_col <= range[4]))
then
if tabstop_index ~= 0 then
return
end
for tabstop_index, tabstops in pairs(M._session.tabstops) do
for _, tabstop in ipairs(tabstops) do
local range = tabstop:get_range()
if
(cursor_row > range[1] or (cursor_row == range[1] and cursor_col >= range[2]))
and (cursor_row < range[3] or (cursor_row == range[3] and cursor_col <= range[4]))
then
if tabstop_index ~= 0 then
return
end
end
end
end
-- The cursor is either not on a tabstop or we reached the end, so exit the session.
M.stop()
return true
end,
})
-- The cursor is either not on a tabstop or we reached the end, so exit the session.
M.stop()
return true
end)
vim.api.nvim_create_autocmd({ 'TextChanged', 'TextChangedI', 'TextChangedP' }, {
group = snippet_group,
nvim_on({ 'TextChanged', 'TextChangedI', 'TextChangedP' }, snippet_group, {
desc = 'Update active tabstops when buffer text changes',
buf = bufnr,
callback = function()
-- Check that the snippet hasn't been deleted.
local snippet_range = get_extmark_range(M._session.bufnr, M._session.extmark_id)
if
(snippet_range[1] == snippet_range[3] and snippet_range[2] == snippet_range[4])
or snippet_range[3] + 1 > vim.fn.line('$')
then
M.stop()
end
}, function()
-- Check that the snippet hasn't been deleted.
local snippet_range = get_extmark_range(M._session.bufnr, M._session.extmark_id)
if
(snippet_range[1] == snippet_range[3] and snippet_range[2] == snippet_range[4])
or snippet_range[3] + 1 > vim.fn.line('$')
then
M.stop()
end
if not M.active() then
return true
end
if not M.active() then
return true
end
-- Sync the tabstops in the current group.
local current_tabstop = M._session.current_tabstop
local current_text = current_tabstop:get_text()
for _, tabstop in ipairs(M._session.tabstops[current_tabstop.index]) do
if tabstop.extmark_id ~= current_tabstop.extmark_id then
tabstop:set_text(current_text)
end
-- Sync the tabstops in the current group.
local current_tabstop = M._session.current_tabstop
local current_text = current_tabstop:get_text()
for _, tabstop in ipairs(M._session.tabstops[current_tabstop.index]) do
if tabstop.extmark_id ~= current_tabstop.extmark_id then
tabstop:set_text(current_text)
end
end,
})
end
end)
vim.api.nvim_create_autocmd('BufLeave', {
group = snippet_group,
nvim_on('BufLeave', snippet_group, {
desc = 'Stop the snippet session when leaving the buffer',
buf = bufnr,
callback = function()
M.stop()
end,
})
vim.api.nvim_create_autocmd('ModeChanged', {
group = snippet_group,
}, function()
M.stop()
end)
nvim_on('ModeChanged', snippet_group, {
desc = 'Stop the snippet session when leaving select mode',
buf = bufnr,
callback = function(args)
if args.match ~= 's:n' then
}, function(args)
if args.match ~= 's:n' then
return
end
vim.schedule(function()
if not M.active() or vim.api.nvim_get_mode().mode:match('^[siRS\19]') then
return
end
vim.schedule(function()
if not M.active() or vim.api.nvim_get_mode().mode:match('^[siRS\19]') then
return
end
M.stop()
end)
end,
})
M.stop()
end)
end)
end
--- Expands the given snippet text.

View File

@@ -3,6 +3,7 @@ local ts = vim.treesitter
local Range = require('vim.treesitter._range')
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
---Treesitter folding is done in two steps:
---(1) compute the fold levels with the syntax tree and cache the result (`compute_folds_levels`)
@@ -226,14 +227,9 @@ function FoldInfo:foldupdate(bufnr, srow, erow)
})) > 0 then
return
end
api.nvim_create_autocmd('InsertLeave', {
group = group,
buf = bufnr,
once = true,
callback = function()
self:do_foldupdate(bufnr)
end,
})
nvim_on('InsertLeave', group, { buf = bufnr, once = true }, function()
self:do_foldupdate(bufnr)
end)
return
end
@@ -391,13 +387,9 @@ function M.foldexpr(lnum)
if not foldinfos[bufnr] then
foldinfos[bufnr] = FoldInfo.new(bufnr)
api.nvim_create_autocmd({ 'BufUnload', 'VimEnter', 'FileType' }, {
buf = bufnr,
once = true,
callback = function()
foldinfos[bufnr] = nil
end,
})
nvim_on({ 'BufUnload', 'VimEnter', 'FileType' }, nil, { buf = bufnr, once = true }, function()
foldinfos[bufnr] = nil
end)
local parser = foldinfos[bufnr].parser
if not parser then
@@ -441,28 +433,27 @@ function M.foldexpr(lnum)
return foldinfos[bufnr].levels[lnum] or '0'
end
api.nvim_create_autocmd('OptionSet', {
nvim_on('OptionSet', group, {
pattern = { 'foldminlines', 'foldnestmax' },
desc = 'Refresh treesitter folds',
callback = function()
local buf = api.nvim_get_current_buf()
local bufs = vim.v.option_type == 'global' and vim.tbl_keys(foldinfos)
or foldinfos[buf] and { buf }
or {}
for _, bufnr in ipairs(bufs) do
local foldinfo = FoldInfo.new(bufnr)
foldinfos[bufnr] = foldinfo
api.nvim_buf_call(bufnr, function()
compute_folds_levels(bufnr, foldinfo, nil, nil, function()
-- FileType/BufUnload can clear or replace the fold state while this
-- async parse is in flight. Ignore callbacks for stale generations.
if foldinfos[bufnr] ~= foldinfo then
return
end
foldinfo:foldupdate(bufnr, 0, api.nvim_buf_line_count(bufnr))
end)
}, function()
local buf = api.nvim_get_current_buf()
local bufs = vim.v.option_type == 'global' and vim.tbl_keys(foldinfos)
or foldinfos[buf] and { buf }
or {}
for _, bufnr in ipairs(bufs) do
local foldinfo = FoldInfo.new(bufnr)
foldinfos[bufnr] = foldinfo
api.nvim_buf_call(bufnr, function()
compute_folds_levels(bufnr, foldinfo, nil, nil, function()
-- FileType/BufUnload can clear or replace the fold state while this
-- async parse is in flight. Ignore callbacks for stale generations.
if foldinfos[bufnr] ~= foldinfo then
return
end
foldinfo:foldupdate(bufnr, 0, api.nvim_buf_line_count(bufnr))
end)
end
end,
})
end)
end
end)
return M

View File

@@ -1,4 +1,5 @@
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local Range = require('vim.treesitter._range')
@@ -469,107 +470,83 @@ function M.inspect_tree(opts)
local group = api.nvim_create_augroup('nvim.treesitter.dev', {})
api.nvim_create_autocmd('CursorMoved', {
group = group,
buf = b,
callback = function()
if not api.nvim_buf_is_loaded(buf) then
return true
end
w = api.nvim_get_current_win()
api.nvim_buf_clear_namespace(buf, treeview.ns, 0, -1)
local row = api.nvim_win_get_cursor(w)[1]
local lnum, col, end_lnum, end_col = treeview:get(row).node:range()
api.nvim_buf_set_extmark(buf, treeview.ns, lnum, col, {
end_row = end_lnum,
end_col = math.max(0, end_col),
hl_group = 'Visual',
})
-- update source window if original was closed
if not api.nvim_win_is_valid(win) then
win = assert(vim.fn.win_findbuf(buf)[1])
end
local topline, botline = vim.fn.line('w0', win), vim.fn.line('w$', win)
-- Move the cursor if highlighted range is completely out of view
if lnum < topline and end_lnum < topline then
api.nvim_win_set_cursor(win, { end_lnum + 1, 0 })
elseif lnum > botline and end_lnum > botline then
api.nvim_win_set_cursor(win, { lnum + 1, 0 })
end
end,
})
api.nvim_create_autocmd('CursorMoved', {
group = group,
buf = buf,
callback = function()
if not api.nvim_buf_is_loaded(b) then
return true
end
set_inspector_cursor(treeview, opts.lang, buf, b, w)
end,
})
api.nvim_create_autocmd({ 'TextChanged', 'InsertLeave' }, {
group = group,
buf = buf,
callback = function()
if not api.nvim_buf_is_loaded(b) then
return true
end
local treeview_opts = treeview.opts
treeview = assert(TSTreeView:new(buf, opts.lang))
treeview.opts = treeview_opts
treeview:draw(b)
end,
})
api.nvim_create_autocmd('BufLeave', {
group = group,
buf = b,
callback = function()
if not api.nvim_buf_is_loaded(buf) then
return true
end
api.nvim_buf_clear_namespace(buf, treeview.ns, 0, -1)
end,
})
api.nvim_create_autocmd('BufLeave', {
group = group,
buf = buf,
callback = function()
if not api.nvim_buf_is_loaded(b) then
return true
end
api.nvim_buf_clear_namespace(b, treeview.ns, 0, -1)
end,
})
api.nvim_create_autocmd({ 'BufHidden', 'BufUnload', 'QuitPre' }, {
group = group,
buf = buf,
callback = function()
-- don't close inpector window if source buffer
-- has more than one open window
if #vim.fn.win_findbuf(buf) > 1 then
return
end
-- close all tree windows
for _, window in pairs(vim.fn.win_findbuf(b)) do
close_win(window)
end
nvim_on('CursorMoved', group, { buf = b }, function()
if not api.nvim_buf_is_loaded(buf) then
return true
end,
})
end
w = api.nvim_get_current_win()
api.nvim_buf_clear_namespace(buf, treeview.ns, 0, -1)
local row = api.nvim_win_get_cursor(w)[1]
local lnum, col, end_lnum, end_col = treeview:get(row).node:range()
api.nvim_buf_set_extmark(buf, treeview.ns, lnum, col, {
end_row = end_lnum,
end_col = math.max(0, end_col),
hl_group = 'Visual',
})
-- update source window if original was closed
if not api.nvim_win_is_valid(win) then
win = assert(vim.fn.win_findbuf(buf)[1])
end
local topline, botline = vim.fn.line('w0', win), vim.fn.line('w$', win)
-- Move the cursor if highlighted range is completely out of view
if lnum < topline and end_lnum < topline then
api.nvim_win_set_cursor(win, { end_lnum + 1, 0 })
elseif lnum > botline and end_lnum > botline then
api.nvim_win_set_cursor(win, { lnum + 1, 0 })
end
end)
nvim_on('CursorMoved', group, { buf = buf }, function()
if not api.nvim_buf_is_loaded(b) then
return true
end
set_inspector_cursor(treeview, opts.lang, buf, b, w)
end)
nvim_on({ 'TextChanged', 'InsertLeave' }, group, { buf = buf }, function()
if not api.nvim_buf_is_loaded(b) then
return true
end
local treeview_opts = treeview.opts
treeview = assert(TSTreeView:new(buf, opts.lang))
treeview.opts = treeview_opts
treeview:draw(b)
end)
nvim_on('BufLeave', group, { buf = b }, function()
if not api.nvim_buf_is_loaded(buf) then
return true
end
api.nvim_buf_clear_namespace(buf, treeview.ns, 0, -1)
end)
nvim_on('BufLeave', group, { buf = buf }, function()
if not api.nvim_buf_is_loaded(b) then
return true
end
api.nvim_buf_clear_namespace(b, treeview.ns, 0, -1)
end)
nvim_on({ 'BufHidden', 'BufUnload', 'QuitPre' }, group, { buf = buf }, function()
-- don't close inpector window if source buffer
-- has more than one open window
if #vim.fn.win_findbuf(buf) > 1 then
return
end
-- close all tree windows
for _, window in pairs(vim.fn.win_findbuf(b)) do
close_win(window)
end
return true
end)
end
local edit_ns = api.nvim_create_namespace('nvim.treesitter.dev_edit')
@@ -671,53 +648,43 @@ function M.edit_query(lang)
api.nvim_buf_set_name(query_buf, string.format('%s/query_editor.scm', lang))
local group = api.nvim_create_augroup('nvim.treesitter.dev_edit', {})
api.nvim_create_autocmd({ 'TextChanged', 'InsertLeave' }, {
group = group,
nvim_on({ 'TextChanged', 'InsertLeave' }, group, {
buf = query_buf,
desc = 'Update query editor diagnostics when the query changes',
callback = function()
vim.treesitter.query.lint(query_buf, { langs = lang, clear = false })
end,
})
api.nvim_create_autocmd({ 'TextChanged', 'InsertLeave', 'CursorMoved', 'BufEnter' }, {
group = group,
}, function()
vim.treesitter.query.lint(query_buf, { langs = lang, clear = false })
end)
nvim_on({ 'TextChanged', 'InsertLeave', 'CursorMoved', 'BufEnter' }, group, {
buf = query_buf,
desc = 'Update query editor highlights when the cursor moves',
callback = function()
if api.nvim_win_is_valid(win) then
update_editor_highlights(query_win, win, lang)
end
end,
})
api.nvim_create_autocmd('BufLeave', {
group = group,
}, function()
if api.nvim_win_is_valid(win) then
update_editor_highlights(query_win, win, lang)
end
end)
nvim_on('BufLeave', group, {
buf = query_buf,
desc = 'Clear highlights when leaving the query editor',
callback = function()
api.nvim_buf_clear_namespace(buf, edit_ns, 0, -1)
end,
})
api.nvim_create_autocmd('BufLeave', {
group = group,
}, function()
api.nvim_buf_clear_namespace(buf, edit_ns, 0, -1)
end)
nvim_on('BufLeave', group, {
buf = buf,
desc = 'Clear the query editor highlights when leaving the source buffer',
callback = function()
if not api.nvim_buf_is_loaded(query_buf) then
return true
end
}, function()
if not api.nvim_buf_is_loaded(query_buf) then
return true
end
api.nvim_buf_clear_namespace(query_buf, edit_ns, 0, -1)
end,
})
api.nvim_create_autocmd({ 'BufHidden', 'BufUnload' }, {
group = group,
api.nvim_buf_clear_namespace(query_buf, edit_ns, 0, -1)
end)
nvim_on({ 'BufHidden', 'BufUnload' }, group, {
buf = buf,
desc = 'Close the editor window when the source buffer is hidden or unloaded',
once = true,
callback = function()
close_win(query_win)
end,
})
}, function()
close_win(query_win)
end)
api.nvim_buf_set_lines(query_buf, 0, -1, false, {
';; Write queries here (see $VIMRUNTIME/queries/ for examples).',

View File

@@ -2,6 +2,7 @@
--- text. See |vim.treesitter.query.parse()| for a working example.
local api = vim.api
local nvim_on = require('vim._core.util').nvim_on
local language = require('vim.treesitter.language')
local memoize = vim.func._memoize
local cmp_ge = require('vim.treesitter._range').cmp_pos.ge
@@ -333,14 +334,17 @@ M.get = memoize('concat-2', function(lang, query_name)
return M.parse(lang, query_string)
end, false)
api.nvim_create_autocmd('OptionSet', {
pattern = { 'runtimepath' },
group = api.nvim_create_augroup('nvim.treesitter.query_cache_reset', { clear = true }),
callback = function()
nvim_on(
'OptionSet',
api.nvim_create_augroup('nvim.treesitter.query_cache_reset', { clear = true }),
{
pattern = { 'runtimepath' },
},
function()
--- @diagnostic disable-next-line: undefined-field LuaLS bad at generics
M.get:clear()
end,
})
end
)
--- Parses a {query} string and returns a `Query` object (|lua-treesitter-query|), which can be used
--- to search the tree for the query patterns (via |Query:iter_captures()|, |Query:iter_matches()|),

View File

@@ -28,18 +28,19 @@ function M.request(payload, opts, on_response)
timer = assert(vim.uv.new_timer())
end
local id = vim.api.nvim_create_autocmd('TermResponse', {
group = opts.group,
nested = true,
callback = function(ev)
local id = require('vim._core.util').nvim_on(
'TermResponse',
opts.group,
{ nested = true },
function(ev)
local stop = on_response(ev.data.sequence)
-- If on_response is done, cancel the timeout so on_timeout doesn't fire spuriously.
if stop and timer and not timer:is_closing() then
timer:close()
end
return stop
end,
})
end
)
if payload ~= '' then
vim.api.nvim_ui_send(payload)

View File

@@ -342,11 +342,13 @@ do
--- Initialize Progress handlers.
local function progress_init()
progress_group = vim.api.nvim_create_augroup('nvim.ui.progress_status', { clear = true })
progress_autocmd = vim.api.nvim_create_autocmd('Progress', {
group = progress_group,
desc = 'Tracks progress messages for vim.ui.progress_status()',
---@param ev {data: vim.event.progress.data}
callback = function(ev)
progress_autocmd = require('vim._core.util').nvim_on(
'Progress',
progress_group,
{
desc = 'Tracks progress messages for vim.ui.progress_status()',
}, ---@param ev {data: vim.event.progress.data}
function(ev)
if not ev.data or not ev.data.id then
return
end
@@ -362,8 +364,8 @@ do
then
progress[ev.data.id] = nil
end
end,
})
end
)
end
--- Gets a status description summarizing currently running progress messages.

View File

@@ -1,3 +1,5 @@
local nvim_on = require('vim._core.util').nvim_on
local M = {}
---@brief
@@ -142,10 +144,8 @@ function M._supported(opts)
return require('vim.ui.img._kitty').supported(opts)
end
vim.api.nvim_create_autocmd('VimLeavePre', {
callback = function()
M.del(math.huge)
end,
})
nvim_on('VimLeavePre', nil, function()
M.del(math.huge)
end)
return M

View File

@@ -324,13 +324,10 @@ describe('startup', function()
os.remove('Xtest_shada')
end)
assert_l_out(
'updatecount=0 shadafile=NONE loadplugins=false scripts=1\n',
nil,
nil,
'-',
script
)
assert_l_out(function(out)
-- Accept scripts=2 for PUC Lua where `vim._core.util` is sourced from disk instead of a preload blob.
return matches('updatecount=0 shadafile=NONE loadplugins=false scripts=[12]\n', out)
end, nil, nil, '-', script)
-- User can override.
assert_l_out(