Skip to content

Commit de372bb

Browse files
committed
feat: make fs actions async
1 parent 9e4c395 commit de372bb

File tree

9 files changed

+390
-189
lines changed

9 files changed

+390
-189
lines changed
Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
local log = require "nvim-tree.log"
2+
local async = require "nvim-tree.async"
23
local view = require "nvim-tree.view"
34
local utils = require "nvim-tree.utils"
45
local renderer = require "nvim-tree.renderer"
@@ -37,41 +38,43 @@ function M.fn(fname)
3738

3839
local absolute_paths_searched = {}
3940

40-
local found = Iterator.builder(core.get_explorer().nodes)
41-
:matcher(function(node)
42-
return node.absolute_path == fname_real or node.link_to == fname_real
43-
end)
44-
:applier(function(node)
45-
line = line + 1
41+
async.exec(function()
42+
local found = Iterator.builder(core.get_explorer().nodes)
43+
:matcher(function(node)
44+
return node.absolute_path == fname_real or node.link_to == fname_real
45+
end)
46+
:applier(function(node)
47+
line = line + 1
4648

47-
if vim.tbl_contains(absolute_paths_searched, node.absolute_path) then
48-
return
49-
end
50-
table.insert(absolute_paths_searched, node.absolute_path)
51-
52-
local abs_match = vim.startswith(fname_real, node.absolute_path .. utils.path_separator)
53-
local link_match = node.link_to and vim.startswith(fname_real, node.link_to .. utils.path_separator)
54-
55-
if abs_match or link_match then
56-
node.open = true
57-
if #node.nodes == 0 then
58-
core.get_explorer():expand(node)
49+
if vim.tbl_contains(absolute_paths_searched, node.absolute_path) then
50+
return
5951
end
60-
end
61-
end)
62-
:recursor(function(node)
63-
return node.open and node.nodes
64-
end)
65-
:iterate()
52+
table.insert(absolute_paths_searched, node.absolute_path)
6653

67-
if found and view.is_visible() then
68-
renderer.draw()
69-
view.set_cursor { line, 0 }
70-
end
54+
local abs_match = vim.startswith(fname_real, node.absolute_path .. utils.path_separator)
55+
local link_match = node.link_to and vim.startswith(fname_real, node.link_to .. utils.path_separator)
7156

72-
running[fname_real] = false
73-
74-
log.profile_end(ps, "find file %s", fname_real)
57+
if abs_match or link_match then
58+
node.open = true
59+
if #node.nodes == 0 then
60+
core.get_explorer():expand(node)
61+
end
62+
end
63+
end)
64+
:recursor(function(node)
65+
return node.open and node.nodes
66+
end)
67+
:async_iterate()
68+
69+
async.schedule()
70+
if found and view.is_visible() then
71+
renderer.draw()
72+
view.set_cursor { line, 0 }
73+
end
74+
end, function()
75+
running[fname_real] = false
76+
log.profile_end(ps, "find file %s", fname_real)
77+
end)
7578
end
7679

7780
return M

lua/nvim-tree/actions/fs/copy-paste.lua

Lines changed: 84 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ local utils = require "nvim-tree.utils"
44
local core = require "nvim-tree.core"
55
local events = require "nvim-tree.events"
66
local notify = require "nvim-tree.notify"
7+
local async = require "nvim-tree.async"
78

89
local M = {}
910

@@ -12,9 +13,75 @@ local clipboard = {
1213
copy = {},
1314
}
1415

16+
local function do_copy_dir(source, destination, mode)
17+
errmsg, handle = async.call(function(cb)
18+
return vim.loop.fs_opendir(source, cb, 32)
19+
end)
20+
if not handle then
21+
log.line("copy_paste", "do_copy fs_scandir '%s' failed '%s'", source, errmsg)
22+
return false, errmsg
23+
end
24+
25+
local _, stats = async.call(vim.loop.fs_stat, destination)
26+
if stats then
27+
errMsg = async.call(vim.loop.fs_chmod, destination, mode)
28+
if errMsg then
29+
log.line("copy_paste", "do_copy fs_chmod '%s' failed '%s'", destination, errmsg)
30+
-- ignore error and continue, the dir exists
31+
end
32+
else
33+
errmsg, success = async.call(vim.loop.fs_mkdir, destination, mode)
34+
if not success then
35+
log.line("copy_paste", "do_copy fs_mkdir '%s' failed '%s'", destination, errmsg)
36+
return false, errmsg
37+
end
38+
end
39+
40+
while true do
41+
local _, entries = async.call(vim.loop.fs_readdir, handle)
42+
if not entries or #entries == 0 then
43+
break
44+
end
45+
local cp_tasks = {}
46+
for _, entry in pairs(entries) do
47+
local name = entry.name
48+
local type = entry.type
49+
local new_name = utils.path_join { source, name }
50+
local new_destination = utils.path_join { destination, name }
51+
52+
errMsg, stats = async.call(vim.loop.fs_stat, new_name)
53+
if errMsg then
54+
log.line("copy_paste", "do_copy fs_stat '%s' failed '%s'", new_name, errmsg)
55+
else
56+
if type == "directory" then
57+
success, errmsg = do_copy_dir(new_name, new_destination, stats.mode)
58+
if not success then
59+
return false, errmsg
60+
end
61+
else
62+
table.insert(cp_tasks, function()
63+
errmsg, success = async.call(vim.loop.fs_copyfile, new_name, new_destination, nil)
64+
if not success then
65+
log.line("copy_paste", "do_copy fs_copyfile failed '%s'", errmsg)
66+
return false, errmsg
67+
end
68+
return true
69+
end)
70+
end
71+
end
72+
end
73+
for _, result in ipairs(async.all(unpack(cp_tasks))) do
74+
success = unpack(result)
75+
if not success then
76+
return false
77+
end
78+
end
79+
end
80+
return true
81+
end
82+
1583
local function do_copy(source, destination)
16-
local source_stats, handle
17-
local success, errmsg
84+
local source_stats, success, errmsg
1885

