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(changed clipboard function params to accomodate persistence)

+91 -78
+11 -13
lua/yankbank/clipboard.lua
··· 1 1 -- clipboard.lua 2 2 local M = {} 3 3 4 - -- TODO: convert string to table yanks to work better with files 4 + -- import persistence module 5 + local persistence = require("yankbank.persistence") 5 6 6 7 -- Function to add yanked text to table 7 8 ---@param yanks table 8 9 ---@param reg_types table 9 10 ---@param text string 10 11 ---@param reg_type string 11 - ---@param max_entries integer 12 - function M.add_yank(yanks, reg_types, text, reg_type, max_entries) 12 + ---@param opts table 13 + function M.add_yank(yanks, reg_types, text, reg_type, opts) 13 14 -- avoid adding empty strings 14 15 -- TODO: could block adding single characters here 15 16 if text == "" or text == " " or text == "\n" then 16 17 return 17 18 end 18 - print( 19 - "yanks: ", 20 - vim.inspect(yanks), 21 - "| reg_types: ", 22 - vim.inspect(reg_types) 23 - ) 24 19 25 20 -- do not update with duplicate values 26 21 for _, entry in ipairs(yanks) do ··· 32 27 -- add entry to bank 33 28 table.insert(yanks, 1, text) 34 29 table.insert(reg_types, 1, reg_type) 35 - if #yanks > max_entries then 30 + if #yanks > opts.max_entries then 36 31 table.remove(yanks) 37 32 table.remove(reg_types) 38 33 end 34 + 35 + -- add entry to persistent store 36 + persistence.add_entry(yanks, reg_types, opts) 39 37 end 40 38 41 39 -- autocommand to listen for yank events 42 40 ---@param yanks table 43 41 ---@param reg_types table 44 - ---@param max_entries integer 45 - function M.setup_yank_autocmd(yanks, reg_types, max_entries) 42 + ---@param opts table 43 + function M.setup_yank_autocmd(yanks, reg_types, opts) 46 44 vim.api.nvim_create_autocmd("TextYankPost", { 47 45 callback = function() 48 46 -- get register information ··· 55 53 if #yanked_text <= 1 then 56 54 return 57 55 end 58 - M.add_yank(yanks, reg_types, yanked_text, reg_type, max_entries) 56 + M.add_yank(yanks, reg_types, yanked_text, reg_type, opts) 59 57 end 60 58 end, 61 59 })
+19 -23
lua/yankbank/init.lua
··· 8 8 -- initialize yanks tables 9 9 local yanks = {} 10 10 local reg_types = {} 11 - local max_entries = 10 12 - local sep = "-----" 13 - local persist_type = "file" 14 - local persist_path = "/tmp/yankbank.txt" 11 + 12 + local plugin_path = debug.getinfo(1).source:sub(2):match("(.*/).*/.*/") or "./" 13 + 14 + -- default plugin options 15 + local default_opts = { 16 + max_entries = 10, 17 + sep = "-----", 18 + persist_type = "memory", 19 + persist_path = plugin_path .. "bank.txt", 20 + } 15 21 16 22 -- wrapper function for main plugin functionality 17 - ---@param opts table|nil 23 + ---@param opts table 18 24 local function show_yank_bank(opts) 19 25 -- Parse command arguments directly if args are provided as a string 20 - opts = opts or {} 21 - 22 - -- Fallback to defaults if necessary 23 - local max_entries_opt = opts.max_entries or max_entries 24 - local sep_opt = opts.sep or sep 25 - 26 - opts.keymaps = opts.keymaps or {} 27 26 28 27 local bufnr, display_lines, line_yank_map = 29 - menu.create_and_fill_buffer(yanks, reg_types, max_entries_opt, sep_opt) 28 + menu.create_and_fill_buffer(yanks, reg_types, opts) 29 + 30 30 -- handle empty bank case 31 - if not bufnr then 31 + if not bufnr or not display_lines or not line_yank_map then 32 32 return 33 33 end 34 + 34 35 local win_id = menu.open_window(bufnr, display_lines) 35 36 menu.set_keymaps(win_id, bufnr, yanks, reg_types, line_yank_map, opts) 36 37 end 37 38 38 39 -- plugin setup 39 - ---@param opts table|nil 40 + ---@param opts table? 40 41 function M.setup(opts) 41 - opts = opts or {} 42 - 43 - -- parse opts 44 - max_entries = opts.max_entries or max_entries 45 - 46 - persist_type = opts.persist_type or persist_type 47 - persist_path = opts.persist_path or persist_path 42 + -- merge opts with default options table 43 + opts = vim.tbl_deep_extend("force", default_opts, opts or {}) 48 44 49 45 -- create clipboard autocmds 50 - clipboard.setup_yank_autocmd(yanks, reg_types, max_entries) 46 + clipboard.setup_yank_autocmd(yanks, reg_types, opts) 51 47 52 48 -- Create user command 53 49 vim.api.nvim_create_user_command("YankBank", function()
+22 -7
lua/yankbank/menu.lua
··· 6 6 local data = require("yankbank.data") 7 7 local helpers = require("yankbank.helpers") 8 8 9 - -- create new buffer and reformat yank table for ui 10 - function M.create_and_fill_buffer(yanks, reg_types, max_entries, sep) 9 + ---create new buffer and reformat yank table for ui 10 + ---@param yanks table 11 + ---@param reg_types table 12 + ---@param opts table 13 + ---@return integer? 14 + ---@return table? 15 + ---@return table? 16 + function M.create_and_fill_buffer(yanks, reg_types, opts) 11 17 -- check the content of the system clipboard register 12 18 -- TODO: this could be replaced with some sort of polling of the + register 13 19 local text = vim.fn.getreg("+") 14 20 local most_recent_yank = yanks[1] or "" 15 21 if text ~= most_recent_yank then 16 22 local reg_type = vim.fn.getregtype("+") 17 - clipboard.add_yank(yanks, reg_types, text, reg_type, max_entries) 23 + clipboard.add_yank(yanks, reg_types, text, reg_type, opts) 18 24 end 19 25 20 26 -- stop if yank table is empty 21 27 if #yanks == 0 then 22 28 print("No yanks to show.") 23 - return 29 + return nil, nil, nil 24 30 end 25 31 26 32 -- create new buffer ··· 30 36 local current_filetype = vim.bo.filetype 31 37 vim.api.nvim_set_option_value("filetype", current_filetype, { buf = bufnr }) 32 38 33 - local display_lines, line_yank_map = data.get_display_lines(yanks, sep) 39 + local display_lines, line_yank_map = data.get_display_lines(yanks, opts.sep) 34 40 35 41 -- replace current buffer contents with updated table 36 42 vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, display_lines) ··· 38 44 return bufnr, display_lines, line_yank_map 39 45 end 40 46 41 - -- Calculate size and create popup window from bufnr 47 + ---Calculate size and create popup window from bufnr 48 + ---@param bufnr integer 49 + ---@param display_lines table 50 + ---@return integer 42 51 function M.open_window(bufnr, display_lines) 43 52 -- set maximum window width based on number of lines 44 53 local max_width = 0 ··· 81 90 return win_id 82 91 end 83 92 84 - -- Set key mappings for the popup window 93 + ---Set key mappings for the popup window 94 + ---@param win_id integer 95 + ---@param bufnr integer 96 + ---@param yanks table 97 + ---@param reg_types table 98 + ---@param line_yank_map table 99 + ---@param opts table 85 100 function M.set_keymaps(win_id, bufnr, yanks, reg_types, line_yank_map, opts) 86 101 -- Key mappings for selection and closing the popup 87 102 local map_opts = { noremap = true, silent = true, buffer = bufnr }
+28 -12
lua/yankbank/persistence.lua
··· 1 1 -- persistence.lua 2 - 3 2 local M = {} 4 3 5 - -- TODO: for file-based persistence: 6 - -- - need system for moving entries around in list 7 - -- - either use tags and search by tag (could be out of order) 8 - -- - or keep list in sorted order (likely more i/o heavy) 9 - -- - store local copy of list in memory, to make accesses to popup quick 10 - -- - might need plenary for the asynchronous r/w accesses 4 + ---comment 5 + ---@param yanks table 6 + ---@param reg_types table 7 + ---@param opts table 8 + function M.add_entry(yanks, reg_types, opts) 9 + if not opts.persist_type then 10 + return 11 + elseif opts.persist_type == "memory" then 12 + return 13 + elseif opts.persist_type == "file" then 14 + -- TODO: 15 + elseif opts.persist_type == "sqlite" then 16 + end 17 + end 11 18 12 - function M.enable_persistence(yanks, opts) 19 + ---initialize bank persistence 20 + ---@param yanks table 21 + ---@param reg_types table 22 + ---@param opts table 23 + function M.setup(yanks, reg_types, opts) 13 24 if not opts.persist_type then 14 25 return 15 26 elseif opts.persist_type == "file" then 16 27 -- TODO: 17 - require("persistence.file").setup_persistence( 18 - yanks, 28 + require("yankbank.persistence.file").setup_persistence( 19 29 opts.persist_path, 20 - opts.max_entries 30 + opts.max_entries, 31 + yanks, 32 + reg_types 21 33 ) 22 34 elseif opts.persist_type == "sqlite" then 23 35 -- TODO: 24 - require("persistence.sql").init_db(yanks, opts.persist_path) 36 + require("yankbank.persistence.sql").init_db( 37 + yanks, 38 + reg_types, 39 + opts.persist_path 40 + ) 25 41 end 26 42 end 27 43
+11 -23
lua/yankbank/persistence/file.lua
··· 50 50 ---@param line string: line from file being checked 51 51 ---@return table|nil 52 52 local function check_for_entry(line) 53 - local i, l, rt = 54 - string.match(line, "<YANKBANK_ENTRY:(%d+),(%d+),(%a+)>") 53 + local i, l, rt = string.match(line, "<YANKBANK_ENTRY:(%d+),(%d+),(%a+)>") 55 54 if i then 56 55 return { 57 56 index = tonumber(i), ··· 87 86 local lines = {} 88 87 for line in f:lines() do 89 88 if 90 - string.match( 91 - line, 92 - "<YANKBANK_ENTRY:" .. n_entries .. ",%d+,%a+>" 93 - ) 89 + string.match(line, "<YANKBANK_ENTRY:" .. n_entries .. ",%d+,%a+>") 94 90 then 95 91 n_entries = n_entries - 1 96 92 lines[1] = "<YANKBANK_LIST:" .. n_entries .. ">\n" ··· 142 138 143 139 -- write entry header 144 140 -- FIX: #entry doesn't match number of lines when it is string (number of chars instead of lines) 145 - f:write( 146 - "<YANKBANK_ENTRY:1," 147 - .. #entry 148 - .. "," 149 - .. reg_type 150 - .. ">\n" 151 - ) 141 + f:write("<YANKBANK_ENTRY:1," .. #entry .. "," .. reg_type .. ">\n") 152 142 -- write entry 153 143 if type(entry) == "string" then 154 144 f:write(entry) ··· 161 151 162 152 -- write remaining lines 163 153 for i = 2, #lines do 164 - local n, l, rt = string.match( 165 - lines[i], 166 - "<YANKBANK_ENTRY:(%d+),(%d+),(%a+)>" 167 - ) 154 + local n, l, rt = 155 + string.match(lines[i], "<YANKBANK_ENTRY:(%d+),(%d+),(%a+)>") 168 156 if n then 169 157 lines[i] = "<YANKBANK_ENTRY:" 170 - .. n + 1 171 - .. "," 172 - .. l 173 - .. "," 174 - .. rt 175 - .. ">" 158 + .. n + 1 159 + .. "," 160 + .. l 161 + .. "," 162 + .. rt 163 + .. ">" 176 164 end 177 165 -- TODO: check headers 178 166 f:write(lines[i] .. "\n")