Skip to content

Commit 6e7f5ea

Browse files
committed
feat: async rename and create file
1 parent e8a89db commit 6e7f5ea

File tree

6 files changed

+327
-60
lines changed

6 files changed

+327
-60
lines changed

doc/nvim-tree-lua.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,12 @@ Subsequent calls to setup will replace the previous configuration.
408408
watcher = false,
409409
},
410410
},
411+
experimental = {
412+
async = {
413+
create_file = false,
414+
rename_file = false,
415+
}
416+
}
411417
} -- END_DEFAULT_OPTS
412418
<
413419

@@ -1206,6 +1212,20 @@ Configuration for diagnostic logging.
12061212
|nvim-tree.filesystem_watchers| processing, verbose.
12071213
Type: `boolean`, Default: `false`
12081214

1215+
*nvim-tree.experimental*
1216+
Configuration for experimental features.
1217+
1218+
*nvim-tree.experimental.async*
1219+
Control experimental async behavior.
1220+
1221+
*nvim-tree.experimental.async.create_file*
1222+
Toggle async behavior of create file operation.
1223+
Type: `boolean`, Default: `false`
1224+
1225+
*nvim-tree.experimental.async.rename_file*
1226+
Toggle async behavior of rename file operation.
1227+
Type: `boolean`, Default: `false`
1228+
12091229
==============================================================================
12101230
5. API *nvim-tree-api*
12111231

lua/nvim-tree.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,12 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
733733
watcher = false,
734734
},
735735
},
736+
experimental = {
737+
async = {
738+
create_file = false,
739+
rename_file = false,
740+
},
741+
},
736742
} -- END_DEFAULT_OPTS
737743

738744
local function merge_options(conf)

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

Lines changed: 88 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,28 @@ 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

12+
---@async
1113
local function create_and_notify(file)
12-
local ok, fd = pcall(vim.loop.fs_open, file, "w", 420)
13-
if not ok then
14+
local fd, err
15+
if M.enable_async then
16+
err, fd = async.call(vim.loop.fs_open, file, "w", 420)
17+
else
18+
fd, err = vim.loop.fs_open(file, "w", 420)
19+
end
20+
if err then
1421
notify.error("Couldn't create file " .. file)
1522
return
1623
end
17-
vim.loop.fs_close(fd)
18-
events._dispatch_file_created(file)
19-
end
20-
21-
local function create_file(file)
22-
if utils.file_exists(file) then
23-
local prompt_select = "Overwrite " .. file .. " ?"
24-
local prompt_input = prompt_select .. " y/n: "
25-
lib.prompt(prompt_input, prompt_select, { "y", "n" }, { "Yes", "No" }, function(item_short)
26-
utils.clear_prompt()
27-
if item_short == "y" then
28-
create_and_notify(file)
29-
end
30-
end)
24+
if M.enable_async then
25+
async.call(vim.loop.fs_close, fd)
3126
else
32-
create_and_notify(file)
27+
vim.loop.fs_close(fd)
3328
end
29+
events._dispatch_file_created(file)
3430
end
3531

3632
local function get_num_nodes(iter)
@@ -41,6 +37,68 @@ local function get_num_nodes(iter)
4137
return i
4238
end
4339

40+
local function create_file(new_file_path)
41+
-- create a folder for each path element if the folder does not exist
42+
-- if the answer ends with a /, create a file for the last path element
43+
local is_last_path_file = not new_file_path:match(utils.path_separator .. "$")
44+
local path_to_create = ""
45+
local idx = 0
46+
47+
local num_nodes = get_num_nodes(utils.path_split(utils.path_remove_trailing(new_file_path)))
48+
local is_error = false
49+
for path in utils.path_split(new_file_path) do
50+
idx = idx + 1
51+
local p = utils.path_remove_trailing(path)
52+
if #path_to_create == 0 and utils.is_windows then
53+
path_to_create = utils.path_join { p, path_to_create }
54+
else
55+
path_to_create = utils.path_join { path_to_create, p }
56+
end
57+
if is_last_path_file and idx == num_nodes then
58+
if M.enable_async then
59+
async.schedule()
60+
end
61+
if utils.file_exists(new_file_path) then
62+
local prompt_select = "Overwrite " .. new_file_path .. " ?"
63+
local prompt_input = prompt_select .. " y/n: "
64+
if M.enable_async then
65+
local item_short = async.call(lib.prompt, prompt_input, prompt_select, { "y", "n" }, { "Yes", "No" })
66+
utils.clear_prompt()
67+
if item_short == "y" then
68+
create_and_notify(new_file_path)
69+
end
70+
else
71+
lib.prompt(prompt_input, prompt_select, { "y", "n" }, { "Yes", "No" }, function(item_short)
72+
utils.clear_prompt()
73+
if item_short == "y" then
74+
create_and_notify(new_file_path)
75+
end
76+
end)
77+
end
78+
else
79+
create_and_notify(new_file_path)
80+
end
81+
elseif not utils.file_exists(path_to_create) then
82+
local err
83+
if M.enable_async then
84+
err = async.call(vim.loop.fs_mkdir, path_to_create, 493)
85+
else
86+
local _
87+
_, err = vim.loop.fs_mkdir(path_to_create, 493)
88+
end
89+
if err then
90+
notify.error("Could not create folder " .. path_to_create .. ": " .. err)
91+
is_error = true
92+
break
93+
end
94+
events._dispatch_folder_created(new_file_path)
95+
end
96+
end
97+
if not is_error then
98+
notify.info(new_file_path .. " was properly created")
99+
end
100+
end
101+
44102
local function get_containing_folder(node)
45103
if node.nodes ~= nil then
46104
return utils.path_add_trailing(node.absolute_path)
@@ -49,7 +107,8 @@ local function get_containing_folder(node)
49107
return node.absolute_path:sub(0, -node_name_size - 1)
50108
end
51109