1986
source_stats, errmsg = vim.loop.fs_stat(source)
2087
if not source_stats then
@@ -30,40 +97,16 @@ local function do_copy(source, destination)
3097
end
3198

3299
if source_stats.type == "file" then
33-
success, errmsg = vim.loop.fs_copyfile(source, destination)
100+
errmsg, success = async.call(vim.loop.fs_copyfile, source, destination)
34101
if not success then
35102
log.line("copy_paste", "do_copy fs_copyfile failed '%s'", errmsg)
36103
return false, errmsg
37104
end
38-
return true
39105
elseif source_stats.type == "directory" then
40-
handle, errmsg = vim.loop.fs_scandir(source)
41-
if type(handle) == "string" then
42-
return false, handle
43-
elseif not handle then
44-
log.line("copy_paste", "do_copy fs_scandir '%s' failed '%s'", source, errmsg)
45-
return false, errmsg
46-
end
47-
48-
success, errmsg = vim.loop.fs_mkdir(destination, source_stats.mode)
106+
success, errmsg = do_copy_dir(source, destination, source_stats.mode)
49107
if not success then
50-
log.line("copy_paste", "do_copy fs_mkdir '%s' failed '%s'", destination, errmsg)
51108
return false, errmsg
52109
end
53-
54-
while true do
55-
local name, _ = vim.loop.fs_scandir_next(handle)
56-
if not name then
57-
break
58-
end
59-
60-
local new_name = utils.path_join { source, name }
61-
local new_destination = utils.path_join { destination, name }
62-
success, errmsg = do_copy(new_name, new_destination)
63-
if not success then
64-
return false, errmsg
65-
end
66-
end
67110
else
68111
errmsg = string.format("'%s' illegal file type '%s'", source, source_stats.type)
69112
log.line("copy_paste", "do_copy %s", errmsg)
@@ -96,19 +139,17 @@ local function do_single_paste(source, dest, action_type, action_fn)
96139
if dest_stats then
97140
local prompt_select = "Overwrite " .. dest .. " ?"
98141
local prompt_input = prompt_select .. " y/n/r(ename): "
99-
lib.prompt(prompt_input, prompt_select, { "y", "n", "r" }, { "Yes", "No", "Rename" }, function(item_short)
142+
local item_short = async.call(lib.prompt, prompt_input, prompt_select, { "y", "n", "r" }, { "Yes", "No", "Rename" })
143+
utils.clear_prompt()
144+
if item_short == "y" then
145+
on_process()
146+
elseif item_short == "r" then
147+
local new_dest = async.call(vim.ui.input, { prompt = "Rename to ", default = dest, completion = "dir" })
100148
utils.clear_prompt()
101-
if item_short == "y" then
102-
on_process()
103-
elseif item_short == "r" then
104-
vim.ui.input({ prompt = "Rename to ", default = dest, completion = "dir" }, function(new_dest)
105-
utils.clear_prompt()
106-
if new_dest then
107-
do_single_paste(source, new_dest, action_type, action_fn)
108-
end
109-
end)
149+
if new_dest then
150+
do_single_paste(source, new_dest, action_type, action_fn)
110151
end
111-
end)
152+
end
112153
else
113154
on_process()
114155
end
@@ -184,24 +225,24 @@ local function do_cut(source, destination)
184225
return true
185226
end
186227

187-
events._dispatch_will_rename_node(source, destination)
188-
local success, errmsg = vim.loop.fs_rename(source, destination)
228+
local errmsg, success = async.call(vim.loop.fs_rename, source, destination)
189229
if not success then
190230
log.line("copy_paste", "do_cut fs_rename failed '%s'", errmsg)
191231
return false, errmsg
192232
end
233+
async.schedule()
193234
utils.rename_loaded_buffers(source, destination)
194235
events._dispatch_node_renamed(source, destination)
195236
return true
196237
end
197238

198-
function M.paste(node)
239+
M.paste = async.wrap(function(node)
199240
if clipboard.move[1] ~= nil then
200241
return do_paste(node, "move", do_cut)
201242
end
202243

203244
return do_paste(node, "copy", do_copy)
204-
end
245+
end)
205246

