mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 19:38:20 +00:00
feat(vim.hl): allow multiple timed highlights simultaneously #33283
Problem: Currently vim.hl.range only allows one timed highlight.
Creating another one, removes the old one.
Solution: vim.hl.range now returns a timer and a function. The timer
keeps track of how much time is left in the highlight and the function
allows you to clear it, letting the user decide what to do with old
highlights.
(cherry picked from commit eae2d3b145
)
This commit is contained in:

committed by
siddhantdev

parent
dd547ef1ea
commit
5829b5de0a
@@ -640,6 +640,12 @@ vim.hl.range({bufnr}, {ns}, {higroup}, {start}, {finish}, {opts})
|
||||
• {timeout}? (`integer`, default: -1 no timeout) Time in ms
|
||||
before highlight is cleared
|
||||
|
||||
Return (multiple): ~
|
||||
(`uv.uv_timer_t?`) range_timer A timer which manages how much time the
|
||||
highlight has left
|
||||
(`fun()?`) range_clear A function which allows clearing the highlight
|
||||
manually. nil is returned if timeout is not specified
|
||||
|
||||
|
||||
==============================================================================
|
||||
VIM.DIFF *vim.diff*
|
||||
|
@@ -176,7 +176,7 @@ API
|
||||
aligned text that truncates before covering up buffer text.
|
||||
• `virt_lines_overflow` field accepts value `scroll` to enable horizontal
|
||||
scrolling for virtual lines with 'nowrap'.
|
||||
• |vim.hl.range()| now has a optional `timeout` field which allows for a timed highlight
|
||||
• |vim.hl.range()| now has a optional `timeout` field which allows for multiple timed highlights
|
||||
|
||||
DEFAULTS
|
||||
|
||||
|
@@ -17,9 +17,6 @@ M.priorities = {
|
||||
user = 200,
|
||||
}
|
||||
|
||||
local range_timer --- @type uv.uv_timer_t?
|
||||
local range_hl_clear --- @type fun()?
|
||||
|
||||
--- @class vim.hl.range.Opts
|
||||
--- @inlinedoc
|
||||
---
|
||||
@@ -47,6 +44,10 @@ local range_hl_clear --- @type fun()?
|
||||
---@param start integer[]|string Start of region as a (line, column) tuple or string accepted by |getpos()|
|
||||
---@param finish integer[]|string End of region as a (line, column) tuple or string accepted by |getpos()|
|
||||
---@param opts? vim.hl.range.Opts
|
||||
--- @return uv.uv_timer_t? range_timer A timer which manages how much time the
|
||||
--- highlight has left
|
||||
--- @return fun()? range_clear A function which allows clearing the highlight manually.
|
||||
--- nil is returned if timeout is not specified
|
||||
function M.range(bufnr, ns, higroup, start, finish, opts)
|
||||
opts = opts or {}
|
||||
local regtype = opts.regtype or 'v'
|
||||
@@ -108,38 +109,38 @@ function M.range(bufnr, ns, higroup, start, finish, opts)
|
||||
end
|
||||
end
|
||||
|
||||
if range_timer and not range_timer:is_closing() then
|
||||
range_timer:close()
|
||||
assert(range_hl_clear)
|
||||
range_hl_clear()
|
||||
end
|
||||
|
||||
range_hl_clear = function()
|
||||
range_timer = nil
|
||||
range_hl_clear = nil
|
||||
pcall(vim.api.nvim_buf_clear_namespace, bufnr, ns, 0, -1)
|
||||
pcall(vim.api.nvim__ns_set, { wins = {} })
|
||||
end
|
||||
|
||||
local extmarks = {} --- @type integer[]
|
||||
for _, res in ipairs(region) do
|
||||
local start_row = res[1][2] - 1
|
||||
local start_col = res[1][3] - 1
|
||||
local end_row = res[2][2] - 1
|
||||
local end_col = res[2][3]
|
||||
api.nvim_buf_set_extmark(bufnr, ns, start_row, start_col, {
|
||||
hl_group = higroup,
|
||||
end_row = end_row,
|
||||
end_col = end_col,
|
||||
priority = priority,
|
||||
strict = false,
|
||||
})
|
||||
table.insert(
|
||||
extmarks,
|
||||
api.nvim_buf_set_extmark(bufnr, ns, start_row, start_col, {
|
||||
hl_group = higroup,
|
||||
end_row = end_row,
|
||||
end_col = end_col,
|
||||
priority = priority,
|
||||
strict = false,
|
||||
})
|
||||
)
|
||||
end
|
||||
|
||||
local range_hl_clear = function()
|
||||
for _, mark in ipairs(extmarks) do
|
||||
api.nvim_buf_del_extmark(bufnr, ns, mark)
|
||||
end
|
||||
end
|
||||
|
||||
if timeout ~= -1 then
|
||||
range_timer = vim.defer_fn(range_hl_clear, timeout)
|
||||
local range_timer = vim.defer_fn(range_hl_clear, timeout)
|
||||
return range_timer, range_hl_clear
|
||||
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')
|
||||
|
||||
--- Highlight the yanked text during a |TextYankPost| event.
|
||||
@@ -179,8 +180,14 @@ function M.on_yank(opts)
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local winid = vim.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()
|
||||
end
|
||||
|
||||
vim.api.nvim__ns_set(yank_ns, { wins = { winid } })
|
||||
M.range(bufnr, yank_ns, higroup, "'[", "']", {
|
||||
yank_timer, yank_hl_clear = M.range(bufnr, yank_ns, higroup, "'[", "']", {
|
||||
regtype = event.regtype,
|
||||
inclusive = true,
|
||||
priority = opts.priority or M.priorities.user,
|
||||
|
@@ -139,6 +139,80 @@ describe('vim.hl.range', function()
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
it('shows multiple highlights with different timeouts simultaneously', function()
|
||||
local timeout1 = 300
|
||||
local timeout2 = 600
|
||||
exec_lua(function()
|
||||
local ns = vim.api.nvim_create_namespace('')
|
||||
vim.hl.range(0, ns, 'Search', { 0, 0 }, { 4, 0 }, { timeout = timeout1 })
|
||||
vim.hl.range(0, ns, 'Search', { 2, 6 }, { 3, 5 }, { timeout = timeout2 })
|
||||
end)
|
||||
screen:expect({
|
||||
grid = [[
|
||||
{10:^asdfghjkl}{100:$} |
|
||||
{10:«口=口»}{100:$} |
|
||||
{10:qwertyuiop}{100:$} |
|
||||
{10:口口=口口}{1:$} |
|
||||
zxcvbnm{1:$} |
|
||||
|
|
||||
]],
|
||||
timeout = timeout1 / 3,
|
||||
})
|
||||
screen:expect({
|
||||
grid = [[
|
||||
^asdfghjkl{1:$} |
|
||||
«口=口»{1:$} |
|
||||
qwerty{10:uiop}{100:$} |
|
||||
{10:口口}=口口{1:$} |
|
||||
zxcvbnm{1:$} |
|
||||
|
|
||||
]],
|
||||
timeout = timeout1 + ((timeout2 - timeout1) / 3),
|
||||
})
|
||||
screen:expect([[
|
||||
^asdfghjkl{1:$} |
|
||||
«口=口»{1:$} |
|
||||
qwertyuiop{1:$} |
|
||||
口口=口口{1:$} |
|
||||
zxcvbnm{1:$} |
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
it('allows cancelling a highlight that has not timed out', function()
|
||||
exec_lua(function()
|
||||
local timeout = 3000
|
||||
local range_timer
|
||||
local range_hl_clear
|
||||
local ns = vim.api.nvim_create_namespace('')
|
||||
range_timer, range_hl_clear = vim.hl.range(
|
||||
0,
|
||||
ns,
|
||||
'Search',
|
||||
{ 0, 0 },
|
||||
{ 4, 0 },
|
||||
{ timeout = timeout }
|
||||
)
|
||||
if range_timer and not range_timer:is_closing() then
|
||||
range_timer:close()
|
||||
assert(range_hl_clear)
|
||||
range_hl_clear()
|
||||
range_hl_clear() -- Exercise redundant call
|
||||
end
|
||||
end)
|
||||
screen:expect({
|
||||
grid = [[
|
||||
^asdfghjkl{1:$} |
|
||||
«口=口»{1:$} |
|
||||
qwertyuiop{1:$} |
|
||||
口口=口口{1:$} |
|
||||
zxcvbnm{1:$} |
|
||||
|
|
||||
]],
|
||||
unchanged = true,
|
||||
})
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('vim.hl.on_yank', function()
|
||||
|
Reference in New Issue
Block a user