aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/lua.txt6
-rw-r--r--runtime/doc/news.txt2
-rw-r--r--runtime/lua/vim/hl.lua57
-rw-r--r--test/functional/lua/hl_spec.lua74
4 files changed, 113 insertions, 26 deletions
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index be118cf790..c580c55a5e 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -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*
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 72ab3373f7..72d394dd68 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -104,7 +104,7 @@ The following new features were added.
API
-• todo
+• |vim.hl.range()| now allows multiple timed highlights
DEFAULTS
diff --git a/runtime/lua/vim/hl.lua b/runtime/lua/vim/hl.lua
index a2b06c9727..d15ee1fd10 100644
--- a/runtime/lua/vim/hl.lua
+++ b/runtime/lua/vim/hl.lua
@@ -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,
diff --git a/test/functional/lua/hl_spec.lua b/test/functional/lua/hl_spec.lua
index 12be01e0a5..e3c6c706c7 100644
--- a/test/functional/lua/hl_spec.lua
+++ b/test/functional/lua/hl_spec.lua
@@ -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()