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

Configure Feed

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

feat(integrated file persist with existing functionalities)

+107 -88
+7 -3
lua/yankbank/clipboard.lua
··· 18 18 end 19 19 20 20 -- do not update with duplicate values 21 + -- BUG: there seems to be some issues here when trying to add on pipup open 21 22 for _, entry in ipairs(yanks) do 22 23 if entry == text then 23 24 return ··· 33 34 end 34 35 35 36 -- add entry to persistent store 36 - persistence.add_entry(yanks, reg_types, opts) 37 + persistence.add_entry(text, reg_type, opts) 37 38 end 39 + 40 + -- TODO: autocmd for focus gained (check system clipboard?) 38 41 39 42 -- autocommand to listen for yank events 40 43 ---@param yanks table ··· 45 48 callback = function() 46 49 -- get register information 47 50 local rn = vim.v.event.regname 48 - local reg_type = vim.fn.getregtype("+") 51 + -- local reg_type = vim.fn.getregtype("+") 49 52 50 53 -- check changes wwere made to default register 51 - if vim.v.event.regname == "" then 54 + if vim.v.event.regname == "" or vim.v.event.regname == "+" then 55 + local reg_type = vim.fn.getregtype(rn) 52 56 local yanked_text = vim.fn.getreg(rn) 53 57 if #yanked_text <= 1 then 54 58 return
+10 -4
lua/yankbank/data.lua
··· 1 1 -- data.lua 2 2 local M = {} 3 3 4 - -- reformat yanks table for popup 4 + ---reformat yanks table for popup 5 5 ---@param yanks table 6 6 ---@param sep string 7 7 ---@return table, table ··· 13 13 -- calculate the maximum width needed for the yank numbers 14 14 local max_digits = #tostring(#yanks) 15 15 16 + -- assumes yanks is table of strings 16 17 for i, yank in ipairs(yanks) do 17 18 yank_num = yank_num + 1 18 19 19 - -- remove trailing newlines 20 - yank = yank:gsub("\n$", "") 21 - local yank_lines = vim.split(yank, "\n", { plain = true }) 20 + -- FIX: there were changes here, might need further changes 21 + local yank_lines = yank 22 + if type(yank) == "string" then 23 + -- remove trailing newlines 24 + yank = yank:gsub("\n$", "") 25 + yank_lines = vim.split(yank, "\n", { plain = true }) 26 + end 27 + 22 28 local leading_space, leading_space_length 23 29 24 30 -- determine the number of leading whitespaces on the first line
+15 -7
lua/yankbank/helpers.lua
··· 45 45 end 46 46 47 47 -- customized paste function that functions like 'p' 48 - ---@param text string 48 + ---@param text string|table 49 49 ---@param reg_type string 50 50 function M.smart_paste(text, reg_type) 51 - -- convert text string to string list 52 51 local lines = {} 53 - for line in text:gmatch("([^\n]*)\n?") do 54 - table.insert(lines, line) 52 + if type(text) == "string" then 53 + -- convert text string to string list 54 + for line in text:gmatch("([^\n]*)\n?") do 55 + table.insert(lines, line) 56 + end 57 + if #lines > 1 then 58 + table.remove(lines) 59 + end 60 + else 61 + -- text is already table 62 + lines = text 55 63 end 56 64 57 65 -- remove last newline character to replicate base put behavior 58 - if #lines > 1 then 59 - table.remove(lines) 60 - end 66 + -- if lines[#lines] == "" then 67 + -- table.remove(lines) 68 + -- end 61 69 vim.api.nvim_put(lines, reg_type, true, true) 62 70 end 63 71
+6
lua/yankbank/init.lua
··· 4 4 -- local imports 5 5 local menu = require("yankbank.menu") 6 6 local clipboard = require("yankbank.clipboard") 7 + local persistence = require("yankbank.persistence") 7 8 8 9 -- initialize yanks tables 9 10 local yanks = {} ··· 44 45 45 46 -- create clipboard autocmds 46 47 clipboard.setup_yank_autocmd(yanks, reg_types, opts) 48 + 49 + -- enable persistence based on opts 50 + yanks, reg_types = persistence.setup(yanks, reg_types, opts) 51 + -- print(vim.inspect(yanks)) 52 + -- print(vim.inspect(reg_types)) 47 53 48 54 -- Create user command 49 55 vim.api.nvim_create_user_command("YankBank", function()
+5 -6
lua/yankbank/menu.lua
··· 16 16 function M.create_and_fill_buffer(yanks, reg_types, opts) 17 17 -- check the content of the system clipboard register 18 18 -- TODO: this could be replaced with some sort of polling of the + register 19 - local text = vim.fn.getreg("+") 20 - local most_recent_yank = yanks[1] or "" 21 - if text ~= most_recent_yank then 22 - local reg_type = vim.fn.getregtype("+") 23 - clipboard.add_yank(yanks, reg_types, text, reg_type, opts) 24 - end 19 + -- local text = vim.fn.getreg("+") 20 + -- local most_recent_yank = yanks[1] or "" 21 + -- local reg_type = vim.fn.getregtype("+") 22 + -- clipboard.add_yank(yanks, reg_types, text, reg_type, opts) 25 23 26 24 -- stop if yank table is empty 27 25 if #yanks == 0 then ··· 36 34 local current_filetype = vim.bo.filetype 37 35 vim.api.nvim_set_option_value("filetype", current_filetype, { buf = bufnr }) 38 36 37 + -- TODO: need to update yanks from bank file before get_display_lines 39 38 local display_lines, line_yank_map = data.get_display_lines(yanks, opts.sep) 40 39 41 40 -- replace current buffer contents with updated table
+17 -15
lua/yankbank/persistence.lua
··· 1 1 -- persistence.lua 2 2 local M = {} 3 3 4 - ---comment 5 - ---@param yanks table 6 - ---@param reg_types table 4 + local persistence = {} 5 + 6 + ---add entry from bank to 7 + ---@param entry string|table 8 + ---@param reg_type string 7 9 ---@param opts table 8 - function M.add_entry(yanks, reg_types, opts) 10 + function M.add_entry(entry, reg_type, opts) 9 11 if not opts.persist_type then 10 - return 11 - elseif opts.persist_type == "memory" then 12 12 return 13 13 elseif opts.persist_type == "file" then 14 - -- TODO: 14 + persistence.add_to_bankfile(opts.persist_path, entry, reg_type) 15 15 elseif opts.persist_type == "sqlite" then 16 + -- TODO: implement sqlite persist 16 17 end 17 18 end 18 19 ··· 20 21 ---@param yanks table 21 22 ---@param reg_types table 22 23 ---@param opts table 24 + ---@return table 25 + ---@return table 23 26 function M.setup(yanks, reg_types, opts) 24 27 if not opts.persist_type then 25 - return 28 + return {}, {} 26 29 elseif opts.persist_type == "file" then 27 - -- TODO: 28 - require("yankbank.persistence.file").setup_persistence( 30 + persistence = require("yankbank.persistence.file") 31 + return persistence.setup_persistence( 29 32 opts.persist_path, 30 33 opts.max_entries, 31 34 yanks, ··· 33 36 ) 34 37 elseif opts.persist_type == "sqlite" then 35 38 -- TODO: 36 - require("yankbank.persistence.sql").init_db( 37 - yanks, 38 - reg_types, 39 - opts.persist_path 40 - ) 39 + persistence = require("yankbank.persistence.sql") 40 + persistence.init_db(yanks, reg_types, opts.persist_path) 41 41 end 42 + 43 + return {}, {} 42 44 end 43 45 44 46 return M
+47 -53
lua/yankbank/persistence/file.lua
··· 1 1 -- persistence/file.lua 2 - 3 2 local M = {} 4 3 5 4 local n_entries = 0 6 5 local m_entries = 10 7 6 8 - -- function that checks if a file exists 7 + ---function that checks if a file exists 9 8 ---@param file string: file path 10 9 ---@return boolean 11 10 local function file_exists(file) ··· 16 15 return f ~= nil 17 16 end 18 17 19 - -- function that reads all lines of file into a table 18 + ---function that reads all lines of file into a table 20 19 ---@param file string: file path 21 20 ---@return table 22 21 local function read_lines(file) ··· 32 31 return lines 33 32 end 34 33 35 - -- check first line from file for presence of yankbank list header. 36 - -- if it exists, populate current number of entries. 34 + ---check first line from file for presence of yankbank list header. 35 + ---if it exists, populate current number of entries. 37 36 ---@param line string 38 37 ---@return boolean 39 38 local function check_for_header(line) ··· 45 44 return false 46 45 end 47 46 48 - -- function that checks for the presence of a yankbank header on a given line. 49 - -- returns t/f and index, length for entries that exist 47 + ---function that checks for the presence of a yankbank header on a given line. 48 + ---returns t/f and index, length for entries that exist 50 49 ---@param line string: line from file being checked 51 50 ---@return table|nil 52 51 local function check_for_entry(line) ··· 60 59 end 61 60 end 62 61 63 - -- function that reads a yankbank entry from an index to an offset. 62 + ---get line count of a string 63 + ---@param str string 64 + ---@return integer 65 + local function get_line_count(str) 66 + local lines = 1 67 + for i = 1, #str do 68 + local c = str:sub(i, i) 69 + if c == "\n" then 70 + lines = lines + 1 71 + end 72 + end 73 + return lines 74 + end 75 + 76 + ---function that reads a yankbank entry from an index to an offset. 64 77 ---@param i integer: starting index 65 78 ---@param offset integer: stopping point = i+offset 66 79 ---@param lines table: file contents ··· 70 83 for j = i, i + offset - 1 do 71 84 entry[#entry + 1] = lines[j] 72 85 end 86 + -- handle extra newline added to end of entry in bank file 87 + if #entry > 1 then 88 + table.remove(entry) 89 + end 73 90 return entry 74 91 end 75 92 76 - -- remove entry from bankfile 93 + ---remove entry from bankfile 77 94 ---@param file string: bank file name 78 95 local function remove_last_entry(file) 79 96 local f, err = io.open(file, "r+") 80 97 if not f then 81 98 error("Could not open file for reading: " .. err) 82 99 end 83 - -- FIX: extra newline on entries inserted after removal 84 100 85 101 -- read lines from file until matching entry is found 86 102 local lines = {} ··· 89 105 string.match(line, "<YANKBANK_ENTRY:" .. n_entries .. ",%d+,%a+>") 90 106 then 91 107 n_entries = n_entries - 1 92 - lines[1] = "<YANKBANK_LIST:" .. n_entries .. ">\n" 108 + lines[1] = "<YANKBANK_LIST:" .. n_entries .. ">" 93 109 break 94 110 else 95 111 lines[#lines + 1] = line ··· 103 119 error("Could not open file for writing: " .. err) 104 120 end 105 121 for i = 1, #lines do 106 - -- TODO: check if newline is necessary for table 107 122 f:write(lines[i] .. "\n") 108 123 end 109 124 f:close() 110 125 end 111 126 112 - -- TODO: docs or remove function 113 - local function open_file(file, mode) 114 - local f, err = io.open(file, mode) 115 - if not f then 116 - error("Could not open file: " .. err) 117 - end 118 - return f 119 - end 120 - 121 - -- add entry bankfile. (this function needs to be callable from outside the module) 127 + ---add entry bankfile. (this function needs to be callable from outside the module) 122 128 ---@param file string 123 129 ---@param entry table|string 124 130 ---@param reg_type string 125 - -- TODO: trigger in add_yank in clipboard.lua 126 - -- Function scope probably needs to change to a different level (or be callable from persistence.lua) 127 - local function add_to_bankfile(file, entry, reg_type) 131 + function M.add_to_bankfile(file, entry, reg_type) 132 + -- remove last entry if new capacity would exceed maximum 128 133 if n_entries >= m_entries then 129 134 remove_last_entry(file) 130 135 end 131 136 n_entries = n_entries + 1 132 137 133 138 local lines = read_lines(file) 134 - local f = open_file(file, "w+") 139 + local f, err = io.open(file, "w+") 140 + if not f then 141 + error("Could not open file: " .. err) 142 + end 135 143 136 144 -- add list header 137 145 f:write("<YANKBANK_LIST:" .. n_entries .. ">\n") 138 146 147 + -- get line count of entry (special case for strings) 148 + local len = #entry 149 + if type(entry) == "string" then 150 + len = get_line_count(entry) 151 + end 152 + 139 153 -- write entry header 140 - -- FIX: #entry doesn't match number of lines when it is string (number of chars instead of lines) 141 - f:write("<YANKBANK_ENTRY:1," .. #entry .. "," .. reg_type .. ">\n") 142 - -- write entry 154 + f:write("<YANKBANK_ENTRY:1," .. len .. "," .. reg_type .. ">\n") 155 + -- write new entry 143 156 if type(entry) == "string" then 144 - f:write(entry) 157 + f:write(entry .. "\n") 145 158 else 146 159 for i = 1, #entry do 147 - -- TODO: check if newline is necessary for table 148 160 f:write(entry[i] .. "\n") 149 161 end 150 162 end 151 163 152 - -- write remaining lines 164 + -- write back previous entries 153 165 for i = 2, #lines do 154 166 local n, l, rt = 155 167 string.match(lines[i], "<YANKBANK_ENTRY:(%d+),(%d+),(%a+)>") ··· 162 174 .. rt 163 175 .. ">" 164 176 end 165 - -- TODO: check headers 166 177 f:write(lines[i] .. "\n") 167 178 end 168 179 169 180 f:close() 170 181 end 171 182 172 - -- populate yankbank with entries contained in file. 183 + ---populate yankbank with entries contained in file. 173 184 ---@param yanks table: table to populate with yanks 174 185 ---@param file string: yankbank persistence file 175 186 ---@param max_entries integer: maximum number of yankbank entries ··· 200 211 return yanks, reg_types 201 212 end 202 213 203 - -- setup function for a persistence file. 204 - -- should be called in plugin setup function 214 + ---setup function for a persistence file. 215 + ---should be called in plugin setup function 205 216 ---@param file string: file path 206 217 ---@param max_entries integer: maximum number of yankbank entries 207 218 ---@param yanks table: table to populate with yanks ··· 223 234 m_entries = max_entries 224 235 return populate_yankbank(file, max_entries, yanks, reg_types) 225 236 end 226 - 227 - -- TEST: remove later 228 - local yanks = {} 229 - local reg_types = {} 230 - M.setup_persistence("test.txt", 10, yanks, reg_types) 231 - -- print(vim.inspect(yanks)) 232 - -- print(vim.inspect(reg_types)) 233 - yanks = {} 234 - reg_types = {} 235 - -- print(vim.inspect(yanks)) 236 - -- print(vim.inspect(reg_types)) 237 - M.setup_persistence("test1.txt", 10, yanks, reg_types) 238 - m_entries = 10 239 - add_to_bankfile("test1.txt", "text11", "v") 240 - os.execute("sleep .1") 241 - -- add_to_bankfile("test1.txt", "text10", "V") 242 - -- remove_last_entry("test1.txt") 243 237 244 238 return M