runtime(netrw): Use right file system commands initialization for Windows

Commit: 
https://github.com/vim/vim/commit/a2d87ba615f15956116f43f7c70f1b315d679cb4
Author: Miguel Barro <[email protected]>
Date:   Sun Feb 15 15:50:24 2026 +0000

    runtime(netrw): Use right file system commands initialization for Windows
    
    closes: https://github.com/vim/vim/issues/19287
    fixes:  https://github.com/vim/vim/issues/12290
    
    Signed-off-by: Miguel Barro <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim 
b/runtime/pack/dist/opt/netrw/autoload/netrw.vim
index 7ec29a2c3..4f0d7b3b5 100644
--- a/runtime/pack/dist/opt/netrw/autoload/netrw.vim
+++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim
@@ -18,6 +18,7 @@
 " 2025 Nov 28 by Vim Project fix undefined variable in *NetrwMenu #18829
 " 2025 Dec 26 by Vim Project fix use of g:netrw_cygwin #19015
 " 2026 Jan 19 by Vim Project do not create swapfiles #18854
+" 2026 Feb 15 by Vim Project fix global variable initialization for MS-Windows 
#19287
 " Copyright:  Copyright (C) 2016 Charles E. Campbell {{{1
 "             Permission is hereby granted to use and distribute this code,
 "             with or without modifications, provided that this copyright
@@ -265,8 +266,8 @@ if !exists("g:netrw_localcopycmd")
     let g:netrw_localcopycmdopt = ''
 
     if has("win32") && !g:netrw_cygwin
-        let g:netrw_localcopycmd   = expand("$COMSPEC", v:true)
-        let g:netrw_localcopycmdopt = '/c copy'
+        let g:netrw_localcopycmd = $COMSPEC
+        let g:netrw_localcopycmdopt = ' /c copy'
     endif
 endif
 
@@ -275,30 +276,31 @@ if !exists("g:netrw_localcopydircmd")
     let g:netrw_localcopydircmdopt = '-R'
 
     if has("win32") && !g:netrw_cygwin
-        let g:netrw_localcopydircmd   = "cp"
-        call s:NetrwInit("g:netrw_localcopydircmdopt", "-R")
+        let g:netrw_localcopydircmd = "xcopy"
+        let g:netrw_localcopydircmdopt = " /E /I /H /C /Y"
     endif
 endif
 
-if has("win32")
-  if g:netrw_cygwin
-    call s:NetrwInit("g:netrw_localmkdir","mkdir")
+if !exists("g:netrw_localmkdir")
+  if has("win32")
+    if g:netrw_cygwin
+      let g:netrw_localmkdir= "mkdir"
+    else
+      let g:netrw_localmkdir = $COMSPEC
+      let g:netrw_localmkdiropt= " /c mkdir"
+    endif
   else
-    call s:NetrwInit("g:netrw_localmkdir",expand("$COMSPEC", v:true))
-    call s:NetrwInit("g:netrw_localmkdiropt"," /c mkdir")
+    let g:netrw_localmkdir= "mkdir"
   endif
-else
-  call s:NetrwInit("g:netrw_localmkdir","mkdir")
 endif
-call s:NetrwInit("g:netrw_remote_mkdir","mkdir")
 
 if !exists("g:netrw_localmovecmd")
   if has("win32")
     if g:netrw_cygwin
       let g:netrw_localmovecmd= "mv"
     else
-      let g:netrw_localmovecmd   = expand("$COMSPEC", v:true)
-      call s:NetrwInit("g:netrw_localmovecmdopt"," /c move")
+      let g:netrw_localmovecmd = $COMSPEC
+      let g:netrw_localmovecmdopt= " /c move"
     endif
   elseif has("unix") || has("macunix")
     let g:netrw_localmovecmd= "mv"
@@ -980,7 +982,7 @@ function netrw#Obtain(islocal,fname,...)
     " obtain a file from local b:netrw_curdir to (local) tgtdir
     if exists("b:netrw_curdir") && getcwd() != b:netrw_curdir
       let topath = netrw#fs#ComposePath(tgtdir,"")