206247
function M.print_clipboard()
207248
local content = {}

lua/nvim-tree/actions/fs/create-file.lua

Lines changed: 47 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ local core = require "nvim-tree.core"
55
local notify = require "nvim-tree.notify"
66

77
local find_file = require("nvim-tree.actions.finders.find-file").fn
8+
local async = require "nvim-tree.async"
89

910
local M = {}
1011

1112
local function create_and_notify(file)
12-
local ok, fd = pcall(vim.loop.fs_open, file, "w", 420)
13-
if not ok then
13+
local err, fd = async.call(vim.loop.fs_open, file, "w", 420)
14+
if err then
1415
notify.error("Couldn't create file " .. file)
1516
return
1617
end
17-
vim.loop.fs_close(fd)
18+
async.call(vim.loop.fs_close, fd)
1819
events._dispatch_file_created(file)
1920
end
2021

@@ -49,7 +50,7 @@ local function get_containing_folder(node)
4950
return node.absolute_path:sub(0, -node_name_size - 1)
5051
end
5152

52-
function M.fn(node)
53+
M.fn = async.wrap(function(node)
5354
node = node and lib.get_last_group_node(node)
5455
if not node or node.name == ".." then
5556
node = {
@@ -63,53 +64,53 @@ function M.fn(node)
6364

6465
local input_opts = { prompt = "Create file ", default = containing_folder, completion = "file" }
6566

66-
vim.ui.input(input_opts, function(new_file_path)
67-
utils.clear_prompt()
68-
if not new_file_path or new_file_path == containing_folder then
69-
return
70-
end
67+
local new_file_path = async.call(vim.ui.input, input_opts)
68+
utils.clear_prompt()
69+
if not new_file_path or new_file_path == containing_folder then
70+
return
71+
end
7172

72-
if utils.file_exists(new_file_path) then
73-
notify.warn "Cannot create: file already exists"
74-
return
75-
end
73+
if utils.file_exists(new_file_path) then
74+
notify.warn "Cannot create: file already exists"
75+
return
76+
end
7677

77-
-- create a folder for each path element if the folder does not exist
78-
-- if the answer ends with a /, create a file for the last path element
79-
local is_last_path_file = not new_file_path:match(utils.path_separator .. "$")
80-
local path_to_create = ""
81-
local idx = 0
82-
83-
local num_nodes = get_num_nodes(utils.path_split(utils.path_remove_trailing(new_file_path)))
84-
local is_error = false
85-
for path in utils.path_split(new_file_path) do
86-
idx = idx + 1
87-
local p = utils.path_remove_trailing(path)
88-
if #path_to_create == 0 and vim.fn.has "win32" == 1 then
89-
path_to_create = utils.path_join { p, path_to_create }
90-
else
91-
path_to_create = utils.path_join { path_to_create, p }
92-
end
93-
if is_last_path_file and idx == num_nodes then
94-
create_file(path_to_create)
95-
elseif not utils.file_exists(path_to_create) then
96-
local success = vim.loop.fs_mkdir(path_to_create, 493)
97-
if not success then
98-
notify.error("Could not create folder " .. path_to_create)
99-
is_error = true
100-
break
101-
end
102-
events._dispatch_folder_created(new_file_path)
103-
end
78+
-- create a folder for each path element if the folder does not exist
79+
-- if the answer ends with a /, create a file for the last path element
80+
local is_last_path_file = not new_file_path:match(utils.path_separator .. "$")
81+
local path_to_create = ""
82+
local idx = 0
83+
84+
local num_nodes = get_num_nodes(utils.path_split(utils.path_remove_trailing(new_file_path)))
85+
local is_error = false
86+
for path in utils.path_split(new_file_path) do
87+
idx = idx + 1
88+
local p = utils.path_remove_trailing(path)
89+
async.schedule()
90+
if #path_to_create == 0 and vim.fn.has "win32" == 1 then
91+
path_to_create = utils.path_join { p, path_to_create }
92+
else
93+
path_to_create = utils.path_join { path_to_create, p }
10494
end
105-
if not is_error then
106-
notify.info(new_file_path .. " was properly created")
95+
if is_last_path_file and idx == num_nodes then
96+
create_file(path_to_create)
97+
elseif not utils.file_exists(path_to_create) then
98+
local err = async.call(vim.loop.fs_mkdir, path_to_create, 493)
99+
if err then
100+
notify.error("Could not create folder " .. path_to_create .. " :" .. err)
101+
is_error = true
102+
break
103+
end
104+
events._dispatch_folder_created(new_file_path)
107105
end
106+
end
107+
if not is_error then
108+
notify.info(new_file_path .. " was properly created")
109+
end
108110

109-
-- synchronously refreshes as we can't wait for the watchers
110-
find_file(utils.path_remove_trailing(new_file_path))
111-
end)
112-
end
111+
-- synchronously refreshes as we can't wait for the watchers
112+
find_file(utils.path_remove_trailing(new_file_path))
113+
end)
113114

114115
function M.setup(opts)
115116
M.enable_reload = not opts.filesystem_watchers.enable

0 commit comments

Comments
 (0)