Skip to content

Commit 6fbcb5a

Browse files
authored
refactor(#2831): multi instance clipboard (#2869)
* refactor(#2831): multi instance clipboard * refactor(#2831): multi instance clipboard * refactor(#2831): multi instance clipboard
1 parent e962e97 commit 6fbcb5a

File tree

6 files changed

+100
-79
lines changed

6 files changed

+100
-79
lines changed

Diff for: lua/nvim-tree/actions/fs/copy-paste.lua renamed to lua/nvim-tree/actions/fs/clipboard.lua

+83-58
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,38 @@ local reloaders = require "nvim-tree.actions.reloaders"
99

1010
local find_file = require("nvim-tree.actions.finders.find-file").fn
1111

12-
local M = {
13-
config = {},
12+
---@enum ACTION
13+
local ACTION = {
14+
copy = "copy",
15+
cut = "cut",
1416
}
1517

16-
local clipboard = {
17-
cut = {},
18-
copy = {},
19-
}
18+
---@class Clipboard to handle all actions.fs clipboard API
19+
---@field config table hydrated user opts.filters
20+
---@field private explorer Explorer
21+
---@field private data table<ACTION, Node[]>
22+
local Clipboard = {}
23+
24+
---@param opts table user options
25+
---@param explorer Explorer
26+
---@return Clipboard
27+
function Clipboard:new(opts, explorer)
28+
local o = {
29+
explorer = explorer,
30+
data = {
31+
[ACTION.copy] = {},
32+
[ACTION.cut] = {},
33+
},
34+
config = {
35+
filesystem_watchers = opts.filesystem_watchers,
36+
actions = opts.actions,
37+
},
38+
}
39+
40+
setmetatable(o, self)
41+
self.__index = self
42+
return o
43+
end
2044

2145
---@param source string
2246
---@param destination string
@@ -85,11 +109,11 @@ end
85109

86110
---@param source string
87111
---@param dest string
88-
---@param action_type string
112+
---@param action ACTION
89113
---@param action_fn fun(source: string, dest: string)
90114
---@return boolean|nil -- success
91115
---@return string|nil -- error message
92-
local function do_single_paste(source, dest, action_type, action_fn)
116+
local function do_single_paste(source, dest, action, action_fn)
93117
local dest_stats
94118
local success, errmsg, errcode
95119
local notify_source = notify.render_path(source)
@@ -98,14 +122,14 @@ local function do_single_paste(source, dest, action_type, action_fn)
98122

99123
dest_stats, errmsg, errcode = vim.loop.fs_stat(dest)
100124
if not dest_stats and errcode ~= "ENOENT" then
101-
notify.error("Could not " .. action_type .. " " .. notify_source .. " - " .. (errmsg or "???"))
125+
notify.error("Could not " .. action .. " " .. notify_source .. " - " .. (errmsg or "???"))
102126
return false, errmsg
103127
end
104128

105129
local function on_process()
106130
success, errmsg = action_fn(source, dest)
107131
if not success then
108-
notify.error("Could not " .. action_type .. " " .. notify_source .. " - " .. (errmsg or "???"))
132+
notify.error("Could not " .. action .. " " .. notify_source .. " - " .. (errmsg or "???"))
109133
return false, errmsg
110134
end
111135

@@ -123,7 +147,7 @@ local function do_single_paste(source, dest, action_type, action_fn)
123147
vim.ui.input(input_opts, function(new_dest)
124148
utils.clear_prompt()
125149
if new_dest then
126-
do_single_paste(source, new_dest, action_type, action_fn)
150+
do_single_paste(source, new_dest, action, action_fn)
127151
end
128152
end)
129153
else
@@ -137,7 +161,7 @@ local function do_single_paste(source, dest, action_type, action_fn)
137161
vim.ui.input(input_opts, function(new_dest)
138162
utils.clear_prompt()
139163
if new_dest then
140-
do_single_paste(source, new_dest, action_type, action_fn)
164+
do_single_paste(source, new_dest, action, action_fn)
141165
end
142166
end)
143167
end
@@ -165,37 +189,42 @@ local function toggle(node, clip)
165189
notify.info(notify_node .. " added to clipboard.")
166190
end
167191

168-
function M.clear_clipboard()
169-
clipboard.cut = {}
170-
clipboard.copy = {}
192+
---Clear copied and cut
193+
function Clipboard:clear_clipboard()
194+
self.data[ACTION.copy] = {}
195+
self.data[ACTION.cut] = {}
171196
notify.info "Clipboard has been emptied."
172197
renderer.draw()
173198
end
174199

200+
---Copy one node
175201
---@param node Node
176-
function M.copy(node)
177-
utils.array_remove(clipboard.cut, node)
178-
toggle(node, clipboard.copy)
202+
function Clipboard:copy(node)
203+
utils.array_remove(self.data[ACTION.cut], node)
204+
toggle(node, self.data[ACTION.copy])
179205
renderer.draw()
180206
end
181207

208+
---Cut one node
182209
---@param node Node
183-
function M.cut(node)
184-
utils.array_remove(clipboard.copy, node)
185-
toggle(node, clipboard.cut)
210+
function Clipboard:cut(node)
211+
utils.array_remove(self.data[ACTION.copy], node)
212+
toggle(node, self.data[ACTION.cut])
186213
renderer.draw()
187214
end
188215

216+
---Paste cut or cop
217+
---@private
189218
---@param node Node
190-
---@param action_type string
219+
---@param action ACTION
191220
---@param action_fn fun(source: string, dest: string)
192-
local function do_paste(node, action_type, action_fn)
221+
function Clipboard:do_paste(node, action, action_fn)
193222
node = lib.get_last_group_node(node)
194223
local explorer = core.get_explorer()
195224
if node.name == ".." and explorer then
196225
node = explorer
197226
end
198-
local clip = clipboard[action_type]
227+
local clip = self.data[action]
199228
if #clip == 0 then
200229
return
201230
end
@@ -204,7 +233,7 @@ local function do_paste(node, action_type, action_fn)
204233
local stats, errmsg, errcode = vim.loop.fs_stat(destination)
205234
if not stats and errcode ~= "ENOENT" then
206235
log.line("copy_paste", "do_paste fs_stat '%s' failed '%s'", destination, errmsg)
207-
notify.error("Could not " .. action_type .. " " .. notify.render_path(destination) .. " - " .. (errmsg or "???"))
236+
notify.error("Could not " .. action .. " " .. notify.render_path(destination) .. " - " .. (errmsg or "???"))
208237
return
209238
end
210239
local is_dir = stats and stats.type == "directory"
@@ -214,11 +243,11 @@ local function do_paste(node, action_type, action_fn)
214243

215244
for _, _node in ipairs(clip) do
216245
local dest = utils.path_join { destination, _node.name }
217-
do_single_paste(_node.absolute_path, dest, action_type, action_fn)
246+
do_single_paste(_node.absolute_path, dest, action, action_fn)
218247
end
219248

220-
clipboard[action_type] = {}
221-
if not M.config.filesystem_watchers.enable then
249+
self.data[action] = {}
250+
if not self.config.filesystem_watchers.enable then
222251
reloaders.reload_explorer()
223252
end
224253
end
@@ -246,26 +275,27 @@ local function do_cut(source, destination)
246275
return true
247276
end
248277

278+
---Paste cut (if present) or copy (if present)
249279
---@param node Node
250-
function M.paste(node)
251-
if clipboard.cut[1] ~= nil then
252-
do_paste(node, "cut", do_cut)
253-
else
254-
do_paste(node, "copy", do_copy)
280+
function Clipboard:paste(node)
281+
if self.data[ACTION.cut][1] ~= nil then
282+
self:do_paste(node, ACTION.cut, do_cut)
283+
elseif self.data[ACTION.copy][1] ~= nil then
284+
self:do_paste(node, ACTION.copy, do_copy)
255285
end
256286
end
257287

258-
function M.print_clipboard()
288+
function Clipboard:print_clipboard()
259289
local content = {}
260-
if #clipboard.cut > 0 then
290+
if #self.data[ACTION.cut] > 0 then
261291
table.insert(content, "Cut")
262-
for _, node in pairs(clipboard.cut) do
292+
for _, node in pairs(self.data[ACTION.cut]) do
263293
table.insert(content, " * " .. (notify.render_path(node.absolute_path)))
264294
end
265295
end
266-
if #clipboard.copy > 0 then
296+
if #self.data[ACTION.copy] > 0 then
267297
table.insert(content, "Copy")
268-
for _, node in pairs(clipboard.copy) do
298+
for _, node in pairs(self.data[ACTION.copy]) do
269299
table.insert(content, " * " .. (notify.render_path(node.absolute_path)))
270300
end
271301
end
@@ -274,10 +304,10 @@ function M.print_clipboard()
274304
end
275305

276306
---@param content string
277-
local function copy_to_clipboard(content)
307+
function Clipboard:copy_to_reg(content)
278308
local clipboard_name
279309
local reg
280-
if M.config.actions.use_system_clipboard == true then
310+
if self.config.actions.use_system_clipboard == true then
281311
clipboard_name = "system"
282312
reg = "+"
283313
else
@@ -298,18 +328,18 @@ local function copy_to_clipboard(content)
298328
end
299329

300330
---@param node Node
301-
function M.copy_filename(node)
302-
copy_to_clipboard(node.name)
331+
function Clipboard:copy_filename(node)
332+
self:copy_to_reg(node.name)
303333
end
304334

305335
---@param node Node
306-
function M.copy_basename(node)
336+
function Clipboard:copy_basename(node)
307337
local basename = vim.fn.fnamemodify(node.name, ":r")
308-
copy_to_clipboard(basename)
338+
self:copy_to_reg(basename)
309339
end
310340

311341
---@param node Node
312-
function M.copy_path(node)
342+
function Clipboard:copy_path(node)
313343
local absolute_path = node.absolute_path
314344
local cwd = core.get_cwd()
315345
if cwd == nil then
@@ -318,33 +348,28 @@ function M.copy_path(node)
318348

319349
local relative_path = utils.path_relative(absolute_path, cwd)
320350
local content = node.nodes ~= nil and utils.path_add_trailing(relative_path) or relative_path
321-
copy_to_clipboard(content)
351+
self:copy_to_reg(content)
322352
end
323353

324354
---@param node Node
325-
function M.copy_absolute_path(node)
355+
function Clipboard:copy_absolute_path(node)
326356
local absolute_path = node.absolute_path
327357
local content = node.nodes ~= nil and utils.path_add_trailing(absolute_path) or absolute_path
328-
copy_to_clipboard(content)
358+
self:copy_to_reg(content)
329359
end
330360

331361
---Node is cut. Will not be copied.
332362
---@param node Node
333363
---@return boolean
334-
function M.is_cut(node)
335-
return vim.tbl_contains(clipboard.cut, node)
364+
function Clipboard:is_cut(node)
365+
return vim.tbl_contains(self.data[ACTION.cut], node)
336366
end
337367

338368
---Node is copied. Will not be cut.
339369
---@param node Node
340370
---@return boolean
341-
function M.is_copied(node)
342-
return vim.tbl_contains(clipboard.copy, node)
343-
end
344-
345-
function M.setup(opts)
346-
M.config.filesystem_watchers = opts.filesystem_watchers
347-
M.config.actions = opts.actions
371+
function Clipboard:is_copied(node)
372+
return vim.tbl_contains(self.data[ACTION.copy], node)
348373
end
349374

350-
return M
375+
return Clipboard

Diff for: lua/nvim-tree/actions/fs/init.lua

-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
local M = {}
22

3-
M.copy_paste = require "nvim-tree.actions.fs.copy-paste"
43
M.create_file = require "nvim-tree.actions.fs.create-file"
54
M.remove_file = require "nvim-tree.actions.fs.remove-file"
65
M.rename_file = require "nvim-tree.actions.fs.rename-file"
76
M.trash = require "nvim-tree.actions.fs.trash"
87

98
function M.setup(opts)
10-
M.copy_paste.setup(opts)
119
M.remove_file.setup(opts)
1210
M.rename_file.setup(opts)
1311
M.trash.setup(opts)

Diff for: lua/nvim-tree/api.lua

+9-9
Original file line numberDiff line numberDiff line change
@@ -174,15 +174,15 @@ Api.fs.rename = wrap_node(actions.fs.rename_file.fn ":t")
174174
Api.fs.rename_sub = wrap_node(actions.fs.rename_file.fn ":p:h")
175175
Api.fs.rename_basename = wrap_node(actions.fs.rename_file.fn ":t:r")
176176
Api.fs.rename_full = wrap_node(actions.fs.rename_file.fn ":p")
177-
Api.fs.cut = wrap_node(actions.fs.copy_paste.cut)
178-
Api.fs.paste = wrap_node(actions.fs.copy_paste.paste)
179-
Api.fs.clear_clipboard = wrap(actions.fs.copy_paste.clear_clipboard)
180-
Api.fs.print_clipboard = wrap(actions.fs.copy_paste.print_clipboard)
181-
Api.fs.copy.node = wrap_node(actions.fs.copy_paste.copy)
182-
Api.fs.copy.absolute_path = wrap_node(actions.fs.copy_paste.copy_absolute_path)
183-
Api.fs.copy.filename = wrap_node(actions.fs.copy_paste.copy_filename)
184-
Api.fs.copy.basename = wrap_node(actions.fs.copy_paste.copy_basename)
185-
Api.fs.copy.relative_path = wrap_node(actions.fs.copy_paste.copy_path)
177+
Api.fs.cut = wrap_node(wrap_explorer_member("clipboard", "cut"))
178+
Api.fs.paste = wrap_node(wrap_explorer_member("clipboard", "paste"))
179+
Api.fs.clear_clipboard = wrap_explorer_member("clipboard", "clear_clipboard")
180+
Api.fs.print_clipboard = wrap_explorer_member("clipboard", "print_clipboard")
181+
Api.fs.copy.node = wrap_node(wrap_explorer_member("clipboard", "copy"))
182+
Api.fs.copy.absolute_path = wrap_node(wrap_explorer_member("clipboard", "copy_absolute_path"))
183+
Api.fs.copy.filename = wrap_node(wrap_explorer_member("clipboard", "copy_filename"))
184+
Api.fs.copy.basename = wrap_node(wrap_explorer_member("clipboard", "copy_basename"))
185+
Api.fs.copy.relative_path = wrap_node(wrap_explorer_member("clipboard", "copy_path"))
186186

187187
---@param mode string
188188
---@param node table

Diff for: lua/nvim-tree/explorer/init.lua

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ local Filters = require "nvim-tree.explorer.filters"
66
local Marks = {} -- circular dependencies
77
local LiveFilter = require "nvim-tree.explorer.live-filter"
88
local Sorters = require "nvim-tree.explorer.sorters"
9+
local Clipboard = {} -- circular dependencies
910

1011
local M = {}
1112

@@ -20,6 +21,7 @@ M.reload = require("nvim-tree.explorer.reload").reload
2021
---@field live_filter LiveFilter
2122
---@field sorters Sorter
2223
---@field marks Marks
24+
---@field clipboard Clipboard
2325

2426
local Explorer = {}
2527
Explorer.__index = Explorer
@@ -50,6 +52,7 @@ function Explorer.new(path)
5052
explorer.filters = Filters:new(M.config, explorer)
5153
explorer.live_filter = LiveFilter:new(M.config, explorer)
5254
explorer.marks = Marks:new(M.config, explorer)
55+
explorer.clipboard = Clipboard:new(M.config, explorer)
5356
explorer:_load(explorer)
5457
return explorer
5558
end
@@ -87,6 +90,7 @@ function M.setup(opts)
8790
require("nvim-tree.explorer.watch").setup(opts)
8891

8992
Marks = require "nvim-tree.marks"
93+
Clipboard = require "nvim-tree.actions.fs.clipboard"
9094
end
9195

9296
M.Explorer = Explorer

Diff for: lua/nvim-tree/renderer/decorator/copied.lua

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
local copy_paste
1+
local core = require "nvim-tree.core"
22

33
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
44
local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
@@ -20,17 +20,14 @@ function DecoratorCopied:new(opts)
2020
})
2121
---@cast o DecoratorCopied
2222

23-
-- cyclic
24-
copy_paste = copy_paste or require "nvim-tree.actions.fs.copy-paste"
25-
2623
return o
2724
end
2825

2926
---Copied highlight: renderer.highlight_clipboard and node is copied
3027
---@param node Node
3128
---@return string|nil group
3229
function DecoratorCopied:calculate_highlight(node)
33-
if self.hl_pos ~= HL_POSITION.none and copy_paste.is_copied(node) then
30+
if self.hl_pos ~= HL_POSITION.none and core.get_explorer() and core.get_explorer().clipboard:is_copied(node) then
3431
return "NvimTreeCopiedHL"
3532
end
3633
end

0 commit comments

Comments
 (0)