52-
function M.fn(node)
110+
--TODO: once async feature is finalized, use `async.wrap` instead of cb param
111+
function M.fn(node, cb)
53112
node = node and lib.get_last_group_node(node)
54113
if not node or node.name == ".." then
55114
node = {
@@ -74,45 +133,24 @@ function M.fn(node)
74133
return
75134
end
76135

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
136+
if M.enable_async then
137+
async.exec(create_file, new_file_path, function(err)
138+
find_file(utils.path_remove_trailing(new_file_path))
139+
if cb then
140+
cb(err)
101141
end
102-
events._dispatch_folder_created(new_file_path)
103-
end
104-
end
105-
if not is_error then
106-
notify.info(new_file_path .. " was properly created")
142+
end)
143+
else
144+
create_file(new_file_path)
145+
-- synchronously refreshes as we can't wait for the watchers
146+
find_file(utils.path_remove_trailing(new_file_path))
107147
end
108-
109-
-- synchronously refreshes as we can't wait for the watchers
110-
find_file(utils.path_remove_trailing(new_file_path))
111148
end)
112149
end
113150

114151
function M.setup(opts)
115152
M.enable_reload = not opts.filesystem_watchers.enable
153+
M.enable_async = opts.experimental.async.create_file
116154
end
117155

118156
return M

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

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ local lib = require "nvim-tree.lib"
22
local utils = require "nvim-tree.utils"
33
local events = require "nvim-tree.events"
44
local notify = require "nvim-tree.notify"
5+
local async = require "nvim-tree.async"
56

67
local M = {}
78

@@ -15,26 +16,34 @@ local function err_fmt(from, to, reason)
1516
return string.format("Cannot rename %s -> %s: %s", from, to, reason)
1617
end
1718

18-
function M.rename(node, to)
19+
---@async
20+
function M.rename(node, to, use_async)
1921
if utils.file_exists(to) then
20-
notify.warn(err_fmt(node.absolute_path, to, "file already exists"))
21-
return
22+
return notify.warn(err_fmt(node.absolute_path, to, "file already exists"))
2223
end
2324

2425
events._dispatch_will_rename_node(node.absolute_path, to)
25-
local success, err = vim.loop.fs_rename(node.absolute_path, to)
26+
local success, err
27+
if use_async then
28+
err, success = async.call(vim.loop.fs_rename, node.absolute_path, to)
29+
else
30+
success, err = vim.loop.fs_rename(node.absolute_path, to)
31+
end
2632
if not success then
2733
return notify.warn(err_fmt(node.absolute_path, to, err))
2834
end
2935
notify.info(node.absolute_path .. "" .. to)
36+
if use_async then
37+
async.schedule()
38+
end
3039
utils.rename_loaded_buffers(node.absolute_path, to)
3140
events._dispatch_node_renamed(node.absolute_path, to)
3241
end
3342

3443
function M.fn(default_modifier)
3544
default_modifier = default_modifier or ":t"
3645

37-
return function(node, modifier)
46+
return function(node, modifier, cb)
3847
if type(node) ~= "table" then
3948
node = lib.get_node_at_cursor()
4049
end
@@ -76,17 +85,31 @@ function M.fn(default_modifier)
7685
if not new_file_path then
7786
return
7887
end
79-
80-
M.rename(node, prepend .. new_file_path .. append)
81-
if M.enable_reload then
82-
require("nvim-tree.actions.reloaders.reloaders").reload_explorer()
88+
if M.enable_async then
89+
async.exec(M.rename, node, prepend .. new_file_path .. append, true, function(err)
90+
if err then
91+
notify.error(err)
92+
end
93+
if M.enable_reload then
94+
require("nvim-tree.actions.reloaders.reloaders").reload_explorer()
95+
end
96+
if cb then
97+
cb(err)
98+
end
99+
end)
100+
else
101+
M.rename(node, prepend .. new_file_path .. append, false)
102+
if M.enable_reload then
103+
require("nvim-tree.actions.reloaders.reloaders").reload_explorer()
104+
end
83105
end
84106
end)
85107
end
86108
end
87109

88110
function M.setup(opts)
89111
M.enable_reload = not opts.filesystem_watchers.enable
112+
M.enable_async = opts.experimental.async.rename_file
90113
end
91114

92115
return M

0 commit comments

Comments
 (0)