Neovim plugin improving access to clipboard history (mirror)
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor/feat: switched to global opts table and added new api functionality

ptdewey 154ad4e3 eca193db

+127 -107
+22 -6
lua/yankbank/api.lua
··· 1 1 local M = {} 2 2 3 - -- TODO: figure out how api should get 'yanks' and 'reg_types' 4 - -- - possibly rewrite these to be global variables 5 - 6 - --- DOC: 3 + --- get a table containg a single yankbank entry by index 7 4 ---@param i integer 8 5 ---@return table 9 6 function M.get_entry(i) 10 - -- TODO: figure out how tables are being populated for the api 11 7 return { 12 8 yank_text = YANKS[i], 13 9 reg_type = REG_TYPES[i], 14 10 } 15 11 end 16 12 17 - --- DOC: 13 + --- get a table containing all yankbank entries 18 14 ---@return table 19 15 function M.get_all() 20 16 local out = {} ··· 26 22 end 27 23 return out 28 24 end 25 + 26 + --- add an entry to yankbank 27 + ---@param yank_text string yank text to add to YANKS table 28 + ---@param reg_type string register type "v", "V", or "^V" for visual, visual-linewise, and visual-block modes respectively. 29 + function M.add_entry(yank_text, reg_type) 30 + require("yankbank.clipboard").add_yank(yank_text, reg_type) 31 + end 32 + 33 + --- remove entry from yankbank by index 34 + ---@param i integer index to remove 35 + function M.remove_entry(i) 36 + local yank_text = table.remove(YANKS, i) 37 + table.remove(REG_TYPES, i) 38 + if OPTS.persist_type == "sqlite" then 39 + require("yankbank.persistence.sql").data():remove_match(yank_text) 40 + end 41 + end 42 + 43 + -- TODO: individual popup keymap setting functions 44 + -- - could just update opts table that is passed into set_keymaps 29 45 30 46 return M
+7 -9
lua/yankbank/clipboard.lua
··· 6 6 --- Function to add yanked text to table 7 7 ---@param text string 8 8 ---@param reg_type string 9 - ---@param opts table 10 - function M.add_yank(text, reg_type, opts) 9 + function M.add_yank(text, reg_type) 11 10 -- avoid adding empty strings 12 11 if text == "" and text == " " and text == "\n" then 13 12 return ··· 28 27 table.insert(REG_TYPES, 1, reg_type) 29 28 30 29 -- trim table size if necessary 31 - if #YANKS > opts.max_entries then 30 + if #YANKS > OPTS.max_entries then 32 31 table.remove(YANKS) 33 32 table.remove(REG_TYPES) 34 33 end 35 34 36 35 -- add entry to persistent store 37 - persistence.add_entry(text, reg_type, opts) 36 + persistence.add_entry(text, reg_type) 38 37 end 39 38 40 39 --- autocommand to listen for yank events 41 - ---@param opts table 42 - function M.setup_yank_autocmd(opts) 40 + function M.setup_yank_autocmd() 43 41 vim.api.nvim_create_autocmd("TextYankPost", { 44 42 callback = function() 45 43 -- get register information ··· 58 56 return 59 57 end 60 58 61 - M.add_yank(yank_text, reg_type, opts) 59 + M.add_yank(yank_text, reg_type) 62 60 end 63 61 end, 64 62 }) 65 63 66 64 -- poll registers when vim is focused (check for new clipboard activity) 67 - if opts.focus_gain_poll == true then 65 + if OPTS.focus_gain_poll == true then 68 66 vim.api.nvim_create_autocmd("FocusGained", { 69 67 callback = function() 70 68 -- get register information ··· 79 77 return 80 78 end 81 79 82 - M.add_yank(yank_text, reg_type, opts) 80 + M.add_yank(yank_text, reg_type) 83 81 end, 84 82 }) 85 83 end
+3 -4
lua/yankbank/data.lua
··· 1 1 local M = {} 2 2 3 3 --- reformat yanks table for popup 4 - ---@param opts table 5 4 ---@return table, table 6 - function M.get_display_lines(opts) 5 + function M.get_display_lines() 7 6 local display_lines = {} 8 7 local line_yank_map = {} 9 8 local yank_num = 0 ··· 51 50 52 51 if i < #YANKS then 53 52 -- Add a visual separator between yanks, aligned with the yank content 54 - if opts.sep ~= "" then 53 + if OPTS.sep ~= "" then 55 54 table.insert( 56 55 display_lines, 57 - string.rep(" ", max_digits + 2) .. opts.sep 56 + string.rep(" ", max_digits + 2) .. OPTS.sep 58 57 ) 59 58 end 60 59 table.insert(line_yank_map, false)
+14 -22
lua/yankbank/init.lua
··· 5 5 local clipboard = require("yankbank.clipboard") 6 6 local persistence = require("yankbank.persistence") 7 7 8 - -- initialize tables 9 8 YANKS = {} 10 9 REG_TYPES = {} 10 + OPTS = {} 11 11 12 12 -- default plugin options 13 13 local default_opts = { ··· 23 23 } 24 24 25 25 --- wrapper function for main plugin functionality 26 - ---@param opts table 27 - local function show_yank_bank(opts) 28 - YANKS = persistence.get_yanks(opts) or YANKS 26 + local function show_yank_bank() 27 + YANKS = persistence.get_yanks() or YANKS 29 28 30 29 -- initialize buffer and populate bank 31 - local bufnr, display_lines, line_yank_map = 32 - menu.create_and_fill_buffer(opts) 33 - 34 - -- handle empty bank case 35 - if not bufnr or not display_lines or not line_yank_map then 30 + local buf_data = menu.create_and_fill_buffer() 31 + if not buf_data then 36 32 return 37 33 end 38 34 39 - -- open window and set keybinds 40 - local win_id = menu.open_window(bufnr, display_lines) 41 - menu.set_keymaps(win_id, bufnr, line_yank_map, opts) 35 + -- open popup window 36 + buf_data.win_id = menu.open_window(buf_data) 37 + 38 + -- set popup keybinds 39 + menu.set_keymaps(buf_data) 42 40 end 43 41 44 42 -- plugin setup 45 43 ---@param opts? table 46 44 function M.setup(opts) 47 45 -- merge opts with default options table 48 - opts = vim.tbl_deep_extend("keep", opts or {}, default_opts) 46 + OPTS = vim.tbl_deep_extend("keep", opts or {}, default_opts) 49 47 50 48 -- enable persistence based on opts (needs to be called before autocmd setup) 51 - YANKS, REG_TYPES = persistence.setup(opts) 49 + YANKS, REG_TYPES = persistence.setup() 52 50 53 51 -- create clipboard autocmds 54 - clipboard.setup_yank_autocmd(opts) 52 + clipboard.setup_yank_autocmd() 55 53 56 54 -- create user command 57 55 vim.api.nvim_create_user_command("YankBank", function() 58 - show_yank_bank(opts) 56 + show_yank_bank() 59 57 end, { desc = "Show Recent Yanks" }) 60 - 61 - -- TODO: remove 62 - local api = require("yankbank.api") 63 - vim.api.nvim_create_user_command("YankBankApi", function() 64 - print(vim.inspect(api.get_all())) 65 - end, {}) 66 58 end 67 59 68 60 return M
+52 -50
lua/yankbank/menu.lua
··· 3 3 local data = require("yankbank.data") 4 4 local helpers = require("yankbank.helpers") 5 5 6 + --- Container class for YankBank buffer related variables 7 + ---@class YankBankBufData 8 + ---@field bufnr integer 9 + ---@field display_lines table 10 + ---@field line_yank_map table 11 + ---@field win_id integer 12 + 6 13 ---create new buffer and reformat yank table for ui 7 - ---@param opts table 8 - ---@return integer? 9 - ---@return table? 10 - ---@return table? 11 - function M.create_and_fill_buffer(opts) 14 + ---@return YankBankBufData? 15 + function M.create_and_fill_buffer() 12 16 -- stop if yanks or register types table is empty 13 17 if #YANKS == 0 or #REG_TYPES == 0 then 14 18 print("No yanks to show.") 15 - return nil, nil, nil 19 + return nil 16 20 end 17 21 18 22 -- create new buffer ··· 22 26 local current_filetype = vim.bo.filetype 23 27 vim.api.nvim_set_option_value("filetype", current_filetype, { buf = bufnr }) 24 28 25 - local display_lines, line_yank_map = data.get_display_lines(opts) 29 + local display_lines, line_yank_map = data.get_display_lines() 26 30 27 31 -- replace current buffer contents with updated table 28 32 vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, display_lines) 29 33 30 - return bufnr, display_lines, line_yank_map 34 + ---@type YankBankBufData 35 + return { 36 + bufnr = bufnr, 37 + display_lines = display_lines, 38 + line_yank_map = line_yank_map, 39 + win_id = -1, 40 + } 31 41 end 32 42 33 43 ---Calculate size and create popup window from bufnr 34 - ---@param bufnr integer 35 - ---@param display_lines table 44 + ---@param b YankBankBufData 36 45 ---@return integer 37 - function M.open_window(bufnr, display_lines) 46 + function M.open_window(b) 38 47 -- set maximum window width based on number of lines 39 48 local max_width = 0 40 - if display_lines and #display_lines > 0 then 41 - for _, line in ipairs(display_lines) do 49 + if b.display_lines and #b.display_lines > 0 then 50 + for _, line in ipairs(b.display_lines) do 42 51 max_width = math.max(max_width, #line) 43 52 end 44 53 else ··· 51 60 local width = 52 61 math.min(max_width, vim.api.nvim_get_option_value("columns", {}) - 4) 53 62 local height = math.min( 54 - display_lines and #display_lines or 1, 63 + b.display_lines and #b.display_lines or 1, 55 64 vim.api.nvim_get_option_value("lines", {}) - 10 56 65 ) 57 66 58 67 -- open window 59 - local win_id = vim.api.nvim_open_win(bufnr, true, { 68 + local win_id = vim.api.nvim_open_win(b.bufnr, true, { 60 69 relative = "editor", 61 70 width = width, 62 71 height = height, ··· 77 86 end 78 87 79 88 --- Set key mappings for the popup window 80 - ---@param win_id integer 81 - ---@param bufnr integer 82 - ---@param line_yank_map table 83 - ---@param opts table 84 - function M.set_keymaps(win_id, bufnr, line_yank_map, opts) 85 - -- Key mappings for selection and closing the popup 86 - local map_opts = { noremap = true, silent = true, buffer = bufnr } 87 - 89 + ---@param b YankBankBufData 90 + function M.set_keymaps(b) 88 91 -- default plugin keymaps 89 92 local default_keymaps = { 90 93 navigation_next = "j", ··· 99 102 yank_register = "+", 100 103 } 101 104 105 + -- key mappings for selection and closing the popup 106 + local map_opts = { noremap = true, silent = true, buffer = b.bufnr } 107 + 102 108 -- merge default and options keymap tables 103 - local k = vim.tbl_deep_extend("force", default_keymaps, opts.keymaps or {}) 109 + local k = vim.tbl_deep_extend("force", default_keymaps, OPTS.keymaps or {}) 104 110 105 111 -- merge default and options keymap tables 106 - opts.registers = 107 - vim.tbl_deep_extend("force", default_registers, opts.registers or {}) 112 + OPTS.registers = 113 + vim.tbl_deep_extend("force", default_registers, OPTS.registers or {}) 108 114 109 115 -- check table for number behavior option (prefix or jump, default to prefix) 110 - opts.num_behavior = opts.num_behavior or "prefix" 116 + OPTS.num_behavior = OPTS.num_behavior or "prefix" 111 117 112 118 -- popup buffer navigation binds 113 - if opts.num_behavior == "prefix" then 119 + if OPTS.num_behavior == "prefix" then 114 120 vim.keymap.set("n", k.navigation_next, function() 115 121 local count = vim.v.count1 > 0 and vim.v.count1 or 1 116 122 helpers.next_numbered_item(count) 117 123 return "" 118 - end, { noremap = true, silent = true, buffer = bufnr }) 124 + end, { noremap = true, silent = true, buffer = b.bufnr }) 119 125 vim.keymap.set("n", k.navigation_prev, function() 120 126 local count = vim.v.count1 > 0 and vim.v.count1 or 1 121 127 helpers.prev_numbered_item(count) 122 128 return "" 123 - end, { noremap = true, silent = true, buffer = bufnr }) 129 + end, { noremap = true, silent = true, buffer = b.bufnr }) 124 130 else 125 131 vim.keymap.set( 126 132 "n", 127 133 k.navigation_next, 128 134 helpers.next_numbered_item, 129 - { noremap = true, silent = true, buffer = bufnr } 135 + { noremap = true, silent = true, buffer = b.bufnr } 130 136 ) 131 137 vim.keymap.set( 132 138 "n", 133 139 k.navigation_prev, 134 140 helpers.prev_numbered_item, 135 - { noremap = true, silent = true, buffer = bufnr } 141 + { noremap = true, silent = true, buffer = b.bufnr } 136 142 ) 137 143 end 138 144 139 145 -- Map number keys to jump to entry if num_behavior is 'jump' 140 - if opts.num_behavior == "jump" then 141 - -- TODO: deal with delayed trigger upon hitting number that is part of valid sequence 142 - -- i.e. '1' when '10' is a valid entry 143 - for i = 1, opts.max_entries do 146 + if OPTS.num_behavior == "jump" then 147 + for i = 1, OPTS.max_entries do 144 148 vim.keymap.set("n", tostring(i), function() 145 149 local target_line = nil 146 - for line_num, yank_num in pairs(line_yank_map) do 150 + for line_num, yank_num in pairs(b.line_yank_map) do 147 151 if yank_num == i then 148 152 target_line = line_num 149 153 break 150 154 end 151 155 end 152 156 if target_line then 153 - vim.api.nvim_win_set_cursor(win_id, { target_line, 0 }) 157 + vim.api.nvim_win_set_cursor(b.win_id, { target_line, 0 }) 154 158 end 155 159 end, map_opts) 156 160 end ··· 158 162 159 163 -- bind paste behavior 160 164 vim.keymap.set("n", k.paste, function() 161 - local cursor = vim.api.nvim_win_get_cursor(win_id)[1] 165 + local cursor = vim.api.nvim_win_get_cursor(b.win_id)[1] 162 166 -- use the mapping to find the original yank 163 - local yankIndex = line_yank_map[cursor] 167 + local yankIndex = b.line_yank_map[cursor] 164 168 if yankIndex then 165 169 -- retrieve the full yank, including all lines 166 170 local text = YANKS[yankIndex] 167 171 168 172 -- close window upon selection 169 - vim.api.nvim_win_close(win_id, true) 173 + vim.api.nvim_win_close(b.win_id, true) 170 174 helpers.smart_paste(text, REG_TYPES[yankIndex]) 171 175 else 172 176 print("Error: Invalid selection") 173 177 end 174 - end, { buffer = bufnr }) 178 + end, { buffer = b.bufnr }) 175 179 176 180 -- bind yank behavior 177 181 vim.keymap.set("n", k.yank, function() 178 - local cursor = vim.api.nvim_win_get_cursor(win_id)[1] 179 - local yankIndex = line_yank_map[cursor] 182 + local cursor = vim.api.nvim_win_get_cursor(b.win_id)[1] 183 + local yankIndex = b.line_yank_map[cursor] 180 184 if yankIndex then 181 185 local text = YANKS[yankIndex] 182 - -- NOTE: possibly change this to '"' if not using system clipboard 183 - -- - make this an option 184 - vim.fn.setreg(opts.registers.yank_register, text) 185 - vim.api.nvim_win_close(win_id, true) 186 + vim.fn.setreg(OPTS.registers.yank_register, text) 187 + vim.api.nvim_win_close(b.win_id, true) 186 188 end 187 - end, { buffer = bufnr }) 189 + end, { buffer = b.bufnr }) 188 190 189 191 -- close popup keybinds 190 192 for _, map in ipairs(k.close) do 191 193 vim.keymap.set("n", map, function() 192 - vim.api.nvim_win_close(win_id, true) 194 + vim.api.nvim_win_close(b.win_id, true) 193 195 end, map_opts) 194 196 end 195 197 end
+9 -12
lua/yankbank/persistence.lua
··· 3 3 local persistence = {} 4 4 5 5 ---add entry from bank to 6 - ---@param entry string|table 6 + ---@param entry string 7 7 ---@param reg_type string 8 - ---@param opts table 9 - function M.add_entry(entry, reg_type, opts) 10 - if opts.persist_type == "sqlite" then 8 + function M.add_entry(entry, reg_type) 9 + if OPTS.persist_type == "sqlite" then 11 10 persistence:insert_yank(entry, reg_type) 12 11 end 13 12 end 14 13 15 14 --- get current state of yanks in persistent storage 16 - ---@param opts table 17 - function M.get_yanks(opts) 18 - if opts.persist_type == "sqlite" then 15 + function M.get_yanks() 16 + if OPTS.persist_type == "sqlite" then 19 17 return persistence:get_bank() 20 18 end 21 19 end 22 20 23 21 ---initialize bank persistence 24 - ---@param opts table 25 22 ---@return table 26 23 ---@return table 27 - function M.setup(opts) 28 - if not opts.persist_type then 24 + function M.setup() 25 + if not OPTS.persist_type then 29 26 return {}, {} 30 - elseif opts.persist_type == "sqlite" then 31 - persistence = require("yankbank.persistence.sql").setup(opts) 27 + elseif OPTS.persist_type == "sqlite" then 28 + persistence = require("yankbank.persistence.sql").setup() 32 29 return persistence:get_bank() 33 30 else 34 31 return {}, {}
+20 -4
lua/yankbank/persistence/sql.lua
··· 82 82 return yanks, reg_types 83 83 end 84 84 85 + --- remove an entry from the banks table matching input text 86 + ---@param text string 87 + function data:remove_match(text) 88 + db:with_open(function() 89 + return db:eval( 90 + "DELETE FROM bank WHERE yank_text = :yank_text", 91 + { yank_text = text } 92 + ) 93 + end) 94 + end 95 + 96 + --- get data in sqlite_tbl form (for api use only) 97 + ---@return sqlite_tbl 98 + function M.data() 99 + return data 100 + end 101 + 85 102 --- set up database persistence 86 - ---@param opts table 87 103 ---@return sqlite_tbl data 88 - function M.setup(opts) 89 - max_entries = opts.max_entries 104 + function M.setup() 105 + max_entries = OPTS.max_entries 90 106 91 107 vim.api.nvim_create_user_command("YankBankClearDB", function() 92 108 data:remove() ··· 94 110 REG_TYPES = {} 95 111 end, {}) 96 112 97 - if opts.debug == true then 113 + if OPTS.debug == true then 98 114 vim.api.nvim_create_user_command("YankBankViewDB", function() 99 115 print(vim.inspect(data:get())) 100 116 end, {})