feat(vim.hl): vim.hl.hl_op() #39777

Problem:
vim.hl.on_yank() only works for TextYankPost, not TextPutPost.

Solution:
Introduce hl_op().
Deprecate on_yank().
This commit is contained in:
Ayose C.
2026-05-17 13:56:37 +00:00
committed by GitHub
parent 8e1d6dfada
commit 3ace049c6a
6 changed files with 101 additions and 41 deletions

View File

@@ -8,7 +8,7 @@ local M = {}
--- - `semantic_tokens`: `125`, used for LSP semantic token highlighting
--- - `diagnostics`: `150`, used for code analysis such as diagnostics
--- - `user`: `200`, used for user-triggered highlights such as LSP document
--- symbols or `on_yank` autocommands
--- symbols or `hl_op` autocommands
M.priorities = {
syntax = 50,
treesitter = 100,
@@ -144,26 +144,34 @@ function M.range(buf, ns, higroup, start, finish, opts)
end
end
local yank_timer --- @type uv.uv_timer_t?
local yank_hl_clear --- @type fun()?
local yank_ns = api.nvim_create_namespace('nvim.hlyank')
---@private
---@class (private) vim.hl.OnEventState
---@field timer? uv.uv_timer_t Timer to clear the highlight.
---@field clear? fun() Function to clear the highlight immediately.
--- Highlight the yanked text during a |TextYankPost| event.
--- @type { [string]: vim.hl.OnEventState }
local hl_op_state = {}
local events_ns = api.nvim_create_namespace('nvim.hl.events')
--- Highlight the related text region during a |TextYankPost| or |TextPutPost|
--- event.
---
--- Add the following to your `init.vim`:
---
--- ```vim
--- autocmd TextYankPost * silent! lua vim.hl.on_yank {higroup='Visual', timeout=300}
--- autocmd TextYankPost * silent! lua vim.hl.hl_op {higroup='Visual', timeout=300}
--- autocmd TextPutPost * silent! lua vim.hl.hl_op {higroup='Visual', timeout=300}
--- ```
---
--- @param opts table|nil Optional parameters
--- - event event structure (default vim.v.event)
--- - higroup highlight group for yanked region (default "IncSearch")
--- - higroup highlight group for the text region (default "IncSearch")
--- - on_macro highlight when executing macro (default false)
--- - on_visual highlight when yanking visual selection (default true)
--- - on_visual highlight when the event is in |Visual| mode (default true)
--- - priority integer priority (default |vim.hl.priorities|`.user`)
--- - timeout time in ms before highlight is cleared (default 150)
function M.on_yank(opts)
function M.hl_op(opts)
vim.validate('opts', opts, 'table', true)
opts = opts or {}
local event = opts.event or vim.v.event
@@ -173,31 +181,52 @@ function M.on_yank(opts)
if not on_macro and vim.fn.reg_executing() ~= '' then
return
end
if event.operator ~= 'y' or event.regtype == '' then
if event.regtype == '' then
return
end
if not on_visual and event.visual then
return
end
local state_key --- @type string
if event.operator == 'y' then
state_key = 'yank'
elseif event.operator == 'p' or event.operator == 'P' then
state_key = 'put'
else
return
end
local higroup = opts.higroup or 'IncSearch'
local bufnr = api.nvim_get_current_buf()
local winid = api.nvim_get_current_win()
if yank_timer and not yank_timer:is_closing() then
yank_timer:close()
assert(yank_hl_clear)
yank_hl_clear()
local state = hl_op_state[state_key]
if state ~= nil and state.timer and not state.timer:is_closing() then
state.timer:close()
assert(state.clear)
state.clear()
end
api.nvim__ns_set(yank_ns, { wins = { winid } })
yank_timer, yank_hl_clear = M.range(bufnr, yank_ns, higroup, "'[", "']", {
api.nvim__ns_set(events_ns, { wins = { winid } })
local timer, clear = M.range(bufnr, events_ns, higroup, "'[", "']", {
regtype = event.regtype,
inclusive = true,
priority = opts.priority or M.priorities.user,
timeout = opts.timeout or 150,
})
hl_op_state[state_key] = {
timer = timer,
clear = clear,
}
end
--- @deprecated Please use |vim.hl.hl_op()| instead.
function M.on_yank(opts)
vim.deprecate('vim.hl.on_yank', 'vim.hl.hl_op', '0.13')
return M.hl_op(opts)
end
return M