diff options
| author | zeertzjq <zeertzjq@outlook.com> | 2026-02-16 06:31:16 +0800 |
|---|---|---|
| committer | zeertzjq <zeertzjq@outlook.com> | 2026-04-23 12:51:11 +0800 |
| commit | ed12d56163dd4e355ca8b7e83bbe2fc207e843b9 (patch) | |
| tree | 6714371decfdca1665e26efdbe6f1f110548353e | |
| parent | fd2ea2742536a3bd7711190d94224008d7ca1e84 (diff) | |
vim-patch:a2d87ba: runtime(netrw): Use right file system commands initialization for Windows
closes: vim/vim#19287
fixes: vim/vim#12290
https://github.com/vim/vim/commit/a2d87ba615f15956116f43f7c70f1b315d679cb4
Co-authored-by: Miguel Barro <miguel.barro@live.com>
| -rw-r--r-- | runtime/pack/dist/opt/netrw/autoload/netrw.vim | 261 | ||||
| -rw-r--r-- | runtime/pack/dist/opt/netrw/autoload/netrw/fs.vim | 3 | ||||
| -rw-r--r-- | test/old/testdir/test_plugin_netrw.vim | 461 |
3 files changed, 561 insertions, 164 deletions
diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index db3c1b8348..ba9f511138 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)) @@ -4735,38 +4737,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 @@ -5350,17 +5330,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") @@ -5368,7 +5345,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)) @@ -5376,69 +5353,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,"'\\(.*\\)'",'\1','')|endif - if tgt =~ "'" |let tgt = substitute(tgt ,"'\\(.*\\)'",'\1','')|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)) @@ -5450,11 +5443,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 @@ -5463,24 +5456,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) @@ -5502,18 +5487,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 @@ -5882,28 +5862,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.*\)$','\1','') - 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.*\)$','\1','') 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)) @@ -5916,7 +5903,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,'^\(.*/\)\(.\{-}\)$','\2','') @@ -5926,7 +5913,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,'^\(.*/\)\(.\{-}\)$','\2','') @@ -5936,7 +5923,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,'^\(.*/\)\(.\{-}\)$','\2','') diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw/fs.vim b/runtime/pack/dist/opt/netrw/autoload/netrw/fs.vim index f2f98596a5..8c002a88c5 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 =~ '^\a\{3,}://' diff --git a/test/old/testdir/test_plugin_netrw.vim b/test/old/testdir/test_plugin_netrw.vim index ab11c3da24..ac2cb9fb9e 100644 --- a/test/old/testdir/test_plugin_netrw.vim +++ b/test/old/testdir/test_plugin_netrw.vim @@ -1,32 +1,218 @@ -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:null, completion = v:null) " Nvim: use v:null instead of v:none + + if exists("s:inputdefaults_disabled") && s:inputdefaults_disabled || a:text == v:null + return input(a:prompt) + elseif a:completion == v:null + 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 += [ - \ '\n', - \ '"-- 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& + if has("win32") + " Nvim: default 'shell' is "sh" due to $SHELL being set in Makefile, + " but here 'shell' should be cmd.exe. + set shell=cmd.exe + endif + 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 + " Nvim: 'shellxquote' needs to be empty for the tests for work. + set shellxquote= +endfunc + func s:combine \( usernames \, methods @@ -62,19 +248,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 +269,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 |
