Skip to content

Commit 8f929a6

Browse files
committed
fix: avoid recursion when copying folder to itself
1 parent 563d9f6 commit 8f929a6

File tree

2 files changed

+54
-7
lines changed

2 files changed

+54
-7
lines changed

lua/plenary/path.lua

+11-7
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ function Path:copy(opts)
548548
end
549549
-- dir
550550
if opts.recursive then
551+
local suffix = table.remove(self:_split())
551552
dest:mkdir {
552553
parents = F.if_nil(opts.parents, false, opts.parents),
553554
exists_ok = F.if_nil(opts.exists_ok, true, opts.exists_ok),
@@ -561,13 +562,16 @@ function Path:copy(opts)
561562
})
562563
for _, entry in ipairs(data) do
563564
local entry_path = Path:new(entry)
564-
local suffix = table.remove(entry_path:_split())
565-
local new_dest = dest:joinpath(suffix)
566-
-- clear destination as it might be Path table otherwise failing w/ extend
567-
opts.destination = nil
568-
local new_opts = vim.tbl_deep_extend("force", opts, { destination = new_dest })
569-
-- nil: not overriden if `override = false`
570-
success[new_dest] = entry_path:copy(new_opts) or false
565+
local entry_suffix = table.remove(entry_path:_split())
566+
-- avoid repeated recursive copying if folder is copied into itself
567+
if suffix ~= entry_suffix then
568+
local new_dest = dest:joinpath(entry_suffix)
569+
-- clear destination as it might be Path table otherwise failing w/ extend
570+
opts.destination = nil
571+
local new_opts = vim.tbl_deep_extend("force", opts, { destination = new_dest })
572+
-- nil: not overriden if `override = false`
573+
success[new_dest] = entry_path:copy(new_opts) or false
574+
end
571575
end
572576
return success
573577
else

tests/plenary/path_spec.lua

+43
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,49 @@ describe("Path", function()
478478
src_dir:rm { recursive = true }
479479
end)
480480

481+
it("self copies folders only once", function()
482+
local scan = require "plenary.scandir"
483+
-- create nested folder
484+
local src_dir = Path:new "src"
485+
src_dir:mkdir()
486+
src_dir:joinpath("file1.lua"):touch()
487+
local src_sub_dir = src_dir:joinpath("sub_dir")
488+
src_sub_dir:mkdir()
489+
src_sub_dir:joinpath("file2.lua"):touch()
490+
-- src
491+
-- ├── file1.lua
492+
-- └── sub_dir
493+
-- └── file2_2.lua
494+
495+
local trg_dir = src_dir:joinpath("src")
496+
src_dir:copy { destination = trg_dir, recursive = true }
497+
-- src/
498+
-- ├── file1.lua
499+
-- ├── src
500+
-- │   ├── file1.lua
501+
-- │   └── sub_dir
502+
-- │   └── file2.lua
503+
-- └── sub_dir
504+
-- └── file2.lua
505+
local src_dir_data = scan.scan_dir(src_dir:absolute(), { depth = 1, add_dirs = true})
506+
local trg_dir_data = scan.scan_dir(trg_dir:absolute(), { depth = 1, add_dirs = true})
507+
table.insert(trg_dir_data, trg_dir:absolute())
508+
-- check that equal number of items
509+
assert(#src_dir_data == #trg_dir_data)
510+
-- insert file/folder stems into set
511+
local path_set = {}
512+
-- get suffixes of absolute paths in respective folders and check match by creating set
513+
for _, value in ipairs(src_dir_data) do
514+
value = table.remove(Path:new(value):_split())
515+
path_set[value] = true
516+
end
517+
for _, value in ipairs(trg_dir_data) do
518+
value = table.remove(Path:new(value):_split())
519+
assert(path_set[value])
520+
end
521+
src_dir:rm { recursive = true }
522+
end)
523+
481524
it("can copy directories recursively", function()
482525
-- vim.tbl_flatten doesn't work here as copy doesn't return a list
483526
local flatten

0 commit comments

Comments
 (0)