@@ -429,11 +429,13 @@ function Path:mkdir(opts)
429429 local parents = F .if_nil (opts .parents , false , opts .parents )
430430 local exists_ok = F .if_nil (opts .exists_ok , true , opts .exists_ok )
431431
432- if not exists_ok and self :exists () then
432+ local exists = self :exists ()
433+ if not exists_ok and exists then
433434 error (" FileExistsError:" .. self :absolute ())
434435 end
435436
436- if not uv .fs_mkdir (self :_fs_filename (), mode ) then
437+ -- fs_mkdir returns nil if folder exists
438+ if not uv .fs_mkdir (self :_fs_filename (), mode ) and not exists then
437439 if parents then
438440 local dirs = self :_split ()
439441 local processed = " "
@@ -500,20 +502,77 @@ function Path:rename(opts)
500502 return status
501503end
502504
505+ --- Copy files or folders with defaults akin to GNU's `cp`.
506+ --- @param opts table : options to pass to toggling registered actions
507+ --- @field destination string | Path : target file path to copy to
508+ --- @field recursive bool : whether to copy folders recursively (default : false )
509+ --- @field override bool : whether to override files (default : true )
510+ --- @field interactive bool : confirm if copy would override ; precedes ` override` (default : false )
511+ --- @field respect_gitignore bool : skip folders ignored by all detected ` gitignore` s (default : false )
512+ --- @field hidden bool : whether to add hidden files in recursively copying folders (default : true )
513+ --- @field parents bool : whether to create possibly non-existing parent dirs of ` opts.destination` (default : false )
514+ --- @field exists_ok bool : whether ok if ` opts.destination` exists , if so folders are merged (default : true )
515+ --- @return table {[Path of destination] : bool } indicating success of copy ; nested tables constitute sub dirs
503516function Path :copy (opts )
504517 opts = opts or {}
518+ opts .recursive = F .if_nil (opts .recursive , false , opts .recursive )
519+ opts .override = F .if_nil (opts .override , true , opts .override )
505520
521+ local dest = opts .destination
506522 -- handles `.`, `..`, `./`, and `../`
507- if opts .destination :match " ^%.%.?/?\\ ?.+" then
508- opts .destination = {
509- uv .fs_realpath (opts .destination :sub (1 , 3 )),
510- opts .destination :sub (4 , # opts .destination ),
523+ if not Path .is_path (dest ) then
524+ if type (dest ) == " string" and dest :match " ^%.%.?/?\\ ?.+" then
525+ dest = {
526+ uv .fs_realpath (dest :sub (1 , 3 )),
527+ dest :sub (4 , # dest ),
528+ }
529+ end
530+ dest = Path :new (dest )
531+ end
532+ -- success is true in case file is copied, false otherwise
533+ local success = {}
534+ if not self :is_dir () then
535+ if opts .interactive and dest :exists () then
536+ vim .ui .select (
537+ { " Yes" , " No" },
538+ { prompt = string.format (" Overwrite existing %s?" , dest :absolute ()) },
539+ function (_ , idx )
540+ success [dest ] = uv .fs_copyfile (self :absolute (), dest :absolute (), { excl = not (idx == 1 ) }) or false
541+ end
542+ )
543+ else
544+ -- nil: not overriden if `override = false`
545+ success [dest ] = uv .fs_copyfile (self :absolute (), dest :absolute (), { excl = not opts .override }) or false
546+ end
547+ return success
548+ end
549+ -- dir
550+ if opts .recursive then
551+ dest :mkdir {
552+ parents = F .if_nil (opts .parents , false , opts .parents ),
553+ exists_ok = F .if_nil (opts .exists_ok , true , opts .exists_ok ),
511554 }
555+ local scan = require " plenary.scandir"
556+ local data = scan .scan_dir (self .filename , {
557+ respect_gitignore = F .if_nil (opts .respect_gitignore , false , opts .respect_gitignore ),
558+ hidden = F .if_nil (opts .hidden , true , opts .hidden ),
559+ depth = 1 ,
560+ add_dirs = true ,
561+ })
562+ for _ , entry in ipairs (data ) do
563+ 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
571+ end
572+ return success
573+ else
574+ error (string.format (" Warning: %s was not copied as `recursive=false`" , self :absolute ()))
512575 end
513-
514- local dest = Path :new (opts .destination )
515-
516- return uv .fs_copyfile (self :absolute (), dest :absolute (), { excl = true })
517576end
518577
519578function Path :touch (opts )
0 commit comments