-      if has("win32")
+      if has("win32") && !g:netrw_cygwin
         " transfer files one at time
         for fname in fnamelist
           call system(g:netrw_localcopycmd.g:netrw_localcopycmdopt." 
".netrw#os#Escape(fname)." ".netrw#os#Escape(topath))
@@ -4731,38 +4733,16 @@ function s:NetrwMakeDir(usrhost)
 
         " requested new local directory is neither a pre-existing file or
         " directory, so make it!
-        if exists("*mkdir")
-            if has("unix")
-                call mkdir(fullnewdir,"p",xor(0777, system("umask")))
-            else
-                call mkdir(fullnewdir,"p")
-            endif
+        if has("unix")
+            call mkdir(fullnewdir,"p",xor(0777, system("umask")))
         else
-            let netrw_origdir= netrw#fs#Cwd(1)
-            if s:NetrwLcd(b:netrw_curdir)
-                return
-            endif
-            call netrw#os#Execute("sil! 
!".g:netrw_localmkdir.g:netrw_localmkdiropt.' '.netrw#os#Escape(newdirname,1))
-            if v:shell_error != 0
-                let @@= ykeep
-                call netrw#msg#Notify('ERROR', printf('consider setting 
g:netrw_localmkdir<%s> to something that works', g:netrw_localmkdir))
-                return
-            endif
-            if !g:netrw_keepdir
-                if s:NetrwLcd(netrw_origdir)
-                    return
-                endif
-            endif
+            call mkdir(fullnewdir,"p")
         endif
 
-        if v:shell_error == 0
-            " refresh listing
-            let svpos= winsaveview()
-            call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
-            call winrestview(svpos)
-        else
-            call netrw#msg#Notify('ERROR', printf('unable to make 
directory<%s>', newdirname))
-        endif
+        " on success refresh listing
+        let svpos= winsaveview()
+        call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
+        call winrestview(svpos)
 
     elseif !exists("b:netrw_method") || b:netrw_method == 4
         " Remote mkdir:  using ssh
@@ -5346,17 +5326,14 @@ function s:NetrwMarkFileCopy(islocal,...)
 
     let curdir   = s:NetrwGetCurdir(a:islocal)
     let curbufnr = bufnr("%")
-    if b:netrw_curdir !~ '/$'
-        if !exists("b:netrw_curdir")
-            let b:netrw_curdir= curdir
-        endif
-        let b:netrw_curdir= b:netrw_curdir."/"
+    if !exists("b:netrw_curdir")
+        let b:netrw_curdir= curdir
     endif
 
     " sanity check
     if !exists("s:netrwmarkfilelist_{curbufnr}") || 
empty(s:netrwmarkfilelist_{curbufnr})
         call netrw#msg#Notify('ERROR', 'there are no marked files in this 
window (:help netrw-mf)')
-        return
+        return 0
     endif
 
     if !exists("s:netrwmftgt")
@@ -5364,7 +5341,7 @@ function s:NetrwMarkFileCopy(islocal,...)
         return 0
     endif
 
-    if a:islocal &&  s:netrwmftgt_islocal
+    if a:islocal && s:netrwmftgt_islocal
         " Copy marked files, local directory to local directory
         if !executable(g:netrw_localcopycmd)
             call netrw#msg#Notify('ERROR', printf('g:netrw_localcopycmd<%s> 
not executable on your system, aborting', g:netrw_localcopycmd))
@@ -5372,69 +5349,85 @@ function s:NetrwMarkFileCopy(islocal,...)
         endif
 
         " copy marked files while within the same directory (ie. allow 
renaming)
-        if simplify(s:netrwmftgt) ==# simplify(b:netrw_curdir)
-            if len(s:netrwmarkfilelist_{bufnr('%')}) == 1
-                " only one marked file
-                let args    = 
netrw#os#Escape(b:netrw_curdir.s:netrwmarkfilelist_{bufnr('%')}[0])
-                let oldname = s:netrwmarkfilelist_{bufnr('%')}[0]
-            elseif a:0 == 1
-                " this happens when the next case was used to recursively call 
s:NetrwMarkFileCopy()
-                let args    = netrw#os#Escape(b:netrw_curdir.a:1)
-                let oldname = a:1
-            else
-                " copy multiple marked files inside the same directory
-                let s:recursive= 1
-                for oldname in s:netrwmarkfilelist_{bufnr("%")}
-                    let ret= s:NetrwMarkFileCopy(a:islocal,oldname)
-                    if ret == 0
-                        break
-                    endif
-                endfor
-                unlet s:recursive
-                call s:NetrwUnmarkList(curbufnr,curdir)
-                return ret
-            endif
+        if simplify(s:netrwmftgt."/") ==# simplify(b:netrw_curdir."/")
+            " copy multiple marked files inside the same directory
+            for oldname in s:netrwmarkfilelist_{curbufnr}
+                call inputsave()
+                let newname= input(printf("Copy %s to: ", oldname), oldname, 
'file')
+                call inputrestore()
 
-            call inputsave()
-            let newname= input(printf("Copy %s to: ", oldname), oldname, 
'file')
-            call inputrestore()
+                if empty(newname)
+                    return 0
+                endif
 
-            if empty(newname)
-                return 0
-            endif
+                let tgt = netrw#fs#ComposePath(s:netrwmftgt, newname)
+                let oldname = netrw#fs#ComposePath(b:netrw_curdir, oldname)
+                if tgt ==# oldname
+                    continue
+                endif
 
-            let args = netrw#os#Escape(oldname)
-            let tgt = netrw#os#Escape(s:netrwmftgt.'/'.newname)
+                let ret = filecopy(oldname, tgt)
+                if ret == v:false
+                    call netrw#msg#Notify('ERROR', $'copy failed, unable to 
filecopy() <{oldname}> to <{tgt}>')
+                    break
+                endif
+            endfor
+            call s:NetrwUnmarkList(curbufnr,curdir)
+            NetrwKeepj call s:NetrwRefreshDir(a:islocal, b:netrw_curdir)
+            return ret
         else
