@@ -9,31 +9,34 @@ local find_file = require("nvim-tree.actions.finders.find-file").fn
9
9
10
10
local DirectoryNode = require (" nvim-tree.node.directory" )
11
11
12
- --- @enum ACTION
13
- local ACTION = {
14
- copy = " copy" ,
15
- cut = " cut" ,
16
- }
12
+ --- @alias ClipboardAction " copy" | " cut"
13
+ --- @alias ClipboardData table<ClipboardAction , Node[]>
14
+
15
+ --- @alias ClipboardActionFn fun ( source : string , dest : string ): boolean , string ?
17
16
18
17
--- @class Clipboard to handle all actions.fs clipboard API
19
18
--- @field config table hydrated user opts.filters
20
19
--- @field private explorer Explorer
21
- --- @field private data table<ACTION , Node[]>
20
+ --- @field private data ClipboardData
21
+ --- @field private clipboard_name string
22
+ --- @field private reg string
22
23
local Clipboard = {}
23
24
24
25
--- @param opts table user options
25
26
--- @param explorer Explorer
26
27
--- @return Clipboard
27
28
function Clipboard :new (opts , explorer )
29
+ --- @type Clipboard
28
30
local o = {
29
31
explorer = explorer ,
30
32
data = {
31
- [ ACTION . copy ] = {},
32
- [ ACTION . cut ] = {},
33
+ copy = {},
34
+ cut = {},
33
35
},
36
+ clipboard_name = opts .actions .use_system_clipboard and " system" or " neovim" ,
37
+ reg = opts .actions .use_system_clipboard and " +" or " 1" ,
34
38
config = {
35
39
filesystem_watchers = opts .filesystem_watchers ,
36
- actions = opts .actions ,
37
40
},
38
41
}
39
42
47
50
--- @return boolean
48
51
--- @return string | nil
49
52
local function do_copy (source , destination )
50
- local source_stats , handle
51
- local success , errmsg
53
+ local source_stats , err = vim .loop .fs_stat (source )
52
54
53
- source_stats , errmsg = vim .loop .fs_stat (source )
54
55
if not source_stats then
55
- log .line (" copy_paste" , " do_copy fs_stat '%s' failed '%s'" , source , errmsg )
56
- return false , errmsg
56
+ log .line (" copy_paste" , " do_copy fs_stat '%s' failed '%s'" , source , err )
57
+ return false , err
57
58
end
58
59
59
60
log .line (" copy_paste" , " do_copy %s '%s' -> '%s'" , source_stats .type , source , destination )
@@ -64,25 +65,28 @@ local function do_copy(source, destination)
64
65
end
65
66
66
67
if source_stats .type == " file" then
67
- success , errmsg = vim .loop .fs_copyfile (source , destination )
68
+ local success
69
+ success , err = vim .loop .fs_copyfile (source , destination )
68
70
if not success then
69
- log .line (" copy_paste" , " do_copy fs_copyfile failed '%s'" , errmsg )
70
- return false , errmsg
71
+ log .line (" copy_paste" , " do_copy fs_copyfile failed '%s'" , err )
72
+ return false , err
71
73
end
72
74
return true
73
75
elseif source_stats .type == " directory" then
74
- handle , errmsg = vim .loop .fs_scandir (source )
76
+ local handle
77
+ handle , err = vim .loop .fs_scandir (source )
75
78
if type (handle ) == " string" then
76
79
return false , handle
77
80
elseif not handle then
78
- log .line (" copy_paste" , " do_copy fs_scandir '%s' failed '%s'" , source , errmsg )
79
- return false , errmsg
81
+ log .line (" copy_paste" , " do_copy fs_scandir '%s' failed '%s'" , source , err )
82
+ return false , err
80
83
end
81
84
82
- success , errmsg = vim .loop .fs_mkdir (destination , source_stats .mode )
85
+ local success
86
+ success , err = vim .loop .fs_mkdir (destination , source_stats .mode )
83
87
if not success then
84
- log .line (" copy_paste" , " do_copy fs_mkdir '%s' failed '%s'" , destination , errmsg )
85
- return false , errmsg
88
+ log .line (" copy_paste" , " do_copy fs_mkdir '%s' failed '%s'" , destination , err )
89
+ return false , err
86
90
end
87
91
88
92
while true do
@@ -93,44 +97,42 @@ local function do_copy(source, destination)
93
97
94
98
local new_name = utils .path_join ({ source , name })
95
99
local new_destination = utils .path_join ({ destination , name })
96
- success , errmsg = do_copy (new_name , new_destination )
100
+ success , err = do_copy (new_name , new_destination )
97
101
if not success then
98
- return false , errmsg
102
+ return false , err
99
103
end
100
104
end
101
105
else
102
- errmsg = string.format (" '%s' illegal file type '%s'" , source , source_stats .type )
103
- log .line (" copy_paste" , " do_copy %s" , errmsg )
104
- return false , errmsg
106
+ err = string.format (" '%s' illegal file type '%s'" , source , source_stats .type )
107
+ log .line (" copy_paste" , " do_copy %s" , err )
108
+ return false , err
105
109
end
106
110
107
111
return true
108
112
end
109
113
110
114
--- @param source string
111
115
--- @param dest string
112
- --- @param action ACTION
113
- --- @param action_fn fun ( source : string , dest : string )
116
+ --- @param action ClipboardAction
117
+ --- @param action_fn ClipboardActionFn
114
118
--- @return boolean | nil -- success
115
119
--- @return string | nil -- error message
116
120
local function do_single_paste (source , dest , action , action_fn )
117
- local dest_stats
118
- local success , errmsg , errcode
119
121
local notify_source = notify .render_path (source )
120
122
121
123
log .line (" copy_paste" , " do_single_paste '%s' -> '%s'" , source , dest )
122
124
123
- dest_stats , errmsg , errcode = vim .loop .fs_stat (dest )
124
- if not dest_stats and errcode ~= " ENOENT" then
125
- notify .error (" Could not " .. action .. " " .. notify_source .. " - " .. (errmsg or " ???" ))
126
- return false , errmsg
125
+ local dest_stats , err , err_name = vim .loop .fs_stat (dest )
126
+ if not dest_stats and err_name ~= " ENOENT" then
127
+ notify .error (" Could not " .. action .. " " .. notify_source .. " - " .. (err or " ???" ))
128
+ return false , err
127
129
end
128
130
129
131
local function on_process ()
130
- success , errmsg = action_fn (source , dest )
132
+ local success , error = action_fn (source , dest )
131
133
if not success then
132
- notify .error (" Could not " .. action .. " " .. notify_source .. " - " .. (errmsg or " ???" ))
133
- return false , errmsg
134
+ notify .error (" Could not " .. action .. " " .. notify_source .. " - " .. (error or " ???" ))
135
+ return false , error
134
136
end
135
137
136
138
find_file (utils .path_remove_trailing (dest ))
@@ -173,7 +175,7 @@ local function do_single_paste(source, dest, action, action_fn)
173
175
end
174
176
175
177
--- @param node Node
176
- --- @param clip table
178
+ --- @param clip ClipboardData
177
179
local function toggle (node , clip )
178
180
if node .name == " .." then
179
181
return
@@ -191,49 +193,52 @@ end
191
193
192
194
--- Clear copied and cut
193
195
function Clipboard :clear_clipboard ()
194
- self .data [ ACTION .copy ] = {}
195
- self .data [ ACTION .cut ] = {}
196
+ self .data .copy = {}
197
+ self .data .cut = {}
196
198
notify .info (" Clipboard has been emptied." )
197
199
self .explorer .renderer :draw ()
198
200
end
199
201
200
202
--- Copy one node
201
203
--- @param node Node
202
204
function Clipboard :copy (node )
203
- utils .array_remove (self .data [ ACTION .cut ] , node )
204
- toggle (node , self .data [ ACTION .copy ] )
205
+ utils .array_remove (self .data .cut , node )
206
+ toggle (node , self .data .copy )
205
207
self .explorer .renderer :draw ()
206
208
end
207
209
208
210
--- Cut one node
209
211
--- @param node Node
210
212
function Clipboard :cut (node )
211
- utils .array_remove (self .data [ ACTION .copy ] , node )
212
- toggle (node , self .data [ ACTION .cut ] )
213
+ utils .array_remove (self .data .copy , node )
214
+ toggle (node , self .data .cut )
213
215
self .explorer .renderer :draw ()
214
216
end
215
217
216
218
--- Paste cut or cop
217
219
--- @private
218
220
--- @param node Node
219
- --- @param action ACTION
220
- --- @param action_fn fun ( source : string , dest : string )
221
+ --- @param action ClipboardAction
222
+ --- @param action_fn ClipboardActionFn
221
223
function Clipboard :do_paste (node , action , action_fn )
222
224
if node .name == " .." then
223
225
node = self .explorer
224
- elseif node :is (DirectoryNode ) then
225
- node = node :last_group_node ()
226
+ else
227
+ local dir = node :as (DirectoryNode )
228
+ if dir then
229
+ node = dir :last_group_node ()
230
+ end
226
231
end
227
232
local clip = self .data [action ]
228
233
if # clip == 0 then
229
234
return
230
235
end
231
236
232
237
local destination = node .absolute_path
233
- local stats , errmsg , errcode = vim .loop .fs_stat (destination )
234
- if not stats and errcode ~= " ENOENT" then
235
- log .line (" copy_paste" , " do_paste fs_stat '%s' failed '%s'" , destination , errmsg )
236
- notify .error (" Could not " .. action .. " " .. notify .render_path (destination ) .. " - " .. (errmsg or " ???" ))
238
+ local stats , err , err_name = vim .loop .fs_stat (destination )
239
+ if not stats and err_name ~= " ENOENT" then
240
+ log .line (" copy_paste" , " do_paste fs_stat '%s' failed '%s'" , destination , err )
241
+ notify .error (" Could not " .. action .. " " .. notify .render_path (destination ) .. " - " .. (err or " ???" ))
237
242
return
238
243
end
239
244
local is_dir = stats and stats .type == " directory"
@@ -278,24 +283,24 @@ end
278
283
--- Paste cut (if present) or copy (if present)
279
284
--- @param node Node
280
285
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 )
286
+ if self .data .cut [1 ] ~= nil then
287
+ self :do_paste (node , " cut" , do_cut )
288
+ elseif self .data .copy [1 ] ~= nil then
289
+ self :do_paste (node , " copy" , do_copy )
285
290
end
286
291
end
287
292
288
293
function Clipboard :print_clipboard ()
289
294
local content = {}
290
- if # self .data [ ACTION .cut ] > 0 then
295
+ if # self .data .cut > 0 then
291
296
table.insert (content , " Cut" )
292
- for _ , node in pairs (self .data [ ACTION .cut ] ) do
297
+ for _ , node in pairs (self .data .cut ) do
293
298
table.insert (content , " * " .. (notify .render_path (node .absolute_path )))
294
299
end
295
300
end
296
- if # self .data [ ACTION .copy ] > 0 then
301
+ if # self .data .copy > 0 then
297
302
table.insert (content , " Copy" )
298
- for _ , node in pairs (self .data [ ACTION .copy ] ) do
303
+ for _ , node in pairs (self .data .copy ) do
299
304
table.insert (content , " * " .. (notify .render_path (node .absolute_path )))
300
305
end
301
306
end
@@ -305,65 +310,45 @@ end
305
310
306
311
--- @param content string
307
312
function Clipboard :copy_to_reg (content )
308
- local clipboard_name
309
- local reg
310
- if self .config .actions .use_system_clipboard == true then
311
- clipboard_name = " system"
312
- reg = " +"
313
- else
314
- clipboard_name = " neovim"
315
- reg = " 1"
316
- end
317
-
318
313
-- manually firing TextYankPost does not set vim.v.event
319
314
-- workaround: create a scratch buffer with the clipboard contents and send a yank command
320
315
local temp_buf = vim .api .nvim_create_buf (false , true )
321
316
vim .api .nvim_buf_set_text (temp_buf , 0 , 0 , 0 , 0 , { content })
322
317
vim .api .nvim_buf_call (temp_buf , function ()
323
- vim .cmd (string.format (' normal! "%sy$' , reg ))
318
+ vim .cmd (string.format (' normal! "%sy$' , self . reg ))
324
319
end )
325
320
vim .api .nvim_buf_delete (temp_buf , {})
326
321
327
- notify .info (string.format (" Copied %s to %s clipboard!" , content , clipboard_name ))
322
+ notify .info (string.format (" Copied %s to %s clipboard!" , content , self . clipboard_name ))
328
323
end
329
324
330
325
--- @param node Node
331
326
function Clipboard :copy_filename (node )
332
- local content
333
-
334
327
if node .name == " .." then
335
328
-- root
336
- content = vim .fn .fnamemodify (self .explorer .absolute_path , " :t" )
329
+ self : copy_to_reg ( vim .fn .fnamemodify (self .explorer .absolute_path , " :t" ) )
337
330
else
338
331
-- node
339
- content = node .name
332
+ self : copy_to_reg ( node .name )
340
333
end
341
-
342
- self :copy_to_reg (content )
343
334
end
344
335
345
336
--- @param node Node
346
337
function Clipboard :copy_basename (node )
347
- local content
348
-
349
338
if node .name == " .." then
350
339
-- root
351
- content = vim .fn .fnamemodify (self .explorer .absolute_path , " :t:r" )
340
+ self : copy_to_reg ( vim .fn .fnamemodify (self .explorer .absolute_path , " :t:r" ) )
352
341
else
353
342
-- node
354
- content = vim .fn .fnamemodify (node .name , " :r" )
343
+ self : copy_to_reg ( vim .fn .fnamemodify (node .name , " :r" ) )
355
344
end
356
-
357
- self :copy_to_reg (content )
358
345
end
359
346
360
347
--- @param node Node
361
348
function Clipboard :copy_path (node )
362
- local content
363
-
364
349
if node .name == " .." then
365
350
-- root
366
- content = utils .path_add_trailing (" " )
351
+ self : copy_to_reg ( utils .path_add_trailing (" " ) )
367
352
else
368
353
-- node
369
354
local absolute_path = node .absolute_path
@@ -373,10 +358,12 @@ function Clipboard:copy_path(node)
373
358
end
374
359
375
360
local relative_path = utils .path_relative (absolute_path , cwd )
376
- content = node .nodes ~= nil and utils .path_add_trailing (relative_path ) or relative_path
361
+ if node :is (DirectoryNode ) then
362
+ self :copy_to_reg (utils .path_add_trailing (relative_path ))
363
+ else
364
+ self :copy_to_reg (relative_path )
365
+ end
377
366
end
378
-
379
- self :copy_to_reg (content )
380
367
end
381
368
382
369
--- @param node Node
@@ -394,14 +381,14 @@ end
394
381
--- @param node Node
395
382
--- @return boolean
396
383
function Clipboard :is_cut (node )
397
- return vim .tbl_contains (self .data [ ACTION .cut ] , node )
384
+ return vim .tbl_contains (self .data .cut , node )
398
385
end
399
386
400
387
--- Node is copied. Will not be cut.
401
388
--- @param node Node
402
389
--- @return boolean
403
390
function Clipboard :is_copied (node )
404
- return vim .tbl_contains (self .data [ ACTION .copy ] , node )
391
+ return vim .tbl_contains (self .data .copy , node )
405
392
end
406
393
407
394
return Clipboard
0 commit comments