1local api = vim.api
2
3local highlight = {}
4
5---@private
6function highlight.create(higroup, hi_info, default)
7  local options = {}
8  -- TODO: Add validation
9  for k, v in pairs(hi_info) do
10    table.insert(options, string.format("%s=%s", k, v))
11  end
12  vim.cmd(string.format([[highlight %s %s %s]], default and "default" or "", higroup, table.concat(options, " ")))
13end
14
15---@private
16function highlight.link(higroup, link_to, force)
17  vim.cmd(string.format([[highlight%s link %s %s]], force and "!" or " default", higroup, link_to))
18end
19
20
21--- Highlight range between two positions
22---
23---@param bufnr number of buffer to apply highlighting to
24---@param ns namespace to add highlight to
25---@param higroup highlight group to use for highlighting
26---@param rtype type of range (:help setreg, default charwise)
27---@param inclusive boolean indicating whether the range is end-inclusive (default false)
28function highlight.range(bufnr, ns, higroup, start, finish, rtype, inclusive)
29  rtype = rtype or 'v'
30  inclusive = inclusive or false
31
32  -- sanity check
33  if start[2] < 0 or finish[1] < start[1] then return end
34
35  local region = vim.region(bufnr, start, finish, rtype, inclusive)
36  for linenr, cols in pairs(region) do
37    api.nvim_buf_add_highlight(bufnr, ns, higroup, linenr, cols[1], cols[2])
38  end
39
40end
41
42local yank_ns = api.nvim_create_namespace('hlyank')
43--- Highlight the yanked region
44---
45--- use from init.vim via
46---   au TextYankPost * lua vim.highlight.on_yank()
47--- customize highlight group and timeout via
48---   au TextYankPost * lua vim.highlight.on_yank {higroup="IncSearch", timeout=150}
49--- customize conditions (here: do not highlight a visual selection) via
50---   au TextYankPost * lua vim.highlight.on_yank {on_visual=false}
51---
52-- @param opts dictionary with options controlling the highlight:
53--              - higroup   highlight group for yanked region (default "IncSearch")
54--              - timeout   time in ms before highlight is cleared (default 150)
55--              - on_macro  highlight when executing macro (default false)
56--              - on_visual highlight when yanking visual selection (default true)
57--              - event     event structure (default vim.v.event)
58function highlight.on_yank(opts)
59  vim.validate {
60    opts = { opts,
61    function(t) if t == nil then return true else return type(t) == 'table' end end,
62    'a table or nil to configure options (see `:h highlight.on_yank`)',
63  }}
64  opts = opts or {}
65  local event = opts.event or vim.v.event
66  local on_macro = opts.on_macro or false
67  local on_visual = (opts.on_visual ~= false)
68
69  if (not on_macro) and vim.fn.reg_executing() ~= '' then return end
70  if event.operator ~= 'y' or event.regtype == '' then return end
71  if (not on_visual) and event.visual then return end
72
73  local higroup = opts.higroup or "IncSearch"
74  local timeout = opts.timeout or 150
75
76  local bufnr = api.nvim_get_current_buf()
77  api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1)
78
79  local pos1 = vim.fn.getpos("'[")
80  local pos2 = vim.fn.getpos("']")
81
82  pos1 = {pos1[2] - 1, pos1[3] - 1 + pos1[4]}
83  pos2 = {pos2[2] - 1, pos2[3] - 1 + pos2[4]}
84
85  highlight.range(bufnr, yank_ns, higroup, pos1, pos2, event.regtype, event.inclusive)
86
87  vim.defer_fn(
88    function()
89      if api.nvim_buf_is_valid(bufnr) then
90        api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1)
91      end
92    end,
93    timeout
94  )
95end
96
97return highlight
98