-            let args = 
join(map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),"netrw#os#Escape(b:netrw_curdir.\"/\".v:val)"))
-            let tgt = netrw#os#Escape(s:netrwmftgt)
-        endif
-
-        if !g:netrw_cygwin && has("win32")
-            let args = substitute(args,'/','\','g')
-            let tgt = substitute(tgt, '/','\','g')
+            let args = []
+            for arg in s:netrwmarkfilelist_{curbufnr}
+                call add(args, netrw#fs#ComposePath(b:netrw_curdir, arg))
+            endfor
+            let tgt = s:netrwmftgt
         endif
 
-        if args =~ "'" |let args= substitute(args,"'\(.*\)'",' ','')|endif
-        if tgt  =~ "'" |let tgt = substitute(tgt ,"'\(.*\)'",' ','')|endif
-        if args =~ '//'|let args= substitute(args,'//','/','g')|endif
-        if tgt  =~ '//'|let tgt = substitute(tgt ,'//','/','g')|endif
-
         let copycmd = g:netrw_localcopycmd
         let copycmdopt = g:netrw_localcopycmdopt
 
-        if isdirectory(s:NetrwFile(args))
+        " on Windows, no builtin command supports copying multiple files at 
once
+        " (powershell's Copy-Item cmdlet does but requires , as file separator)
+        if len(s:netrwmarkfilelist_{curbufnr}) > 1 && has("win32") && 
!g:netrw_cygwin
+            " copy multiple marked files
+            for file in args
+                let dest = netrw#fs#ComposePath(tgt, fnamemodify(file, ':t'))
+                let ret = filecopy(file, dest)
+                if ret == v:false
+                    call netrw#msg#Notify('ERROR', $'copy failed, unable to 
filecopy() <{file}> to <{dest}>')
+                    break
+                endif
+            endfor
+            call s:NetrwUnmarkList(curbufnr,curdir)
+            NetrwKeepj call s:NetrwRefreshDir(a:islocal, b:netrw_curdir)
+            return ret
+        endif
+
+        if len(args) == 1 && isdirectory(s:NetrwFile(args[0]))
             let copycmd = g:netrw_localcopydircmd
             let copycmdopt = g:netrw_localcopydircmdopt
-            if has('win32') && !g:netrw_cygwin
+            if has('win32') && g:netrw_localcopydircmd == "xcopy"
                 " window's xcopy doesn't copy a directory to a target 
properly.  Instead, it copies a directory's
                 " contents to a target.  One must append the source directory 
name to the target to get xcopy to
                 " do the right thing.
-                let tgt= tgt.'\'.substitute(a:1,'^.*[\/]','','')
+                let tgt = netrw#fs#ComposePath(tgt, 
fnamemodify(simplify(netrw#fs#PathJoin(args[0],".")),":t"))
             endif
         endif
 
-        call system(printf("%s %s '%s' '%s'", copycmd, copycmdopt, args, tgt))
+        " prepare arguments for shell call
+        let args = join(map(args,'netrw#os#Escape(v:val)'))
+        let tgt = netrw#os#Escape(tgt)
+
+        " enforce noshellslash for system calls
+        if exists('+shellslash') && &shellslash
+            for var in ['copycmd', 'args', 'tgt']
+                let {var} = substitute({var}, '/', '\', 'g')
+            endfor
+        endif
+
+        " shell call
+        let shell_cmd = printf("%s %s %s %s", copycmd, copycmdopt, args, tgt)
+        call system(shell_cmd)
         if v:shell_error != 0
             if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && 
g:netrw_keepdir
                 call netrw#msg#Notify('ERROR', printf("copy failed; perhaps 
due to vim's current directory<%s> not matching netrw's (%s) (see :help 
netrw-cd)", getcwd(), b:netrw_curdir))
@@ -5446,11 +5439,11 @@ function s:NetrwMarkFileCopy(islocal,...)
 
     elseif  a:islocal && !s:netrwmftgt_islocal
         " Copy marked files, local directory to remote directory
-        NetrwKeepj call 
s:NetrwUpload(s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt)
+        NetrwKeepj call 
s:NetrwUpload(s:netrwmarkfilelist_{curbufnr},s:netrwmftgt)
 
     elseif !a:islocal &&  s:netrwmftgt_islocal
         " Copy marked files, remote directory to local directory
-        NetrwKeepj call 
netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt)
+        NetrwKeepj call 
netrw#Obtain(a:islocal,s:netrwmarkfilelist_{curbufnr},s:netrwmftgt)
 
     elseif !a:islocal && !s:netrwmftgt_islocal
         " Copy marked files, remote directory to remote directory
@@ -5459,24 +5452,16 @@ function s:NetrwMarkFileCopy(islocal,...)
         if tmpdir !~ '/'
             let tmpdir= curdir."/".tmpdir
         endif
-        if exists("*mkdir")
-            call mkdir(tmpdir)
-        else
-            call netrw#os#Execute("sil! 
!".g:netrw_localmkdir.g:netrw_localmkdiropt.' '.netrw#os#Escape(tmpdir,1))
-            if v:shell_error != 0
-                call netrw#msg#Notify('WARNING', printf("consider setting 
g:netrw_localmkdir<%s> to something that works", g:netrw_localmkdir))
-                return
-            endif
-        endif
+        call mkdir(tmpdir)
         if isdirectory(s:NetrwFile(tmpdir))
             if s:NetrwLcd(tmpdir)
                 return
             endif
-            NetrwKeepj call 
netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},tmpdir)
-            let localfiles= 
map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),'substitute(v:val,"^.*/","","")')
+            NetrwKeepj call 
netrw#Obtain(a:islocal,s:netrwmarkfilelist_{curbufnr},tmpdir)
+            let localfiles= 
map(deepcopy(s:netrwmarkfilelist_{curbufnr}),'substitute(v:val,"^.*/","","")')
             NetrwKeepj call s:NetrwUpload(localfiles,s:netrwmftgt)
             if getcwd() == tmpdir
-                for fname in s:netrwmarkfilelist_{bufnr('%')}
+                for fname in s:netrwmarkfilelist_{curbufnr}
                     call netrw#fs#Remove(fname)
                 endfor
                 if s:NetrwLcd(curdir)
@@ -5498,18 +5483,13 @@ function s:NetrwMarkFileCopy(islocal,...)
     " -------
     " remove markings from local buffer
     call s:NetrwUnmarkList(curbufnr,curdir)                   " remove 
markings from local buffer
-    if exists("s:recursive")
-    else
-    endif
     " see s:LocalFastBrowser() for g:netrw_fastbrowse interpretation 
(refreshing done for both slow and medium)
     if g:netrw_fastbrowse <= 1
         NetrwKeepj call s:LocalBrowseRefresh()
     else
         " refresh local and targets for fast browsing
-        if !exists("s:recursive")
-            " remove markings from local buffer
-            NetrwKeepj call s:NetrwUnmarkList(curbufnr,curdir)
-        endif
+        " remove markings from local buffer
+        NetrwKeepj call s:NetrwUnmarkList(curbufnr,curdir)
 
         " refresh buffers
         if s:netrwmftgt_islocal
@@ -5878,28 +5858,35 @@ function s:NetrwMarkFileMove(islocal)
             call netrw#msg#Notify('ERROR', printf('g:netrw_localmovecmd<%s> 
not executable on your system, aborting', g:netrw_localmovecmd))
             return
         endif
+
         let tgt = netrw#os#Escape(s:netrwmftgt)
-        if !g:netrw_cygwin && has("win32")
-            let tgt= substitute(tgt, '/','\','g')
-            if g:netrw_localmovecmd =~ '\s'
-                let movecmd     = 
substitute(g:netrw_localmovecmd,'\s.*$','','')
-                let movecmdargs = 
substitute(g:netrw_localmovecmd,'^.\{-}\(\s.*\)$',' ','')
-                let movecmd     = netrw#fs#WinPath(movecmd).movecmdargs
-            else
-                let movecmd = netrw#fs#WinPath(g:netrw_localmovecmd)
-            endif
+        if has("win32") && !g:netrw_cygwin && g:netrw_localmovecmd =~ '\s' && 
g:netrw_localmovecmdopt == ""
+            let movecmd     = substitute(g:netrw_localmovecmd,'\s.*$','','')
+            let movecmdargs = 
substitute(g:netrw_localmovecmd,'^.\{-}\(\s.*\)$',' ','')
         else
-            let movecmd = netrw#fs#WinPath(g:netrw_localmovecmd)
+            let movecmd = g:netrw_localmovecmd
+            let movecmdargs = g:netrw_localmovecmdopt
         endif
-        for fname in s:netrwmarkfilelist_{bufnr("%")}
+
+        " build args list
+        let args = []
+        for fname in s:netrwmarkfilelist_{curbufnr}
             if g:netrw_keepdir
                 " Jul 19, 2022: fixing file move when g:netrw_keepdir is 1
-                let fname= b:netrw_curdir."/".fname
+                let fname= netrw#fs#ComposePath(b:netrw_curdir, fname)
             endif
-            if !g:netrw_cygwin && has("win32")
-                let fname= substitute(fname,'/','\','g')
-            endif
-            let ret= system(movecmd.g:netrw_localmovecmdopt." 
".netrw#os#Escape(fname)." ".tgt)
+            call add(args, netrw#os#Escape(fname))
+        endfor
+
+        " enforce noshellslash for system calls
+        if exists('+shellslash') && &shellslash
+            let tgt = substitute(tgt, '/', '\', 'g')
+            call map(args, "substitute(v:val, '/', '\', 'g')")
+        endif
+
+        for fname in args
+            let shell_cmd = printf("%s %s %s %s", movecmd, movecmdargs, fname, 
tgt)
+            let ret= system(shell_cmd)
             if v:shell_error != 0
                 if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && 
!g:netrw_keepdir
                     call netrw#msg#Notify('ERROR', printf("move failed; 
perhaps due to vim's current directory<%s> not matching netrw's (%s) (see :help 
netrw-cd)", getcwd(), b:netrw_curdir))
@@ -5912,7 +5899,7 @@ function s:NetrwMarkFileMove(islocal)
 
     elseif  a:islocal && !s:netrwmftgt_islocal
         " move: local -> remote
-        let mflist= s:netrwmarkfilelist_{bufnr("%")}
+        let mflist= s:netrwmarkfilelist_{curbufnr}
         NetrwKeepj call s:NetrwMarkFileCopy(a:islocal)
         for fname in mflist
             let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$',' ','')
@@ -5922,7 +5909,7 @@ function s:NetrwMarkFileMove(islocal)
 
     elseif !a:islocal &&  s:netrwmftgt_islocal
         " move: remote -> local
-        let mflist= s:netrwmarkfilelist_{bufnr("%")}
+        let mflist= s:netrwmarkfilelist_{curbufnr}
         NetrwKeepj call s:NetrwMarkFileCopy(a:islocal)
         for fname in mflist
             let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$',' ','')
@@ -5932,7 +5919,7 @@ function s:NetrwMarkFileMove(islocal)
 
     elseif !a:islocal && !s:netrwmftgt_islocal
         " move: remote -> remote
-        let mflist= s:netrwmarkfilelist_{bufnr("%")}
+        let mflist= s:netrwmarkfilelist_{curbufnr}
         NetrwKeepj call s:NetrwMarkFileCopy(a:islocal)
         for fname in mflist
             let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$',' ','')
diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw/fs.vim 
b/runtime/pack/dist/opt/netrw/autoload/netrw/fs.vim
index f2f98596a..8c002a88c 100644
--- a/runtime/pack/dist/opt/netrw/autoload/netrw/fs.vim
+++ b/runtime/pack/dist/opt/netrw/autoload/netrw/fs.vim
@@ -24,6 +24,7 @@ endfunction
 " netrw#fs#ComposePath: Appends a new part to a path taking different systems 
into consideration {{{
 
 function! netrw#fs#ComposePath(base, subdir)
+    const slash = !exists('+shellslash') || &shellslash ? '/' : '\'
     if has('amiga')
         let ec = a:base[strdisplaywidth(a:base)-1]
         if ec != '/' && ec != ':'
@@ -40,7 +41,7 @@ function! netrw#fs#ComposePath(base, subdir)
         if a:base =~ '[/\]$'
             let ret = a:base . a:subdir
         else
-            let ret = a:base . '/' . a:subdir
+            let ret = a:base . slash . a:subdir
         endif
 
     elseif a:base =~ '^ \{3,}://'
diff --git a/src/testdir/test_plugin_netrw.vim 
b/src/testdir/test_plugin_netrw.vim
index ab11c3da2..09109b471 100644
--- a/src/testdir/test_plugin_netrw.vim
+++ b/src/testdir/test_plugin_netrw.vim
@@ -1,32 +1,211 @@
-let s:netrw_path =        $VIMRUNTIME . 
'/pack/dist/opt/netrw/autoload/netrw.vim'
-let s:netrw_test_dir  =   'samples'
-let s:netrw_test_path =   s:netrw_test_dir . '/netrw.vim'
+const s:testdir = expand("<script>:h")
+const s:runtimedir = simplify(s:testdir . '/../../runtime')
+const s:netrw_path = s:runtimedir . '/pack/dist/opt/netrw/autoload/netrw.vim'
+const s:netrw_test_path = s:testdir . '/samples/netrw.vim'
+const s:testScript =<< trim END
+
+" Testing functions: {{{1
+function! TestNetrwCaptureRemotePath(dirname)
+  call s:RemotePathAnalysis(a:dirname)
+  return {"method": s:method, "user": s:user, "machine": s:machine, "port": 
s:port, "path": s:path, "fname": s:fname}
+endfunction
+
+" Test directory creation via s:NetrwMakeDir()
+" Precondition: inputsave() and inputrestore() must be disabled in 
s:NetrwMakeDir
+
+function s:test_inputsave()
+    if exists("s:inputguards_disabled") && s:inputguards_disabled
+        return
+    endif
+    call inputsave()
+endfunction
+
+function s:test_inputrestore()
+    if exists("s:inputguards_disabled") && s:inputguards_disabled
+        return
+    endif
+    call inputrestore()
+endfunction
+
+function s:test_input(prompt, text = v:none, completion = v:none)
+
+    if exists("s:inputdefaults_disabled") && s:inputdefaults_disabled || 
a:text == v:none
+        return input(a:prompt)
+    elseif a:completion == v:none
+        return input(a:prompt, a:text)
+    endif
+
+    return input(a:prompt, a:text, a:completion)
+endfunction
+
+function Test_NetrwMakeDir(parentdir = $HOME, dirname = "NetrwMakeDir", 
symlink = 0) abort
+    if a:symlink
+        " Plainly delegate, this device is necessary because feedkeys() can't
+        " access script functions directly.
+        call s:NetrwMakeDir('')
+        " wipe out the test buffer
+        bw
+        " reenable the guards
+        let s:inputguards_disabled = 0
+    else
+        " Use feedkeys() to simulate user input (directory name)
+        new
+        let b:netrw_curdir = a:parentdir
+        let s:inputguards_disabled = 1
+        call feedkeys($"\<Cmd>call Test_NetrwMakeDir('{a:parentdir}', 
'{a:dirname}', 1)\<CR>{a:dirname}\<CR>", "x")
+    endif
+endfunction
+
+" Test file copy operations via s:NetrwMarkFileCopy()
+function Test_NetrwMarkFileCopy(source_dir, target_dir, marked_files) abort
+    " set up
+    new
+    let b:netrw_curdir= a:source_dir
+    let s:netrwmftgt = a:target_dir
+    let s:netrwmarkfilelist_{bufnr("%")} = a:marked_files
+    let s:netrwmftgt_islocal = 1
+    " delegate
+    call s:NetrwMarkFileCopy(1)
+    " wipe out the test buffer
+    bw
+endfunction
+
+" Corner case: copy into the same dir triggers a user prompt
+function Test_NetrwMarkFileCopy_SameDir(dir = $HOME, symlink = 0) abort
+    const filename = "filename.txt"
+    const file = netrw#fs#PathJoin(a:dir, filename)
+
+    const newfilename = "newfilename.txt"
+    const newfile = netrw#fs#PathJoin(a:dir, newfilename)
+
+    if a:symlink
+        " Plainly delegate, this device is necessary because feedkeys() can't
+        " access script functions directly.
+        " set up
+        new
+        let b:netrw_curdir = a:dir
+        let s:netrwmftgt = a:dir
+        let s:netrwmarkfilelist_{bufnr("%")} = [filename]
+        let s:netrwmftgt_islocal = 1
+
+        " delegate
+        call s:NetrwMarkFileCopy(1)
+
+        " validate
+        call assert_equalfile(file, newfile, "File copy in same dir failed")
+
+        " tear down
+        call delete(file)
+        call delete(newfile)
+        " wipe out the test buffer
+        bw
+        " reenable the guards
+        let s:inputguards_disabled = 0
+        let s:inputdefaults_disabled = 0
+    else
+        " Use feedkeys() to simulate user input (directory name)
+        let s:inputguards_disabled = 1
+        let s:inputdefaults_disabled = 1
+
+        call writefile([$"NetrwMarkFileCopy test file"], file)
+
+        call feedkeys($"\<Cmd>call Test_NetrwMarkFileCopy_SameDir('{a:dir}', 
1)\<CR>{newfilename}\<CR>", "x")
+    endif
+endfunction
+
+
+" Test file copy operations via s:NetrwMarkFileMove()
+function Test_NetrwMarkFileMove(source_dir, target_dir, marked_files) abort
+    " set up
+    new
+    let b:netrw_curdir= a:source_dir
+    let s:netrwmftgt = a:target_dir
+    let s:netrwmarkfilelist_{bufnr("%")} = a:marked_files
+    let s:netrwmftgt_islocal = 1
+    " delegate
+    call s:NetrwMarkFileMove(1)
+    " wipe out the test buffer
+    bw
+endfunction
+
+" }}}
+END
 
 "make copy of netrw script and add function to print local variables"
 func s:appendDebugToNetrw(netrw_path, netrw_test_path)
-  let netrwScript = readfile(a:netrw_path)
 
-  let netrwScript += [
-       \ '
',
-       \ '"-- test helpers ---"',
-       \ 'function! TestNetrwCaptureRemotePath(dirname)',
-       \ '  call s:RemotePathAnalysis(a:dirname)',
-       \ '  return {"method": s:method, "user": s:user, "machine": s:machine, 
"port": s:port, "path": s:path, "fname": s:fname}',
-       \ 'endfunction'
-       \ ]
+  " load the netrw script
+  execute "split" a:netrw_test_path
+  execute "read" a:netrw_path
+
+  " replace input guards for convenient testing versions
+  %substitute@call inputsave()@call s:test_inputsave()@g
+  %substitute@call inputrestore()@call s:test_inputrestore()@g
+  %substitute@\<input(@s:test_input(@g
+
+  call cursor(1,1)
+  let pos = search("Settings Restoration:")-1
+  " insert the test functions before the end guard
+  call assert_false(append(pos, s:testScript))
+
+  " save the modified script content
+  write
+  bwipe!
 
-  call writefile(netrwScript, a:netrw_test_path)
-  execute 'source' a:netrw_test_path
 endfunction
 
-func s:setup()
+func SetUp()
+
+  " prepare modified netrw script
   call s:appendDebugToNetrw(s:netrw_path, s:netrw_test_path)
+
+  " source the modified script
+  exe "source" s:netrw_test_path
+
+  " Rig the package. The modified script guard prevents loading it again.
+  let &runtimepath=s:runtimedir
+  let &packpath=s:runtimedir
+  packadd netrw
+
+  " use proper path
+  if has('win32')
+    let $HOME = substitute($HOME, '/', '\', 'g')
+  endif
+
 endfunction
 
-func s:cleanup()
+func TearDown()
+  " cleanup
   call delete(s:netrw_test_path)
 endfunction
 
+func SetShell(shell)
+    " select different shells
+    if a:shell == "default"
+        set shell& shellcmdflag& shellxquote& shellpipe& shellredir&
+    elseif a:shell == "powershell" " help dos-powershell
+        " powershell desktop is windows only
+        if !has("win32")
+            throw 'Skipped: powershell desktop is missing'
+        endif
+        set shell=powershell shellcmdflag=-NoProfile\ -Command shellxquote=\"
+        set shellpipe=2>&1\ \|\ Out-File\ -Encoding\ default shellredir=2>&1\ 
\|\ Out-File\ -Encoding\ default
+    elseif a:shell == "pwsh" " help dos-powershell
+        " powershell core works crossplatform
+        if !executable("pwsh")
+            throw 'Skipped: powershell core is missing'
+        endif
+        set shell=pwsh shellcmdflag=-NoProfile\ -c shellpipe=>%s\ 2>&1 
shellredir=>%s\ 2>&1
+        if has("win32")
+            set shellxquote=\"
+        else
+            set shellxquote=
+        endif
+    else
+        call assert_report("Trying to select an unknown shell")
+    endif
+endfunc
+
 func s:combine
   \( usernames
   \, methods
@@ -62,19 +241,16 @@ endfunction
 
 
 func Test_netrw_parse_remote_simple()
-  call s:setup()
   let result = TestNetrwCaptureRemotePath('scp://user@localhost:2222/test.txt')
   call assert_equal(result.method, 'scp')
   call assert_equal(result.user, 'user')
   call assert_equal(result.machine, 'localhost')
   call assert_equal(result.port, '2222')
   call assert_equal(result.path, 'test.txt')
-  call s:cleanup()
 endfunction
 
 "testing different combinations"
 func Test_netrw_parse_regular_usernames()
-  call s:setup()
 
   " --- sample data for combinations ---"
   let usernames = ["root", "toor", "user01", "skillIssue"]
@@ -86,47 +262,273 @@ func Test_netrw_parse_regular_usernames()
 
   call s:combine(usernames, methods, hosts, ports, dirs, files)
 
-  call s:cleanup()
 endfunc
 
 "Host myserver
 "    HostName 192.168.1.42
 "    User alice
 func Test_netrw_parse_ssh_config_entries()
-  call s:setup()
   let result = 
TestNetrwCaptureRemotePath('scp://myserver//etc/nginx/nginx.conf')
   call assert_equal(result.method, 'scp')
   call assert_equal(result.user, '')
   call assert_equal(result.machine, 'myserver')
   call assert_equal(result.port, '')
   call assert_equal(result.path, '/etc/nginx/nginx.conf')
-  call s:cleanup()
 endfunction
 
 "username containing special-chars"
 func Test_netrw_parse_special_char_user()
-  call s:setup()
   let result = 
TestNetrwCaptureRemotePath('scp://user-01@localhost:2222/test.txt')
   call assert_equal(result.method, 'scp')
   call assert_equal(result.user, 'user-01')
   call assert_equal(result.machine, 'localhost')
   call assert_equal(result.port, '2222')
   call assert_equal(result.path, 'test.txt')
-  call s:cleanup()
 endfunction
 
 func Test_netrw_wipe_empty_buffer_fastpath()
+  " SetUp() may have opened some buffers
+  let previous = bufnr('$')
   let g:netrw_fastbrowse=0
-  packadd netrw
   call setline(1, 'foobar')
   let  bufnr = bufnr('%')
   tabnew
   Explore
   call search('README.txt', 'W')
   exe ":norm \<cr>"
-  call assert_equal(4, bufnr('$'))
+  call assert_equal(previous + 2, bufnr('$'))
   call assert_true(bufexists(bufnr))
   bw
 
   unlet! netrw_fastbrowse
 endfunction
+
+" ---------------------------------
+" Testing file management functions
+" ---------------------------------
+
+" Browser directory creation
+func s:netrw_mkdir()
+
+  " create a testdir in the fake $HOME
+  call Test_NetrwMakeDir($HOME, "NetrwMakeDir")
+
+  " Check the test directory was created
+  let test_dir = netrw#fs#PathJoin($HOME, "NetrwMakeDir")
+  call WaitForAssert({-> assert_true(
+  \     isdirectory(test_dir),
+  \     "Unable to create a dir via s:NetrwMakeDir()")
+  \ })
+
+  " remove the test directory
+  call delete(test_dir, 'd')
+endfunc
+
+func Test_netrw_mkdir_default()
+  call SetShell('default')
+  call s:netrw_mkdir()
+endfunc
+
+func Test_netrw_mkdir_powershell()
+  call SetShell('powershell')
+  call s:netrw_mkdir()
+endfunc
+
+func Test_netrw_mkdir_pwsh()
+  call SetShell('pwsh')
+  call s:netrw_mkdir()
+endfunc
+
+func s:netrw_filecopy(count = 1)
+  " setup
+  let marked_files = []
+  let source_dir = netrw#fs#PathJoin($HOME, "src")
+  let target_dir = netrw#fs#PathJoin($HOME, "target")
+
+  call mkdir(source_dir, "R")
+  call mkdir(target_dir, "R")
+
+  for i in range(a:count)
+    call add(marked_files, $"testfile{i}.txt")
+    call writefile(
+    \   [$"NetrwMarkFileCopy test file {i}"],
+    \   netrw#fs#PathJoin(source_dir, marked_files[-1]))
+  endfor
+
+  " delegate
+  call Test_NetrwMarkFileCopy(source_dir, target_dir, marked_files)
+
+  " verify
+  for file in marked_files
+    call assert_equalfile(
+    \   netrw#fs#PathJoin(source_dir, file),
+    \   netrw#fs#PathJoin(target_dir, file),
+    \   "File copy failed for " . file)
+  endfor
+endfunc
+
+" Browser file copy
+func s:test_netrw_filecopy()
+
+  " if shellslash is available, check both settings
+  if exists('+shellslash')
+    set shellslash&
+    call s:netrw_filecopy(1)
+    call s:netrw_filecopy(10)
+    set shellslash!
+  endif
+
+  call s:netrw_filecopy(1)
+  call s:netrw_filecopy(10)
+
+endfunc
+
+func Test_netrw_filecopy_default()
+  call SetShell('default')
+  call s:test_netrw_filecopy()
+endfunc
+
+func Test_netrw_filecopy_powershell()
+  call SetShell('powershell')
+  call s:test_netrw_filecopy()
+endfunc
+
+func Test_netrw_filecopy_pwsh()
+  call SetShell('pwsh')
+  call s:test_netrw_filecopy()
+endfunc
+
+" Browser recursive directory copy
+func s:netrw_dircopy(count = 1)
+
+  " setup
+  let marked_dirname = "test_dir"
+  let marked_dir = netrw#fs#PathJoin($HOME, marked_dirname)
+  let target_dir = netrw#fs#PathJoin($HOME, "target")
+
+  call mkdir(marked_dir, "R")
+  call mkdir(target_dir, "R")
+
+  let dir_content = []
+  for i in range(a:count)
+    call add(dir_content, $"testfile{i}.txt")
+    call writefile(
+    \   [$"NetrwMarkFileCopy test dir content {i}"],
+    \   netrw#fs#PathJoin(marked_dir, dir_content[-1]))
+  endfor
+
+  " delegate
+  call Test_NetrwMarkFileCopy($HOME, target_dir, [marked_dirname])
+
+  " verify
+  for file in dir_content
+    call assert_equalfile(
+    \   netrw#fs#PathJoin(marked_dir, file),
+    \   netrw#fs#PathJoin(target_dir, marked_dirname, file),
+    \   "File copy failed for " . file)
+  endfor
+
+endfunc
+
+func s:test_netrw_dircopy()
+
+  " if shellslash is available, check both settings
+  if exists('+shellslash')
+    set shellslash&
+    call s:netrw_dircopy(10)
+    set shellslash!
+  endif
+
+  call s:netrw_dircopy(10)
+
+endfunc
+
+func Test_netrw_dircopy_default()
+  call SetShell('default')
+  call s:test_netrw_dircopy()
+endfunc
+
+func Test_netrw_dircopy_powershell()
+  call SetShell('powershell')
+  call s:test_netrw_dircopy()
+endfunc
+
+func Test_netrw_dircopy_pwsh()
+  call SetShell('pwsh')
+  call s:test_netrw_dircopy()
+endfunc
+
+" Copy file into the same directory with a different name
+func Test_netrw_dircopy_rename_default()
+  call SetShell('default')
+  call Test_NetrwMarkFileCopy_SameDir()
+endfunc
+
+func Test_netrw_dircopy_rename_powershell()
+  call SetShell('powershell')
+  call Test_NetrwMarkFileCopy_SameDir()
+endfunc
+
+func Test_netrw_dircopy_rename_pwsh()
+  call SetShell('pwsh')
+  call Test_NetrwMarkFileCopy_SameDir()
+endfunc
+
+" Browser file move
+func s:netrw_filemove(count = 1)
+  " setup
+  let marked_files = []
+  let source_dir = netrw#fs#PathJoin($HOME, "src")
+  let target_dir = netrw#fs#PathJoin($HOME, "target")
+
+  call mkdir(source_dir, "R")
+  call mkdir(target_dir, "R")
+
+  for i in range(a:count)
+    call add(marked_files, $"testfile{i}.txt")
+    call writefile(
+    \   [$"NetrwMarkFileMove test file {i}"],
+    \   netrw#fs#PathJoin(source_dir, marked_files[-1]))
+  endfor
+
+  " delegate
+  call Test_NetrwMarkFileMove(source_dir, target_dir, marked_files)
+
+  " verify
+  for i in range(a:count)
+    call assert_equal(
+    \   [$"NetrwMarkFileMove test file {i}"],
+    \   readfile(netrw#fs#PathJoin(target_dir, $"testfile{i}.txt")),
+    \   $"File move failed for testfile{i}.txt")
+  endfor
+endfunc
+
+func s:test_netrw_filemove()
+
+  " if shellslash is available, check both settings
+  if exists('+shellslash')
+    set shellslash&
+    call s:netrw_filemove(10)
+    set shellslash!
+  endif
+
+  call s:netrw_filemove(10)
+
+endfunc
+
+func Test_netrw_filemove_default()
+  call SetShell('default')
+  call s:test_netrw_filemove()
+endfunc
+
+func Test_netrw_filemove_powershell()
+  call SetShell('powershell')
+  call s:test_netrw_filemove()
+endfunc
+
+func Test_netrw_filemove_pwsh()
+  call SetShell('pwsh')
+  call s:test_netrw_filemove()
+endfunc
+
+" vim:ts=8 sts=2 sw=2 et

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1vreXT-00Df2H-Sg%40256bit.org.

Raspunde prin e-mail lui