Neovim plugin improving access to clipboard history (mirror)
1local M = {}
2
3local state = require("yankbank.state")
4
5--- get the last zero entry in a table
6---
7---@param t table
8---@return integer?
9local function last_zero_entry(t)
10 for i = #t, 1, -1 do
11 if t[i] == 0 then
12 return i
13 end
14 end
15 return nil
16end
17
18--- Function to add yanked text to table
19---@param text string
20---@param reg_type string
21---@param pin integer|boolean?
22function M.add_yank(text, reg_type, pin)
23 -- avoid adding empty strings
24 if text == "" and text == " " and text == "\n" then
25 return
26 end
27
28 local is_pinned = 0
29 local yanks = state.get_yanks()
30 local reg_types = state.get_reg_types()
31 local pins = state.get_pins()
32
33 -- check for duplicate values already inserted
34 for i, entry in ipairs(yanks) do
35 if entry == text then
36 -- remove matched entry so it can be inserted at 1st position
37 table.remove(yanks, i)
38 table.remove(reg_types, i)
39 is_pinned = table.remove(pins, i)
40 break
41 end
42 end
43
44 -- override is_pinned if pin is set
45 is_pinned = (pin == 1 or pin == true) and 1
46 or (pin == 0 or pin == false) and 0
47 or is_pinned
48
49 -- add entry to bank
50 table.insert(yanks, 1, text)
51 table.insert(reg_types, 1, reg_type)
52 table.insert(pins, 1, is_pinned)
53
54 -- trim table size if necessary
55 local opts = state.get_opts()
56 if #yanks > opts.max_entries then
57 local i = last_zero_entry(pins)
58
59 if not i or i == 1 then
60 -- WARN: undefined behavior
61 print(
62 "Warning: all YankBank entries are pinned, insertion behavior is undefined when all entries are pinned."
63 )
64 else
65 -- remove last non-pinned entry
66 table.remove(yanks, i)
67 table.remove(reg_types, i)
68 table.remove(pins, i)
69 end
70 end
71
72 -- update state
73 state.set_yanks(yanks)
74 state.set_reg_types(reg_types)
75 state.set_pins(pins)
76
77 -- add entry to persistent store
78 require("yankbank.persistence").add_entry(text, reg_type, pin)
79end
80
81--- autocommand to listen for yank events
82function M.setup_yank_autocmd()
83 vim.api.nvim_create_autocmd("TextYankPost", {
84 callback = function()
85 -- get register information
86 local rn = vim.v.event.regname
87
88 -- check changes were made to default register
89 if rn == "" or rn == "+" then
90 local reg_type = vim.fn.getregtype(rn)
91 local yank_text = vim.fn.getreg(rn)
92
93 if not yank_text or type(yank_text) ~= "string" then
94 return
95 end
96
97 if #yank_text <= 1 then
98 return
99 end
100
101 -- lazy load initialization when first yank happens
102 require("yankbank").ensure_initialized()
103 M.add_yank(yank_text, reg_type)
104 end
105 end,
106 })
107
108 -- poll registers when vim is focused (check for new clipboard activity)
109 local opts = state.get_opts()
110 if opts.focus_gain_poll == true then
111 vim.api.nvim_create_autocmd("FocusGained", {
112 callback = function()
113 -- get register information
114 local reg_type = vim.fn.getregtype("+")
115 local yank_text = vim.fn.getreg("+")
116
117 if not yank_text or type(yank_text) ~= "string" then
118 return
119 end
120
121 if string.len(yank_text) <= 1 then
122 return
123 end
124
125 -- lazy load initialization when first focus gain happens
126 require("yankbank").ensure_initialized()
127 M.add_yank(yank_text, reg_type)
128 end,
129 })
130 end
131end
132
133return M