···2626--- add an entry to yankbank
2727---@param yank_text string yank text to add to YANKS table
2828---@param reg_type string register type "v", "V", or "^V" (visual, v-line, v-block respectively)
2929-function M.add_entry(yank_text, reg_type)
3030- require("yankbank.clipboard").add_yank(yank_text, reg_type)
2929+---@param pin integer|boolean?
3030+function M.add_entry(yank_text, reg_type, pin)
3131+ require("yankbank.clipboard").add_yank(yank_text, reg_type, pin)
3132end
32333334--- remove entry from yankbank by index
3435---@param i integer index to remove
3536function M.remove_entry(i)
3637 local yank_text = table.remove(YB_YANKS, i)
3737- table.remove(YB_REG_TYPES, i)
3838+ local reg_type = table.remove(YB_REG_TYPES, i)
3839 if YB_OPTS.persist_type == "sqlite" then
3939- require("yankbank.persistence.sql").data().remove_match(yank_text)
4040+ require("yankbank.persistence.sql")
4141+ .data()
4242+ .remove_match(yank_text, reg_type)
4343+ end
4444+end
4545+4646+--- pin entry to yankbank so that it won't be removed when its position exceeds the max number of entries
4747+---
4848+---@param i integer index to pin
4949+function M.pin_entry(i)
5050+ -- TODO: check persistence mode, max_entries
5151+ -- - add third table YB_PINS to keep track of pins
5252+ -- TODO: show pins differently in popup
5353+ if YB_OPTS.persist_type == "sqlite" then
5454+ return require("yankbank.persistence.sql")
5555+ .data()
5656+ .pin(YB_YANKS[i], YB_REG_TYPES[i])
5757+ end
5858+end
5959+6060+--- unpin bank entry
6161+---
6262+---@param i integer index to unpin
6363+function M.unpin_entry(i)
6464+ -- TODO: check persistence mode, max_entries
6565+ -- - in sql.lua, add 3rd field (bool) pinned
6666+ if YB_OPTS.persist_type == "sqlite" then
6767+ return require("yankbank.persistence.sql")
6868+ .data()
6969+ .unpin(YB_YANKS[i], YB_REG_TYPES[i])
4070 end
4171end
4272
+6-2
lua/yankbank/clipboard.lua
···66--- Function to add yanked text to table
77---@param text string
88---@param reg_type string
99-function M.add_yank(text, reg_type)
99+---@param pin integer|boolean?
1010+function M.add_yank(text, reg_type, pin)
1011 -- avoid adding empty strings
1112 if text == "" and text == " " and text == "\n" then
1213 return
···1617 for i, entry in ipairs(YB_YANKS) do
1718 if entry == text then
1819 -- remove matched entry so it can be inserted at 1st position
2020+ -- TODO: pin handling in global tables
1921 table.remove(YB_YANKS, i)
2022 table.remove(YB_REG_TYPES, i)
2123 break
···2325 end
24262527 -- add entry to bank
2828+ -- TODO: pin handling in global tables
2629 table.insert(YB_YANKS, 1, text)
2730 table.insert(YB_REG_TYPES, 1, reg_type)
28312932 -- trim table size if necessary
3033 if #YB_YANKS > YB_OPTS.max_entries then
3434+ -- TODO: pin handling in global tables
3135 table.remove(YB_YANKS)
3236 table.remove(YB_REG_TYPES)
3337 end
34383539 -- add entry to persistent store
3636- persistence.add_entry(text, reg_type)
4040+ persistence.add_entry(text, reg_type, pin)
3741end
38423943--- autocommand to listen for yank events
+3-2
lua/yankbank/persistence.lua
···55---add entry from bank to
66---@param entry string
77---@param reg_type string
88-function M.add_entry(entry, reg_type)
88+---@param pin integer|boolean?
99+function M.add_entry(entry, reg_type, pin)
910 if YB_OPTS.persist_type == "sqlite" then
1010- persistence:insert_yank(entry, reg_type)
1111+ persistence:insert_yank(entry, reg_type, pin)
1112 end
1213end
1314
+70-13
lua/yankbank/persistence/sql.lua
···1414 -- yanked text should be unique and be primary key
1515 yank_text = { "text", unique = true, primary = true, required = true },
1616 reg_type = { "text", required = true },
1717+ pinned = { "integer", required = true, default = 0 },
1718 },
1819})
1920···2324--- insert yank entry into database
2425---@param yank_text string yanked text
2526---@param reg_type string register type
2626-function data:insert_yank(yank_text, reg_type)
2727+---@param pin integer|boolean? pin status of inserted entry
2828+function data:insert_yank(yank_text, reg_type, pin)
2729 -- attempt to remove entry if count > 0 (to move potential duplicate)
3030+ local is_pinned = 0
2831 if self:count() > 0 then
2932 db:with_open(function()
3333+ -- check if entry exists in db
3434+ local res = db:select("bank", {
3535+ where = {
3636+ yank_text = yank_text,
3737+ reg_type = reg_type,
3838+ },
3939+ })
4040+4141+ -- if result is empty, proceed to insertion
4242+ if #res == 0 then
4343+ return
4444+ end
4545+4646+ -- entry found, get pin status
4747+ is_pinned = res[1].pinned
4848+4949+ -- remove entry from db so it can be moved to first position
3050 db:eval(
3131- "DELETE FROM bank WHERE yank_text = :yank_text",
3232- { yank_text = yank_text }
5151+ "DELETE FROM bank WHERE yank_text = :yank_text and reg_type = :reg_type",
5252+ { yank_text = yank_text, reg_type = reg_type }
3353 )
3454 end)
3555 end
36565757+ -- override is_pinned if pin param is set, default to is_pinned otherwise
5858+ is_pinned = (pin == 1 or pin == true) and 1
5959+ or (pin == 0 or pin == false) and 0
6060+ or is_pinned
6161+3762 -- insert entry using the eval method with parameterized query to avoid error on 'data:insert()'
3863 db:with_open(function()
3964 db:eval(
4040- "INSERT INTO bank (yank_text, reg_type) VALUES (:yank_text, :reg_type)",
4141- { yank_text = yank_text, reg_type = reg_type }
6565+ "INSERT INTO bank (yank_text, reg_type, pinned) VALUES (:yank_text, :reg_type, :pinned)",
6666+ { yank_text = yank_text, reg_type = reg_type, pinned = is_pinned }
4267 )
4368 end)
4469···5176 if self:count() > max_entries then
5277 -- remove the oldest entry
5378 local oldest_entry = db:with_open(function()
5454- return db:select(
5555- "bank",
5656- { order_by = { asc = "rowid" }, limit = { 1 } }
5757- )[1]
7979+ return db:select("bank", {
8080+ where = { pinned = 0 },
8181+ order_by = { asc = "rowid" },
8282+ limit = { 1 },
8383+ })[1]
5884 end)
59856086 if oldest_entry then
···8411085111--- remove an entry from the banks table matching input text
86112---@param text string
8787-function data.remove_match(text)
113113+---@param reg_type string
114114+function data.remove_match(text, reg_type)
88115 db:with_open(function()
89116 return db:eval(
9090- "DELETE FROM bank WHERE yank_text = :yank_text",
9191- { yank_text = text }
117117+ "DELETE FROM bank WHERE yank_text = :yank_text and reg_type = :reg_type",
118118+ { yank_text = text, reg_type = reg_type }
119119+ )
120120+ end)
121121+end
122122+123123+--- pin entry in yankbank to prevent removal
124124+---@param text string text to match and pin
125125+---@param reg_type string reg_type corresponding to text
126126+---@return boolean
127127+function data.pin(text, reg_type)
128128+ return db:with_open(function()
129129+ -- TODO: always returns true or nothing
130130+ return (
131131+ db:eval(
132132+ "UPDATE bank SET pinned = 1 WHERE yank_text = :yank_text and reg_type = :reg_type",
133133+ { yank_text = text, reg_type = reg_type }
134134+ )
135135+ )
136136+ end)
137137+end
138138+139139+--- unpin entry in yankbank to prevent removal
140140+---@param text string
141141+---@param reg_type string reg_type corresponding to text
142142+---@return boolean
143143+function data.unpin(text, reg_type)
144144+ return db:with_open(function()
145145+ -- TODO: always returns true or nothing
146146+ return db:eval(
147147+ "UPDATE bank SET pinned = 0 WHERE yank_text = :yank_text and reg_type = :reg_type",
148148+ { yank_text = text, reg_type = reg_type }
92149 )
93150 end)
94151end
···105162 max_entries = YB_OPTS.max_entries
106163107164 vim.api.nvim_create_user_command("YankBankClearDB", function()
108108- data:remove()
165165+ data:drop()
109166 YB_YANKS = {}
110167 YB_REG_TYPES = {}
111168 end, {})