mirror of
https://github.com/folke/snacks.nvim
synced 2025-08-04 18:58:12 +00:00
feat(explorer): recursive copy and copying of selected items with c
This commit is contained in:
parent
5c5b40b5d0
commit
2528fcb02c
3 changed files with 107 additions and 39 deletions
|
@ -413,8 +413,9 @@ function M:select_key(item)
|
|||
return item._select_key
|
||||
end
|
||||
|
||||
---@param items snacks.picker.Item[]
|
||||
---@param items? snacks.picker.Item[]
|
||||
function M:set_selected(items)
|
||||
items = items or {}
|
||||
self.selected = items
|
||||
self.selected_map = {}
|
||||
for _, item in ipairs(items) do
|
||||
|
|
|
@ -407,6 +407,16 @@ function M.setup(opts)
|
|||
})
|
||||
end
|
||||
|
||||
---@param prompt string
|
||||
---@param fn fun()
|
||||
function M.confirm(prompt, fn)
|
||||
Snacks.picker.select({ "Yes", "No" }, { prompt = prompt }, function(_, idx)
|
||||
if idx == 1 then
|
||||
fn()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
---@type table<string, snacks.picker.Action.spec>
|
||||
M.actions = {
|
||||
explorer_update = function(picker)
|
||||
|
@ -499,57 +509,58 @@ M.actions = {
|
|||
---@type string[]
|
||||
local paths = vim.tbl_map(Snacks.picker.util.path, picker:selected())
|
||||
if #paths == 0 then
|
||||
Snacks.notify.warn("No files selected to move")
|
||||
return
|
||||
Snacks.notify.warn("No files selected to move. Renaming instead.")
|
||||
return M.actions.explorer_rename(picker, picker:current())
|
||||
end
|
||||
local target = state:dir()
|
||||
local what = #paths == 1 and vim.fn.fnamemodify(paths[1], ":p:~:.") or #paths .. " files"
|
||||
local t = vim.fn.fnamemodify(target, ":p:~:.")
|
||||
|
||||
Snacks.picker.select({ "Yes", "No" }, { prompt = "Move " .. what .. " to " .. t .. "?" }, function(_, idx)
|
||||
if idx == 1 then
|
||||
for _, from in ipairs(paths) do
|
||||
local to = target .. "/" .. vim.fn.fnamemodify(from, ":t")
|
||||
Snacks.rename.on_rename_file(from, to, function()
|
||||
local ok, err = pcall(vim.fn.rename, from, to)
|
||||
if not ok then
|
||||
Snacks.notify.error("Failed to move `" .. from .. "`:\n- " .. err)
|
||||
end
|
||||
end)
|
||||
end
|
||||
state:update()
|
||||
M.confirm("Move " .. what .. " to " .. t .. "?", function()
|
||||
for _, from in ipairs(paths) do
|
||||
local to = target .. "/" .. vim.fn.fnamemodify(from, ":t")
|
||||
Snacks.rename.on_rename_file(from, to, function()
|
||||
local ok, err = pcall(vim.fn.rename, from, to)
|
||||
if not ok then
|
||||
Snacks.notify.error("Failed to move `" .. from .. "`:\n- " .. err)
|
||||
end
|
||||
end)
|
||||
end
|
||||
picker.list:set_selected() -- clear selection
|
||||
state:update()
|
||||
end)
|
||||
end,
|
||||
explorer_copy = function(picker, item)
|
||||
if not item then
|
||||
return
|
||||
end
|
||||
if item.dir then
|
||||
Snacks.notify.warn("Cannot copy directories")
|
||||
local state = M.get_state(picker)
|
||||
---@type string[]
|
||||
local paths = vim.tbl_map(Snacks.picker.util.path, picker:selected())
|
||||
-- Copy selection
|
||||
if #paths > 0 then
|
||||
local dir = state:dir()
|
||||
Snacks.picker.util.copy(paths, dir)
|
||||
state:open(dir)
|
||||
picker.list:set_selected() -- clear selection
|
||||
state:update()
|
||||
return
|
||||
end
|
||||
local state = M.get_state(picker)
|
||||
Snacks.input({
|
||||
prompt = "Copy to",
|
||||
}, function(value)
|
||||
if not value or value:find("^%s$") then
|
||||
return
|
||||
end
|
||||
local dir = state:dir()
|
||||
local path = vim.fs.normalize(dir .. "/" .. value)
|
||||
vim.fn.mkdir(vim.fs.dirname(path), "p")
|
||||
state:open(dir)
|
||||
if uv.fs_stat(path) then
|
||||
Snacks.notify.warn("File already exists:\n- `" .. path .. "`")
|
||||
local dir = vim.fs.dirname(item.file)
|
||||
local to = vim.fs.normalize(dir .. "/" .. value)
|
||||
if uv.fs_stat(to) then
|
||||
Snacks.notify.warn("File already exists:\n- `" .. to .. "`")
|
||||
return
|
||||
end
|
||||
uv.fs_copyfile(item.file, path, function(err)
|
||||
if err then
|
||||
Snacks.notify.error("Failed to copy `" .. item.file .. "` to `" .. path .. "`:\n- " .. err)
|
||||
end
|
||||
state:update()
|
||||
end)
|
||||
Snacks.picker.util.copy_path(item.file, to)
|
||||
state:open(dir)
|
||||
state:update()
|
||||
end)
|
||||
end,
|
||||
explorer_del = function(picker)
|
||||
|
@ -560,16 +571,14 @@ M.actions = {
|
|||
return
|
||||
end
|
||||
local what = #paths == 1 and vim.fn.fnamemodify(paths[1], ":p:~:.") or #paths .. " files"
|
||||
Snacks.picker.select({ "Yes", "No" }, { prompt = "Delete " .. what .. "?" }, function(_, idx)
|
||||
if idx == 1 then
|
||||
for _, path in ipairs(paths) do
|
||||
local ok, err = pcall(vim.fn.delete, path, "rf")
|
||||
if not ok then
|
||||
Snacks.notify.error("Failed to delete `" .. path .. "`:\n- " .. err)
|
||||
end
|
||||
M.confirm("Delete " .. what .. "?", function(_, idx)
|
||||
for _, path in ipairs(paths) do
|
||||
local ok, err = pcall(vim.fn.delete, path, "rf")
|
||||
if not ok then
|
||||
Snacks.notify.error("Failed to delete `" .. path .. "`:\n- " .. err)
|
||||
end
|
||||
state:update()
|
||||
end
|
||||
state:update()
|
||||
end)
|
||||
end,
|
||||
explorer_focus = function(picker)
|
||||
|
@ -597,7 +606,6 @@ M.actions = {
|
|||
end,
|
||||
confirm = function(picker, item, action)
|
||||
local state = M.get_state(picker)
|
||||
local item = picker:current()
|
||||
if not item then
|
||||
return
|
||||
elseif item.dir then
|
||||
|
|
|
@ -444,4 +444,63 @@ function M.dir(item)
|
|||
return vim.fn.isdirectory(path) == 1 and path or vim.fs.dirname(path)
|
||||
end
|
||||
|
||||
---@param paths string[]
|
||||
---@param dir string
|
||||
function M.copy(paths, dir)
|
||||
dir = vim.fs.normalize(dir)
|
||||
paths = vim.tbl_map(vim.fs.normalize, paths) ---@type string[]
|
||||
for _, path in ipairs(paths) do
|
||||
local name = vim.fn.fnamemodify(path, ":t")
|
||||
local to = dir .. "/" .. name
|
||||
M.copy_path(path, to)
|
||||
end
|
||||
end
|
||||
|
||||
---@param from string
|
||||
---@param to string
|
||||
function M.copy_path(from, to)
|
||||
if not uv.fs_stat(from) then
|
||||
Snacks.notify.error(("File `%s` does not exist"):format(from))
|
||||
return
|
||||
end
|
||||
if vim.fn.isdirectory(from) == 1 then
|
||||
M.copy_dir(from, to)
|
||||
else
|
||||
M.copy_file(from, to)
|
||||
end
|
||||
end
|
||||
|
||||
---@param from string
|
||||
---@param to string
|
||||
function M.copy_file(from, to)
|
||||
if vim.fn.filereadable(from) == 0 then
|
||||
Snacks.notify.error(("File `%s` is not readable"):format(from))
|
||||
return
|
||||
end
|
||||
if uv.fs_stat(to) then
|
||||
Snacks.notify.error(("File `%s` already exists"):format(to))
|
||||
return
|
||||
end
|
||||
local dir = vim.fs.dirname(to)
|
||||
vim.fn.mkdir(dir, "p")
|
||||
local ok, err = uv.fs_copyfile(from, to, { excl = true, ficlone = true })
|
||||
if not ok then
|
||||
Snacks.notify.error(("Failed to copy file:\n - from: `%s`\n- to: `%s`\n%s"):format(from, to, err))
|
||||
end
|
||||
end
|
||||
|
||||
---@param from string
|
||||
---@param to string
|
||||
function M.copy_dir(from, to)
|
||||
if vim.fn.isdirectory(from) == 0 then
|
||||
Snacks.notify.error(("Directory `%s` does not exist"):format(from))
|
||||
return
|
||||
end
|
||||
vim.fn.mkdir(to, "p")
|
||||
for fname in vim.fs.dir(from, { follow = false }) do
|
||||
local path = from .. "/" .. fname
|
||||
M.copy_path(path, to .. "/" .. fname)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue