Neovim plugin improving access to clipboard history (mirror)
at main 247 lines 8.8 kB view raw view rendered
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![YankBank popup window zoomed](assets/screenshot-2.png) 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)