diff --git a/lua/plenary/path.lua b/lua/plenary/path.lua index a8152f17..44d8e7d7 100644 --- a/lua/plenary/path.lua +++ b/lua/plenary/path.lua @@ -548,6 +548,7 @@ function Path:copy(opts) end -- dir if opts.recursive then + local suffix = table.remove(self:_split()) dest:mkdir { parents = F.if_nil(opts.parents, false, opts.parents), exists_ok = F.if_nil(opts.exists_ok, true, opts.exists_ok), @@ -561,13 +562,16 @@ function Path:copy(opts) }) for _, entry in ipairs(data) do local entry_path = Path:new(entry) - local suffix = table.remove(entry_path:_split()) - local new_dest = dest:joinpath(suffix) - -- clear destination as it might be Path table otherwise failing w/ extend - opts.destination = nil - local new_opts = vim.tbl_deep_extend("force", opts, { destination = new_dest }) - -- nil: not overriden if `override = false` - success[new_dest] = entry_path:copy(new_opts) or false + local entry_suffix = table.remove(entry_path:_split()) + -- avoid repeated recursive copying if folder is copied into itself + if suffix ~= entry_suffix then + local new_dest = dest:joinpath(entry_suffix) + -- clear destination as it might be Path table otherwise failing w/ extend + opts.destination = nil + local new_opts = vim.tbl_deep_extend("force", opts, { destination = new_dest }) + -- nil: not overriden if `override = false` + success[new_dest] = entry_path:copy(new_opts) or false + end end return success else diff --git a/tests/plenary/path_spec.lua b/tests/plenary/path_spec.lua index be9cae1c..34aeb689 100644 --- a/tests/plenary/path_spec.lua +++ b/tests/plenary/path_spec.lua @@ -478,6 +478,49 @@ describe("Path", function() src_dir:rm { recursive = true } end) + it("self copies folders only once", function() + local scan = require "plenary.scandir" + -- create nested folder + local src_dir = Path:new "src" + src_dir:mkdir() + src_dir:joinpath("file1.lua"):touch() + local src_sub_dir = src_dir:joinpath "sub_dir" + src_sub_dir:mkdir() + src_sub_dir:joinpath("file2.lua"):touch() + -- src + -- ├── file1.lua + -- └── sub_dir + -- └── file2_2.lua + + local trg_dir = src_dir:joinpath "src" + src_dir:copy { destination = trg_dir, recursive = true } + -- src/ + -- ├── file1.lua + -- ├── src + -- │   ├── file1.lua + -- │   └── sub_dir + -- │   └── file2.lua + -- └── sub_dir + -- └── file2.lua + local src_dir_data = scan.scan_dir(src_dir:absolute(), { depth = 1, add_dirs = true }) + local trg_dir_data = scan.scan_dir(trg_dir:absolute(), { depth = 1, add_dirs = true }) + table.insert(trg_dir_data, trg_dir:absolute()) + -- check that equal number of items + assert(#src_dir_data == #trg_dir_data) + -- insert file/folder stems into set + local path_set = {} + -- get suffixes of absolute paths in respective folders and check match by creating set + for _, value in ipairs(src_dir_data) do + value = table.remove(Path:new(value):_split()) + path_set[value] = true + end + for _, value in ipairs(trg_dir_data) do + value = table.remove(Path:new(value):_split()) + assert(path_set[value]) + end + src_dir:rm { recursive = true } + end) + it("can copy directories recursively", function() -- vim.tbl_flatten doesn't work here as copy doesn't return a list local flatten