Neovim plugin improving access to clipboard history (mirror)
1# YankBank
2
3A Neovim plugin for keeping track of more recent yanks and deletions and exposing them in a quick access menu.
4
5## What it Does
6
7YankBank stores the N recent yanks into the unnamed register ("), then populates a popup window with these recent yanks, allowing for quick access to recent yank history.
8Upon opening the popup menu, the current contents of the unnamedplus (+) register are also added to the menu (if they are different from the current contents of the unnamed register).
9
10Choosing an entry from the menu (by hitting enter) will paste it into the currently open buffer at the cursor position.
11
12YankBank also offers persistence between sessions, meaning that you won't lose your yanks after closing and reopening a session (see [persistence](#Persistence)).
13
14### Screenshots
15
16
17
18The menu is specific to the current session, and will only contain the contents of the current unnamedplus register upon opening in a completely new session.
19It will be populated further for each yank or deletion in that session.
20
21## Installation and Setup
22
23#### With Persistence (Recommended)
24
25Using lazy.nvim
26```lua
27{
28 "ptdewey/yankbank-nvim",
29 dependencies = "kkharji/sqlite.lua",
30 cmd = { "YankBank" },
31 config = function()
32 require('yankbank').setup({
33 persist_type = "sqlite",
34 })
35 end,
36}
37```
38
39#### Without persistence:
40
41Using lazy.nvim
42```lua
43{
44 "ptdewey/yankbank-nvim",
45 cmd = { "YankBank" },
46 config = function()
47 require('yankbank').setup()
48 end,
49}
50```
51
52#### Lazy loading
53
54Per [best practices](https://github.com/nvim-neorocks/nvim-best-practices?tab=readme-ov-file#sleeping_bed-lazy-loading), YankBank's initialization footprint is very minimal, and functionalities are only loaded when they are needed. As such, I set `lazy=false` in my config, and get a startup time of <1ms.
55
56```lua
57-- plugins/yankbank.lua
58return {
59 {
60 "ptdewey/yankbank-nvim",
61 lazy = false,
62 config = function()
63 -- ...
64 end,
65 },
66 {
67 "kkharji/sqlite.lua",
68 lazy = true,
69 },
70}
71```
72
73If you don't want to load YankBank on startup, I previously loaded it on keypresses that yank text (`y`, `Y`, `d`, `D`, `x`), the `FocusGained` event, and the `YankBank` command.
74```lua
75{
76 "ptdewey/yankbank-nvim",
77 dependencies = "kkharji/sqlite.lua",
78 keys = {
79 { "y" },
80 { "Y", "y$" }, -- redefine Y behavior to y$ to avoid breaking lazy
81 { "D" },
82 { "d" },
83 { "x" },
84 { "<leader>p", desc = "Open YankBank" },
85 },
86 cmd = { "YankBank" },
87 event = { "FocusGained" },
88 config = function()
89 require("yankbank").setup({
90 -- ...
91 })
92 end
93}
94```
95
96
97### Setup Options
98
99The setup function also supports taking in a table of options:
100| Option | Type | Default |
101|-------------|--------------------------------------------|----------------|
102| max_entries | integer number of entries to show in popup | `10` |
103| sep | string separator to show between table entries | `"-----"` |
104| keymaps | table containing keymap overrides | `{}` |
105| keymaps.navigation_next | string | `"j"` |
106| keymaps.navigation_prev | string | `"k"` |
107| keymaps.paste | string | `"<CR>"` |
108| keymaps.paste_back | string | `"P"` |
109| keymaps.yank | string | `"yy"` |
110| keymaps.close | table of strings | `{ "<Esc>", "<C-c>", "q" }` |
111| num_behavior | string defining jump behavior "prefix" or "jump" | `"prefix"` |
112| focus_gain_poll | boolean | `false` |
113| registers | table container for register overrides | `{ }` |
114| registers.yank_register | default register to yank from popup to | `"+"` |
115| persist_type | string defining persistence type "sqlite" or nil | `nil` |
116| db_path | string defining database file path for use with sqlite persistence | plugin install directory |
117| bind_indices | optional string to be used for keybind prefix for pasting by index number (i.e. "<leader>p") | `nil` |
118| pickers | table containing all pickers. | `{}` |
119| pickers.snacks | boolean | `false` |
120
121
122#### Example Configuration
123
124```lua
125{
126 "ptdewey/yankbank-nvim",
127 dependencies = {
128 "folke/snacks.nvim", -- (optional) - snacks picker integration
129 },
130 config = function()
131 require('yankbank').setup({
132 max_entries = 9,
133 sep = "-----",
134 num_behavior = "jump",
135 focus_gain_poll = true,
136 persist_type = "sqlite",
137 keymaps = {
138 paste = "<CR>",
139 paste_back = "P",
140 },
141 registers = {
142 yank_register = "+",
143 },
144 bind_indices = "<leader>p"
145 pickers = {
146 snacks = true,
147 },
148 })
149 end,
150}
151```
152
153If no separator is desired, pass in an empty string for `sep`
154
155The 'num_behavior' option defines in-popup navigation behavior when hitting number keys.
156- `num_behavior = "prefix"` works similar to traditional vim navigation with '3j' moving down 3 entries in the bank.
157- `num_behavior = "jump"` jumps to entry matching the pressed number key (i.e. '3' jumps to entry 3)
158 - Note: If 'max_entries' is a two-digit number, there will be a delay upon pressing numbers that prefix a valid entry.
159
160The 'focus_gain_poll' option allows for enabling an additional autocommand that watches for focus gains (refocusing Neovim window), and checks for changes in the unnamedplus ('+') register, adding to yankbank when new contents are found. This allows for automatically adding text copied from other sources (like a browser) to the yankbank without the bank opening trigger. Off by default, but I highly recommend enabling it with `focus_gain_poll = true`.
161
162### Persistence
163For the best experience with YankBank, enabling persistence is highly recommended.
164If persistence is enabled, sqlite.lua will be used to create a persistent store for recent yanks in the plugin root directory.
165To utilize sqlite persistence, `"kkharji/sqlite.lua"` must be added as a dependency in your config, and `persist_type` must be set to `"sqlite"`:
166
167```lua
168-- lazy
169return {
170 "ptdewey/yankbank-nvim",
171 dependencies = "kkharji/sqlite.lua",
172 config = function()
173 require('yankbank').setup({
174 -- other options...
175 persist_type = "sqlite"
176 })
177 end,
178}
179```
180
181Note: The database can be cleared with the `:YankBankClearDB` command or by deleting the db file (found in the plugin install directory by default).
182
183If you run into any SQL related issues, please file an issue on GitHub. (As a temporary fix, you can also try clearing the database)
184
185
186If you run into permissions issues when creating the db file (i.e. when installing using Nix), use the `db_path` option to change the default file path. (`vim.fn.stdpath("data")` should work)
187
188## Usage
189
190The popup menu can be opened with the command:`:YankBank`, an entry is pasted at the current cursor position by hitting enter, and the menu can be closed by hitting escape, ctrl-c, or q.
191An entry from the menu can also be yanked into the unnamedplus register by hitting yy.
192
193I would personally also recommend setting a keybind to open the menu.
194```lua
195-- map to '<leader>y'
196vim.keymap.set("n", "<leader>y", "<cmd>YankBank<CR>", { noremap = true })
197```
198
199### Snacks Picker
200If `pickers.snacks` is set to true in the setup options, the snacks picker can be used to open the yankbank menu.
201This can be triggered by running the command `:YankBankSnacks` or using `Snacks.picker.yankbank()` as you would any other snacks picker.
202
203---
204
205## API (WIP)
206
207Some plugin internals are also accessible via the YankBank api.
208
209Examples:
210```lua
211-- get the ith entry in the bank
212---@param i integer index to get
213-- output format: { yank_text = "entry", reg_type = "v" }
214local e = require("yankbank.api").get_entry(i)
215
216-- add an entry to the bank
217---@param yank_text string yank text to add to YANKS table
218---@param reg_type string register type "v", "V", or "^V" (visual, v-line, v-block respectively)
219require("yankbank.api").add_entry("yank_text", "reg_type")
220
221-- remove an entry from the bank by index
222---@param i integer index to remove
223require("yankbank.api").remove_entry(i)
224
225--- pin entry to yankbank so that it won't be removed when its position exceeds the max number of entries
226---@param i integer index to pin
227require("yankbank.api").pin_entry(i)
228
229
230--- unpin bank entry
231---@param i integer index to unpin
232require("yankbank.api").unpin_entry(i)
233```
234
235For more details about the API see [lua/yankbank/api.lua](lua/yankbank/api.lua)
236
237---
238
239## Potential Improvements
240- nvim-cmp integration
241- fzf integration
242- telescope integration
243
244## Alternatives
245
246- [nvim-neoclip](https://github.com/AckslD/nvim-neoclip.lua)
247- [yanky.nvim](https://github.com/gbprod/yanky.nvim)