diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index d5e3a77dd4..d6caa50e7d 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -27,6 +27,11 @@ " 2024 Sep 19 by Vim Project: mf-selection highlight uses wrong pattern (#15700) " 2024 Sep 21 by Vim Project: remove extraneous closing bracket (#15718) " 2024 Oct 21 by Vim Project: remove netrwFileHandlers (#15895) +" 2024 Oct 27 by Vim Project: clean up gx mapping (#15721) +" 2024 Oct 30 by Vim Project: fix filetype detection for remote files (#15961) +" 2024 Oct 30 by Vim Project: fix x mapping on cygwin (#13687) +" 2024 Oct 31 by Vim Project: add netrw#Launch() and netrw#Open() (#15962) +" 2024 Oct 31 by Vim Project: fix E874 when browsing remote dir (#15964) " }}} " Former Maintainer: Charles E Campbell " GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim @@ -533,7 +538,6 @@ if !exists("g:netrw_sort_sequence") endif call s:NetrwInit("g:netrw_special_syntax" , 0) call s:NetrwInit("g:netrw_ssh_browse_reject", '^total\s\+\d\+$') -call s:NetrwInit("g:netrw_suppress_gx_mesg", 1) call s:NetrwInit("g:netrw_use_noswf" , 1) call s:NetrwInit("g:netrw_sizestyle" ,"b") " Default values - t-w ---------- {{{3 @@ -1153,47 +1157,35 @@ fun! netrw#Lexplore(count,rightside,...) " if a netrw window is already on the left-side of the tab " and a directory has been specified, explore with that " directory. -" call Decho("case has input argument(s) (a:1<".a:1.">)") let a1 = expand(a:1) -" call Decho("a:1<".a:1."> curwin#".curwin,'~'.expand("")) exe "1wincmd w" if &ft == "netrw" -" call Decho("exe Explore ".fnameescape(a:1),'~'.expand("")) exe "Explore ".fnameescape(a1) exe curwin."wincmd w" let s:lexplore_win= curwin let w:lexplore_buf= bufnr("%") if exists("t:netrw_lexposn") -" call Decho("forgetting t:netrw_lexposn",'~'.expand("")) unlet t:netrw_lexposn endif -" call Dret("netrw#Lexplore") return endif exe curwin."wincmd w" else let a1= "" -" call Decho("no input arguments") endif if exists("t:netrw_lexbufnr") " check if t:netrw_lexbufnr refers to a netrw window let lexwinnr = bufwinnr(t:netrw_lexbufnr) -" call Decho("lexwinnr= bufwinnr(t:netrw_lexbufnr#".t:netrw_lexbufnr.")=".lexwinnr) else let lexwinnr= 0 -" call Decho("t:netrw_lexbufnr doesn't exist") endif -" call Decho("lexwinnr=".lexwinnr,'~'.expand("")) if lexwinnr > 0 " close down netrw explorer window -" call Decho("t:netrw_lexbufnr#".t:netrw_lexbufnr.": close down netrw window",'~'.expand("")) exe lexwinnr."wincmd w" let g:netrw_winsize = -winwidth(0) let t:netrw_lexposn = winsaveview() -" call Decho("saving posn to t:netrw_lexposn<".string(t:netrw_lexposn).">",'~'.expand("")) -" call Decho("saving t:netrw_lexposn",'~'.expand("")) close if lexwinnr < curwin let curwin= curwin - 1 @@ -1202,11 +1194,9 @@ fun! netrw#Lexplore(count,rightside,...) exe curwin."wincmd w" endif unlet t:netrw_lexbufnr -" call Decho("unlet t:netrw_lexbufnr") else " open netrw explorer window -" call Decho("t:netrw_lexbufnr: open netrw explorer window",'~'.expand("")) exe "1wincmd w" let keep_altv = g:netrw_altv let g:netrw_altv = 0 @@ -1215,18 +1205,13 @@ fun! netrw#Lexplore(count,rightside,...) let g:netrw_winsize = a:count endif let curfile= expand("%") -" call Decho("curfile<".curfile.">",'~'.expand("")) exe (a:rightside? "botright" : "topleft")." vertical ".((g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize) . " new" -" call Decho("new buf#".bufnr("%")." win#".winnr()) if a:0 > 0 && a1 != "" -" call Decho("case 1: Explore ".a1,'~'.expand("")) call netrw#Explore(0,0,0,a1) exe "Explore ".fnameescape(a1) elseif curfile =~ '^\a\{3,}://' -" call Decho("case 2: Explore ".substitute(curfile,'[^/\\]*$','',''),'~'.expand("")) call netrw#Explore(0,0,0,substitute(curfile,'[^/\\]*$','','')) else -" call Decho("case 3: Explore .",'~'.expand("")) call netrw#Explore(0,0,0,".") endif if a:count != 0 @@ -1239,11 +1224,7 @@ fun! netrw#Lexplore(count,rightside,...) " Since the intended use of :Lexplore is to have an always-present explorer window, the extra " effort to prevent mis-use of :Lex is warranted. set bh=wipe -" call Decho("let t:netrw_lexbufnr=".t:netrw_lexbufnr) -" call Decho("t:netrw_lexposn".(exists("t:netrw_lexposn")? string(t:netrw_lexposn) : " n/a")) if exists("t:netrw_lexposn") -" call Decho("restoring to t:netrw_lexposn",'~'.expand("")) -" call Decho("restoring posn to t:netrw_lexposn<".string(t:netrw_lexposn).">",'~'.expand("")) call winrestview(t:netrw_lexposn) unlet t:netrw_lexposn endif @@ -1256,10 +1237,8 @@ fun! netrw#Lexplore(count,rightside,...) else let g:netrw_chgwin= 2 endif -" call Decho("let g:netrw_chgwin=".g:netrw_chgwin) endif -" call Dret("netrw#Lexplore") endfun " --------------------------------------------------------------------- @@ -1743,29 +1722,20 @@ endfun " --------------------------------------------------------------------- " s:NetrwOptionsRestore: restore options (based on prior s:NetrwOptionsSave) {{{2 fun! s:NetrwOptionsRestore(vt) -" call Dfunc("s:NetrwOptionsRestore(vt<".a:vt.">) win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> winnr($)=".winnr("$")) -" call Decho("(s:NetrwOptionsRestore) lines=".&lines) -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("")) if !exists("{a:vt}netrw_optionsave") -" call Decho("case ".a:vt."netrw_optionsave : doesn't exist",'~'.expand("")) - - " filereadable() returns zero for remote files (e.g. scp://localhost//etc/fstab) - if filereadable(expand("%")) || expand("%") =~# '^\w\+://\f\+/' -" call Decho("..doing filetype detect anyway") + " filereadable() returns zero for remote files (e.g. scp://user@localhost//etc/fstab) + " Note: @ may not be in 'isfname', so '^\w\+://\f\+/' may not match + if filereadable(expand("%")) || expand("%") =~# '^\w\+://\f\+' filetype detect -" call Decho("..settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("")) else setl ft=netrw endif -" call Decho("..ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("")) -" call Dret("s:NetrwOptionsRestore : ".a:vt."netrw_optionsave doesn't exist") return endif unlet {a:vt}netrw_optionsave if exists("+acd") if exists("{a:vt}netrw_acdkeep") -" call Decho("g:netrw_keepdir=".g:netrw_keepdir.": getcwd<".getcwd()."> acd=".&acd,'~'.expand("")) let curdir = getcwd() let &l:acd = {a:vt}netrw_acdkeep unlet {a:vt}netrw_acdkeep @@ -1774,53 +1744,43 @@ fun! s:NetrwOptionsRestore(vt) endif endif endif -" call Decho("(s:NetrwOptionsRestore) #1 lines=".&lines) call s:NetrwRestoreSetting(a:vt."netrw_aikeep","&l:ai") call s:NetrwRestoreSetting(a:vt."netrw_awkeep","&l:aw") call s:NetrwRestoreSetting(a:vt."netrw_blkeep","&l:bl") call s:NetrwRestoreSetting(a:vt."netrw_btkeep","&l:bt") call s:NetrwRestoreSetting(a:vt."netrw_bombkeep","&l:bomb") -" call Decho("(s:NetrwOptionsRestore) #2 lines=".&lines) call s:NetrwRestoreSetting(a:vt."netrw_cedit","&cedit") call s:NetrwRestoreSetting(a:vt."netrw_cikeep","&l:ci") call s:NetrwRestoreSetting(a:vt."netrw_cinkeep","&l:cin") call s:NetrwRestoreSetting(a:vt."netrw_cinokeep","&l:cino") call s:NetrwRestoreSetting(a:vt."netrw_comkeep","&l:com") -" call Decho("(s:NetrwOptionsRestore) #3 lines=".&lines) call s:NetrwRestoreSetting(a:vt."netrw_cpokeep","&l:cpo") call s:NetrwRestoreSetting(a:vt."netrw_diffkeep","&l:diff") call s:NetrwRestoreSetting(a:vt."netrw_fenkeep","&l:fen") if exists("g:netrw_ffkeep") && g:netrw_ffkeep call s:NetrwRestoreSetting(a:vt."netrw_ffkeep")","&l:ff") endif -" call Decho("(s:NetrwOptionsRestore) #4 lines=".&lines) call s:NetrwRestoreSetting(a:vt."netrw_fokeep" ,"&l:fo") call s:NetrwRestoreSetting(a:vt."netrw_gdkeep" ,"&l:gd") call s:NetrwRestoreSetting(a:vt."netrw_gokeep" ,"&go") call s:NetrwRestoreSetting(a:vt."netrw_hidkeep" ,"&l:hidden") -" call Decho("(s:NetrwOptionsRestore) #5 lines=".&lines) call s:NetrwRestoreSetting(a:vt."netrw_imkeep" ,"&l:im") call s:NetrwRestoreSetting(a:vt."netrw_iskkeep" ,"&l:isk") -" call Decho("(s:NetrwOptionsRestore) #6 lines=".&lines) call s:NetrwRestoreSetting(a:vt."netrw_lines" ,"&lines") -" call Decho("(s:NetrwOptionsRestore) #7 lines=".&lines) call s:NetrwRestoreSetting(a:vt."netrw_lskeep" ,"&l:ls") call s:NetrwRestoreSetting(a:vt."netrw_makeep" ,"&l:ma") call s:NetrwRestoreSetting(a:vt."netrw_magickeep","&l:magic") call s:NetrwRestoreSetting(a:vt."netrw_modkeep" ,"&l:mod") call s:NetrwRestoreSetting(a:vt."netrw_nukeep" ,"&l:nu") -" call Decho("(s:NetrwOptionsRestore) #8 lines=".&lines) call s:NetrwRestoreSetting(a:vt."netrw_rnukeep" ,"&l:rnu") call s:NetrwRestoreSetting(a:vt."netrw_repkeep" ,"&l:report") call s:NetrwRestoreSetting(a:vt."netrw_rokeep" ,"&l:ro") call s:NetrwRestoreSetting(a:vt."netrw_selkeep" ,"&l:sel") -" call Decho("(s:NetrwOptionsRestore) #9 lines=".&lines) call s:NetrwRestoreSetting(a:vt."netrw_spellkeep","&l:spell") call s:NetrwRestoreSetting(a:vt."netrw_twkeep" ,"&l:tw") call s:NetrwRestoreSetting(a:vt."netrw_wigkeep" ,"&l:wig") call s:NetrwRestoreSetting(a:vt."netrw_wrapkeep" ,"&l:wrap") call s:NetrwRestoreSetting(a:vt."netrw_writekeep","&l:write") -" call Decho("(s:NetrwOptionsRestore) #10 lines=".&lines) call s:NetrwRestoreSetting("s:yykeep","@@") " former problem: start with liststyle=0; press : result, following line resets l:ts. " Fixed; in s:PerformListing, when w:netrw_liststyle is s:LONGLIST, will use a printf to pad filename with spaces @@ -1853,22 +1813,12 @@ fun! s:NetrwOptionsRestore(vt) endif call s:NetrwRestoreSetting(a:vt."netrw_slashkeep","@/") -" call Decho("g:netrw_keepdir=".g:netrw_keepdir.": getcwd<".getcwd()."> acd=".&acd,'~'.expand("")) -" call Decho("fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist"),'~'.expand("")) -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("")) -" call Decho("diff=".&l:diff." win#".winnr()." w:netrw_diffkeep=".(exists("w:netrw_diffkeep")? w:netrw_diffkeep : "doesn't exist"),'~'.expand("")) -" call Decho("ts=".&l:ts,'~'.expand("")) " Moved the filetype detect here from NetrwGetFile() because remote files " were having their filetype detect-generated settings overwritten by " NetrwOptionRestore. if &ft != "netrw" -" call Decho("before: filetype detect (ft=".&ft.")",'~'.expand("")) filetype detect -" call Decho("after : filetype detect (ft=".&ft.")",'~'.expand("")) endif -" call Decho("(s:NetrwOptionsRestore) lines=".&lines) -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("")) -" call Dret("s:NetrwOptionsRestore : tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> modified=".&modified." modifiable=".&modifiable." readonly=".&readonly) endfun " --------------------------------------------------------------------- @@ -5244,32 +5194,132 @@ fun! s:NetrwBrowseUpDir(islocal) " call Dret("s:NetrwBrowseUpDir") endfun +func s:redir() + " set up redirection (avoids browser messages) + " by default if not set, g:netrw_suppress_gx_mesg is true + if get(g:, 'netrw_suppress_gx_mesg', 1) + if &srr =~# "%s" + return printf(&srr, has("win32") ? "nul" : "/dev/null") + else + return &srr .. (has("win32") ? "nul" : "/dev/null") + endif + endif + return '' +endfunc + +if has('unix') + if has('win32unix') + " Cygwin provides cygstart + if executable('cygstart') + fun! netrw#Launch(args) + exe 'silent ! cygstart --hide' a:args s:redir() | redraw! + endfun + elseif !empty($MSYSTEM) && executable('start') + " MSYS2/Git Bash comes by default without cygstart; see + " https://www.msys2.org/wiki/How-does-MSYS2-differ-from-Cygwin + " Instead it provides /usr/bin/start script running `cmd.exe //c start` + " Adding "" //b` sets void title, hides cmd window and blocks path conversion + " of /b to \b\ " by MSYS2; see https://www.msys2.org/docs/filesystem-paths/ + fun! netrw#Launch(args) + exe 'silent !start "" //b' a:args s:redir() | redraw! + endfun + else + " imitate /usr/bin/start script for other environments and hope for the best + fun! netrw#Launch(args) + exe 'silent !cmd //c start "" //b' a:args s:redir() | redraw! + endfun + endif + elseif exists('$WSL_DISTRO_NAME') " use cmd.exe to start GUI apps in WSL + fun! netrw#Launch(args) + let args = a:args + exe 'silent !' .. + \ ((args =~? '\v<\f+\.(exe|com|bat|cmd)>') ? + \ 'cmd.exe /c start "" /b ' .. args : + \ 'nohup ' .. args .. ' ' .. s:redir() .. ' &') + \ | redraw! + endfun + else + fun! netrw#Launch(args) + exe ':silent ! nohup' a:args s:redir() '&' | redraw! + endfun + endif +elseif has('win32') + fun! netrw#Launch(args) + exe 'silent !' .. (&shell =~? '\' ? '' : 'cmd.exe /c') + \ 'start "" /b' a:args s:redir() | redraw! + endfun +else + fun! netrw#Launch(dummy) + echom 'No common launcher found' + endfun +endif + +" Git Bash +if has('win32unix') + " (cyg)start suffices + let s:os_viewer = '' +" Windows / WSL +elseif executable('explorer.exe') + let s:os_viewer = 'explorer.exe' +" Linux / BSD +elseif executable('xdg-open') + let s:os_viewer = 'xdg-open' +" MacOS +elseif executable('open') + let s:os_viewer = 'open' +endif + +fun! s:viewer() + if exists('g:netrw_browsex_viewer') && executable(g:netrw_browsex_viewer) + " extract any viewing options. Assumes that they're set apart by spaces. + " call Decho("extract any viewing options from g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("")) + if g:netrw_browsex_viewer =~ '\s' + let viewer = substitute(g:netrw_browsex_viewer,'\s.*$','','') + let viewopt = substitute(g:netrw_browsex_viewer,'^\S\+\s*','','')." " + let oviewer = '' + let cnt = 1 + while !executable(viewer) && viewer != oviewer + let viewer = substitute(g:netrw_browsex_viewer,'^\(\(^\S\+\s\+\)\{'.cnt.'}\S\+\)\(.*\)$','\1','') + let viewopt = substitute(g:netrw_browsex_viewer,'^\(\(^\S\+\s\+\)\{'.cnt.'}\S\+\)\(.*\)$','\3','')." " + let cnt = cnt + 1 + let oviewer = viewer + " call Decho("!exe: viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("")) + endwhile + else + let viewer = g:netrw_browsex_viewer + let viewopt = "" + endif + " call Decho("viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("")) + return viewer .. ' ' .. viewopt + else + if !exists('s:os_viewer') + call netrw#ErrorMsg(s:ERROR,"No program to open this path found. See :help Open for more information.",106) + else + return s:os_viewer + endif + endif +endfun + +fun! netrw#Open(file) abort + call netrw#Launch(s:viewer() .. ' ' .. shellescape(a:file, 1)) +endf + +if !exists('g:netrw_regex_url') + let g:netrw_regex_url = '\%(\%(http\|ftp\|irc\)s\?\|file\)://\S\{-}' +endif + " --------------------------------------------------------------------- " netrw#BrowseX: (implements "x" and "gx") executes a special "viewer" script or program for the {{{2 " given filename; typically this means given their extension. " 0=local, 1=remote fun! netrw#BrowseX(fname,remote) - let use_ctrlo= 1 -" call Dfunc("netrw#BrowseX(fname<".a:fname."> remote=".a:remote.") implements x and gx maps") - if a:remote == 0 && isdirectory(a:fname) " if its really just a local directory, then do a "gf" instead -" call Decho("remote≡0 and a:fname<".a:fname."> ".(isdirectory(a:fname)? "is a directory" : "is not a directory"),'~'.expand("")) -" call Decho("..appears to be a local directory; using e ".a:fname." instead",'~'.expand("")) exe "e ".a:fname -" call Dret("netrw#BrowseX") - return elseif a:remote == 1 && a:fname !~ '^https\=:' && a:fname =~ '/$' " remote directory, not a webpage access, looks like an attempt to do a directory listing -" call Decho("remote≡1 and a:fname<".a:fname.">",'~'.expand("")) -" call Decho("..and fname ".((a:fname =~ '^https\=:')? 'matches' : 'does not match').'^https\=:','~'.expand("")) -" call Decho("..and fname ".((a:fname =~ '/$')? 'matches' : 'does not match').' /$','~'.expand("")) -" call Decho("..appears to be a remote directory listing request; using gf instead",'~'.expand("")) norm! gf -" call Dret("netrw#BrowseX") - return endif -" call Decho("not a local file nor a webpage request",'~'.expand("")) if exists("g:netrw_browsex_viewer") && exists("g:netrw_browsex_support_remote") && !g:netrw_browsex_support_remote let remote = a:remote @@ -5279,7 +5329,6 @@ fun! netrw#BrowseX(fname,remote) let ykeep = @@ let screenposn = winsaveview() -" call Decho("saving posn to screenposn<".string(screenposn).">",'~'.expand("")) " need to save and restore aw setting as gx can invoke this function from non-netrw buffers let awkeep = &aw @@ -5290,22 +5339,18 @@ fun! netrw#BrowseX(fname,remote) if exists("g:Netrw_corehandler") if type(g:Netrw_corehandler) == 2 " g:Netrw_corehandler is a function reference (see :help Funcref) -" call Decho("g:Netrw_corehandler is a funcref",'~'.expand("")) call g:Netrw_corehandler(s:NetrwFile(a:fname)) elseif type(g:Netrw_corehandler) == 3 " g:Netrw_corehandler is a List of function references (see :help Funcref) -" call Decho("g:Netrw_corehandler is a List",'~'.expand("")) for Fncref in g:Netrw_corehandler if type(Fncref) == 2 call Fncref(a:fname) endif endfor endif -" call Decho("restoring posn: screenposn<".string(screenposn).">,'~'.expand(""))" call winrestview(screenposn) let @@= ykeep let &aw= awkeep -" call Dret("netrw#BrowseX : coredump handler invoked") return endif endif @@ -5319,163 +5364,35 @@ fun! netrw#BrowseX(fname,remote) if exten =~ "[\\/]" let exten= "" endif -" call Decho("exten<".exten.">",'~'.expand("")) if remote == 1 " create a local copy -" call Decho("remote: remote=".remote.": create a local copy of <".a:fname.">",'~'.expand("")) setl bh=delete call netrw#NetRead(3,a:fname) " attempt to rename tempfile let basename= substitute(a:fname,'^\(.*\)/\(.*\)\.\([^.]*\)$','\2','') let newname = substitute(s:netrw_tmpfile,'^\(.*\)/\(.*\)\.\([^.]*\)$','\1/'.basename.'.\3','') -" call Decho("basename<".basename.">",'~'.expand("")) -" call Decho("newname <".newname.">",'~'.expand("")) if s:netrw_tmpfile != newname && newname != "" if rename(s:netrw_tmpfile,newname) == 0 " renaming succeeded -" call Decho("renaming succeeded (tmpfile<".s:netrw_tmpfile."> to <".newname.">)") let fname= newname else " renaming failed -" call Decho("renaming failed (tmpfile<".s:netrw_tmpfile."> to <".newname.">)") let fname= s:netrw_tmpfile endif else let fname= s:netrw_tmpfile endif else -" call Decho("local: remote=".remote.": handling local copy of <".a:fname.">",'~'.expand("")) let fname= a:fname " special ~ handler for local if fname =~ '^\~' && expand("$HOME") != "" -" call Decho('invoking special ~ handler','~'.expand("")) let fname= s:NetrwFile(substitute(fname,'^\~',expand("$HOME"),'')) endif endif -" call Decho("fname<".fname.">",'~'.expand("")) -" call Decho("exten<".exten."> "."netrwFileHandlers#NFH_".exten."():exists=".exists("*netrwFileHandlers#NFH_".exten),'~'.expand("")) - " set up redirection (avoids browser messages) - " by default, g:netrw_suppress_gx_mesg is true - if g:netrw_suppress_gx_mesg - if &srr =~ "%s" - if has("win32") - let redir= substitute(&srr,"%s","nul","") - else - let redir= substitute(&srr,"%s","/dev/null","") - endif - elseif has("win32") - let redir= &srr . "nul" - else - let redir= &srr . "/dev/null" - endif - else - let redir= "" - endif -" call Decho("set up redirection: redir{".redir."} srr{".&srr."}",'~'.expand("")) - - " extract any viewing options. Assumes that they're set apart by spaces. - if exists("g:netrw_browsex_viewer") -" call Decho("extract any viewing options from g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("")) - if g:netrw_browsex_viewer =~ '\s' - let viewer = substitute(g:netrw_browsex_viewer,'\s.*$','','') - let viewopt = substitute(g:netrw_browsex_viewer,'^\S\+\s*','','')." " - let oviewer = '' - let cnt = 1 - while !executable(viewer) && viewer != oviewer - let viewer = substitute(g:netrw_browsex_viewer,'^\(\(^\S\+\s\+\)\{'.cnt.'}\S\+\)\(.*\)$','\1','') - let viewopt = substitute(g:netrw_browsex_viewer,'^\(\(^\S\+\s\+\)\{'.cnt.'}\S\+\)\(.*\)$','\3','')." " - let cnt = cnt + 1 - let oviewer = viewer -" call Decho("!exe: viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("")) - endwhile - else - let viewer = g:netrw_browsex_viewer - let viewopt = "" - endif -" call Decho("viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("")) - endif - - " execute the file handler -" call Decho("execute the file handler (if any)",'~'.expand("")) - if exists("g:netrw_browsex_viewer") && executable(viewer) -" call Decho("(netrw#BrowseX) g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("")) - call s:NetrwExe("sil !".viewer." ".viewopt.s:ShellEscape(fname,1).redir) - let ret= v:shell_error - - elseif has("win32") -" call Decho("(netrw#BrowseX) win".(has("win32")? "32" : "64"),'~'.expand("")) - if executable("start") - call s:NetrwExe('sil! !start rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(fname,1)) - elseif executable("rundll32") - call s:NetrwExe('sil! !rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(fname,1)) - else - call netrw#ErrorMsg(s:WARNING,"rundll32 not on path",74) - endif - let ret= v:shell_error - - elseif has("win32unix") - let winfname= 'c:\cygwin'.substitute(fname,'/','\\','g') -" call Decho("(netrw#BrowseX) cygwin: winfname<".s:ShellEscape(winfname,1).">",'~'.expand("")) - if executable("start") -" call Decho("(netrw#BrowseX) win32unix+start",'~'.expand("")) - call s:NetrwExe('sil !start rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(winfname,1)) - elseif executable("rundll32") -" call Decho("(netrw#BrowseX) win32unix+rundll32",'~'.expand("")) - call s:NetrwExe('sil !rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(winfname,1)) - elseif executable("cygstart") -" call Decho("(netrw#BrowseX) win32unix+cygstart",'~'.expand("")) - call s:NetrwExe('sil !cygstart '.s:ShellEscape(fname,1)) - else - call netrw#ErrorMsg(s:WARNING,"rundll32 not on path",74) - endif - let ret= v:shell_error - - elseif has("unix") && $DESKTOP_SESSION == "mate" && executable("atril") -" call Decho("(netrw#BrowseX) unix and atril",'~'.expand("")) - if a:fname =~ '^https\=://' - " atril does not appear to understand how to handle html -- so use gvim to edit the document - let use_ctrlo= 0 -" call Decho("fname<".fname.">") -" call Decho("a:fname<".a:fname.">") - call s:NetrwExe("sil! !gvim ".fname.' -c "keepj keepalt file '.fnameescape(a:fname).'"') - - else - call s:NetrwExe("sil !atril ".s:ShellEscape(fname,1).redir) - endif - let ret= v:shell_error - - elseif has("unix") && executable("kfmclient") && s:CheckIfKde() -" call Decho("(netrw#BrowseX) unix and kfmclient",'~'.expand("")) - call s:NetrwExe("sil !kfmclient exec ".s:ShellEscape(fname,1)." ".redir) - let ret= v:shell_error - - elseif has("unix") && executable("exo-open") && executable("xdg-open") && executable("setsid") -" call Decho("(netrw#BrowseX) unix, exo-open, xdg-open",'~'.expand("")) - call s:NetrwExe("sil !setsid xdg-open ".s:ShellEscape(fname,1).redir.'&') - let ret= v:shell_error - - elseif has("unix") && executable("xdg-open") -" call Decho("(netrw#BrowseX) unix and xdg-open",'~'.expand("")) - call s:NetrwExe("sil !xdg-open ".s:ShellEscape(fname,1).redir.'&') - let ret= v:shell_error - - elseif has("macunix") && executable("open") -" call Decho("(netrw#BrowseX) macunix and open",'~'.expand("")) - call s:NetrwExe("sil !open ".s:ShellEscape(fname,1)." ".redir) - let ret= v:shell_error - else - call netrw#ErrorMsg(s:ERROR, "Couldn't find a program to open '".a:fname."'", 0) - let ret=0 - endif - - if ret - call netrw#ErrorMsg(s:ERROR, "Failed to open '".a:fname."'", 0) - endif - - " restoring redraw! after external file handlers - redraw! + " although shellescape(..., 1) is used in netrw#Open(), it's insufficient + call netrw#Open(escape(fname, '#%')) " cleanup: remove temporary file, " delete current buffer if success with handler, @@ -5483,7 +5400,6 @@ fun! netrw#BrowseX(fname,remote) " Feb 12, 2008: had to de-activate removal of " temporary file because it wasn't getting seen. " if remote == 1 && fname != a:fname -"" call Decho("deleting temporary file<".fname.">",'~'.expand("")) " call s:NetrwDelete(fname) " endif @@ -5492,16 +5408,11 @@ fun! netrw#BrowseX(fname,remote) if g:netrw_use_noswf setl noswf endif - if use_ctrlo - exe "sil! NetrwKeepj norm! \" - endif + exe "sil! NetrwKeepj norm! \" endif -" call Decho("restoring posn to screenposn<".string(screenposn).">",'~'.expand("")) call winrestview(screenposn) let @@ = ykeep let &aw= awkeep - -" call Dret("netrw#BrowseX") endfun " --------------------------------------------------------------------- @@ -5513,12 +5424,37 @@ fun! netrw#GX() if &ft == "netrw" let fname= s:NetrwGetWord() else - let fname= expand((exists("g:netrw_gx")? g:netrw_gx : '')) + let fname= exists("g:netrw_gx")? expand(g:netrw_gx) : s:GetURL() endif " call Dret("netrw#GX <".fname.">") return fname endfun +fun! s:GetURL() abort + let URL = '' + if exists('*Netrw_get_URL_' .. &filetype) + let URL = call('Netrw_get_URL_' .. &filetype, []) + endif + if !empty(URL) | return URL | endif + " URLs end in letter, digit or forward slash + let URL = matchstr(expand(""), '\<' .. g:netrw_regex_url .. '\ze[^A-Za-z0-9/]*$') + if !empty(URL) | return URL | endif + + " Is it a file in the current work dir ... + let file = expand("") + if filereadable(file) | return file | endif + " ... or in that of the current buffer? + let path = fnamemodify(expand('%'), ':p') + if isdirectory(path) + let dir = path + elseif filereadable(path) + let dir = fnamemodify(path, ':h') + endif + if exists('dir') && filereadable(dir..'/'..file) | return dir..'/'..file | endif + + return '' +endf + " --------------------------------------------------------------------- " netrw#BrowseXVis: used by gx in visual mode to select a file for browsing {{{2 fun! netrw#BrowseXVis() @@ -5753,7 +5689,7 @@ fun! s:NetrwGlob(direntry,expr,pare) endif let w:netrw_liststyle= keep_liststyle else - let path= s:ComposePath(fnameescape(a:direntry),a:expr) + let path= s:ComposePath(fnameescape(a:direntry), a:expr) if has("win32") " escape [ so it is not detected as wildcard character, see :h wildcard let path= substitute(path, '[', '[[]', 'g') @@ -5767,7 +5703,6 @@ fun! s:NetrwGlob(direntry,expr,pare) let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")') endif endif -" call Dret("s:NetrwGlob ".string(filelist)) return filelist endfun @@ -6670,6 +6605,7 @@ fun! s:NetrwMaps(islocal) nnoremap U :call NetrwBookHistHandler(5,b:netrw_curdir) nnoremap v :call NetrwSplit(2) nnoremap x :call netrw#BrowseX(NetrwBrowseChgDir(0,NetrwGetWord()),1) + nmap gx x if !hasmapto('NetrwHideEdit') nmap NetrwHideEdit endif @@ -8241,7 +8177,7 @@ fun! netrw#Shrink() elseif winwidth(bufwinnr(t:netrw_lexbufnr)) >= 0 exe "vert resize ".t:netrw_winwidth " call Decho("vert resize ".t:netrw_winwidth,'~'.expand("")) - else + else call netrw#Lexplore(0,0) endif @@ -8558,7 +8494,7 @@ fun! s:NetrwPrevWinOpen(islocal) " call Decho("COMBAK#11: mod=".&mod) " call Decho("wincmd p (now in win#".winnr().") curdir<".curdir.">",'~'.expand("")) " call Decho("COMBAK#12: mod=".&mod) - + if exists("s:lexplore_win") && s:lexplore_win == winnr() " whoops -- user trying to open file in the Lexplore window. " Use Lexplore's opening-file window instead. @@ -9539,22 +9475,16 @@ endfun " Called by s:PerformListing() fun! s:NetrwTreeListing(dirname) if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST -" call Dfunc("s:NetrwTreeListing() bufname<".expand("%").">") -" call Decho("curdir<".a:dirname.">",'~'.expand("")) -" call Decho("win#".winnr().": w:netrw_treetop ".(exists("w:netrw_treetop")? "exists" : "doesn't exist")." w:netrw_treedict ".(exists("w:netrw_treedict")? "exists" : "doesn't exit"),'~'.expand("")) -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("")) " update the treetop if !exists("w:netrw_treetop") -" call Decho("update the treetop (w:netrw_treetop doesn't exist yet)",'~'.expand("")) let w:netrw_treetop= a:dirname let s:netrw_treetop= w:netrw_treetop -" call Decho("w:netrw_treetop<".w:netrw_treetop."> (reusing)",'~'.expand("")) - elseif (w:netrw_treetop =~ ('^'.a:dirname) && s:Strlen(a:dirname) < s:Strlen(w:netrw_treetop)) || a:dirname !~ ('^'.w:netrw_treetop) -" call Decho("update the treetop (override w:netrw_treetop with a:dirname<".a:dirname.">)",'~'.expand("")) + " use \V in case the directory contains specials chars like '$' or '~' + elseif (w:netrw_treetop =~ ('^'.'\V'.a:dirname) && s:Strlen(a:dirname) < s:Strlen(w:netrw_treetop)) + \ || a:dirname !~ ('^'.'\V'.w:netrw_treetop) let w:netrw_treetop= a:dirname let s:netrw_treetop= w:netrw_treetop -" call Decho("w:netrw_treetop<".w:netrw_treetop."> (went up)",'~'.expand("")) endif if exists("w:netrw_treetop") let s:netrw_treetop= w:netrw_treetop @@ -9565,16 +9495,12 @@ fun! s:NetrwTreeListing(dirname) if !exists("w:netrw_treedict") " insure that we have a treedict, albeit empty -" call Decho("initializing w:netrw_treedict to empty",'~'.expand("")) let w:netrw_treedict= {} endif " update the dictionary for the current directory -" call Decho("updating: w:netrw_treedict[".a:dirname.'] -> [directory listing]','~'.expand("")) -" call Decho("w:netrw_bannercnt=".w:netrw_bannercnt." line($)=".line("$"),'~'.expand("")) exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$g@^\.\.\=/$@d _' let w:netrw_treedict[a:dirname]= getline(w:netrw_bannercnt,line("$")) -" call Decho("w:treedict[".a:dirname."]= ".string(w:netrw_treedict[a:dirname]),'~'.expand("")) exe "sil! NetrwKeepj ".w:netrw_bannercnt.",$d _" " if past banner, record word @@ -9583,23 +9509,17 @@ fun! s:NetrwTreeListing(dirname) else let fname= "" endif -" call Decho("fname<".fname.">",'~'.expand("")) -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("")) " display from treetop on down -" call Decho("(s:NetrwTreeListing) w:netrw_treetop<".w:netrw_treetop.">") NetrwKeepj call s:NetrwTreeDisplay(w:netrw_treetop,"") -" call Decho("s:NetrwTreeDisplay) setl noma nomod ro",'~'.expand("")) " remove any blank line remaining as line#1 (happens in treelisting mode with banner suppressed) while getline(1) =~ '^\s*$' && byte2line(1) > 0 -" call Decho("deleting blank line",'~'.expand("")) 1d endwhile exe "setl ".g:netrw_bufsettings -" call Dret("s:NetrwTreeListing : bufname<".expand("%").">") return endif endfun @@ -11963,7 +11883,7 @@ fun! s:NetrwEnew(...) " call Dfunc("s:NetrwEnew() a:0=".a:0." win#".winnr()." winnr($)=".winnr("$")." bufnr($)=".bufnr("$")." expand(%)<".expand("%").">") " call Decho("curdir<".((a:0>0)? a:1 : "")."> buf#".bufnr("%")."<".bufname("%").">",'~'.expand("")) - " Clean out the last buffer: + " Clean out the last buffer: " Check if the last buffer has # > 1, is unlisted, is unnamed, and does not appear in a window " If so, delete it. call s:NetrwBufRemover(bufnr("$")) @@ -12043,13 +11963,16 @@ endfun " s:NetrwExe: executes a string using "!" {{{2 fun! s:NetrwExe(cmd) " call Dfunc("s:NetrwExe(a:cmd<".a:cmd.">)") - if has("win32") && &shell !~? 'cmd\|pwsh\|powershell' && !g:netrw_cygwin + if has("win32") " call Decho("using win32:",expand("")) let savedShell=[&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash] set shell& shellcmdflag& shellxquote& shellxescape& set shellquote& shellpipe& shellredir& shellslash& - exe a:cmd - let [&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash] = savedShell + try + exe a:cmd + finally + let [&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash] = savedShell + endtry else " call Decho("exe ".a:cmd,'~'.expand("")) exe a:cmd @@ -12152,7 +12075,7 @@ fun! s:NetrwHumanReadable(sz) " call Dfunc("s:NetrwHumanReadable(sz=".a:sz.") type=".type(a:sz)." style=".g:netrw_sizestyle ) if g:netrw_sizestyle == 'h' - if a:sz >= 1000000000 + if a:sz >= 1000000000 let sz = printf("%.1f",a:sz/1000000000.0)."g" elseif a:sz >= 10000000 let sz = printf("%d",a:sz/1000000)."m" @@ -12580,7 +12503,7 @@ endfun fun! s:ShellEscape(s, ...) if has('win32') && $SHELL == '' && &shellslash return printf('"%s"', substitute(a:s, '"', '""', 'g')) - endif + endif let f = a:0 > 0 ? a:1 : 0 return shellescape(a:s, f) endfun diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index 7fc7d4140a..ccc899cd6d 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -1465,48 +1465,106 @@ With either form of the command, netrw will first ask for confirmation that the removal is in fact what you want to do. If netrw doesn't have permission to remove a file, it will issue an error message. - *netrw-gx* CUSTOMIZING BROWSING WITH A SPECIAL HANDLER *netrw-x* *netrw-handler* {{{2 Certain files, such as html, gif, jpeg, (word/office) doc, etc, files, are best seen with a special handler (ie. a tool provided with your computer's -operating system). Netrw allows one to invoke such special handlers by: > +operating system). Netrw allows one to invoke such special handlers by: - * when Exploring, hit the "x" key - * when editing, hit gx with the cursor atop the special filename -< (latter not available if the |g:netrw_nogx| variable exists) + * hitting gx with the cursor atop the file path or alternatively x + in a netrw buffer; the former can be disabled by defining the + |g:netrw_nogx| variable + * when in command line, typing :Open , see |:Open| below. -Netrw determines which special handler by the following method: - - * if |g:netrw_browsex_viewer| exists, then it will be used to attempt to - view files. Examples of useful settings (place into your <.vimrc>): > - - :let g:netrw_browsex_viewer= "kfmclient exec" -< or > - :let g:netrw_browsex_viewer= "xdg-open" -< - If the viewer you wish to use does not support handling of a remote URL - directory, set |g:netrw_browsex_support_remote| to 0. - * for Windows 32 or 64, the URL and FileProtocolHandler dlls are used. - * for Gnome (with gnome-open): gnome-open is used. - * for KDE (with kfmclient) : kfmclient is used - * for Mac OS X : open is used. - -The gx mapping extends to all buffers; apply "gx" while atop a word and netrw -will apply a special handler to it (like "x" works when in a netrw buffer). One may also use visual mode (see |visual-start|) to select the text that the -special handler will use. Normally gx uses expand("") to pick up the -text under the cursor; one may change what |expand()| uses via the +special handler will use. Normally gx checks for a close-by URL or file name +to pick up the text under the cursor; one may change what |expand()| uses via the |g:netrw_gx| variable (options include "", ""). Note that expand("") depends on the |'isfname'| setting. Alternatively, one may select the text to be used by gx by making a visual selection (see |visual-block|) and then pressing gx. +The selection function can be adapted for each filetype by adding a function +`Netrw_get_URL_`, where is given by the 'filetype'. +The function should return the URL or file name to be used by gx, and will +fall back to the default behavior if it returns an empty string. +For example, special handlers for links Markdown and HTML are + +" make gx work on concealed links regardless of exact cursor position: > + + function Netrw_get_URL_markdown() + " markdown URL such as [link text](http://ya.ru 'yandex search') + try + let save_view = winsaveview() + if searchpair('\[.\{-}\](', '', ')\zs', 'cbW', '', line('.')) > 0 + return matchstr(getline('.')[col('.')-1:], + \ '\[.\{-}\](\zs' .. g:netrw_regex_url .. '\ze\(\s\+.\{-}\)\?)') + endif + finally + call winrestview(save_view) + return '' + endtry + endfunction + + function Netrw_get_URL_html() + " HTML URL such as Python is here + " + try + let save_view = winsaveview() + if searchpair('\|/>\)\zs', 'cbW', '', line('.')) > 0 + return matchstr(getline('.')[col('.') - 1 : ], + \ 'href=["'.."'"..']\?\zs\S\{-}\ze["'.."'"..']\?/\?>') + endif + finally + call winrestview(save_view) + return '' + endtry + endfunction +< +Other than a file path, the text under the cursor may be a URL. Netrw uses +by default the following regular expression to determine if the text under the +cursor is a URL: +> + :let g:netrw_regex_url = '\%(\%(http\|ftp\|irc\)s\?\|file\)://\S\{-}' +< Associated setting variables: |g:netrw_gx| control how gx picks up the text under the cursor |g:netrw_nogx| prevent gx map while editing |g:netrw_suppress_gx_mesg| controls gx's suppression of browser messages +OPENING FILES AND LAUNCHING APPS *netrw-gx* *:Open* *:Launch* {{{2 + +Netrw determines which special handler by the following method: + + * if |g:netrw_browsex_viewer| exists, then it will be used to attempt to + view files. Examples of useful settings (place into your <.vimrc>): + If the viewer you wish to use does not support handling of a remote URL + directory, set |g:netrw_browsex_support_remote| to 0. + * otherwise: + + * for Windows : explorer.exe is used + * for Mac OS X : open is used. + * for Linux : xdg-open is used. + +To open a path (or URL) by the appropriate handler, type > + + :Open +< +No escaping, neither for the shell nor for Vim's command-line, is needed. + +To launch a specific application , often being > + + :Launch . + +Since can be arbitrarily complex, in particular contain many file +paths, the escaping is left to the user. + +If you disabled the netrw plugin by setting g:loaded_netrwPlugin (see +|netrw-noload|), then you can use > + + :call netrw#Launch(' ') + :call netrw#Open('') +< *netrw-curdir* DELETING BOOKMARKS *netrw-mB* {{{2 diff --git a/runtime/plugin/netrwPlugin.vim b/runtime/plugin/netrwPlugin.vim index c70e6518ff..775b650e71 100644 --- a/runtime/plugin/netrwPlugin.vim +++ b/runtime/plugin/netrwPlugin.vim @@ -1,9 +1,12 @@ " netrwPlugin.vim: Handles file transfer and remote directory listing across a network " PLUGIN SECTION " Maintainer: This runtime file is looking for a new maintainer. -" Date: Feb 09, 2021 +" Date: Sep 09, 2021 " Last Change: " 2024 May 08 by Vim Project: cleanup legacy Win9X checks +" 2024 Oct 27 by Vim Project: cleanup gx mapping +" 2024 Oct 28 by Vim Project: further improvements +" 2024 Oct 31 by Vim Project: use autoloaded functions " Former Maintainer: Charles E Campbell " GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim " Copyright: Copyright (C) 1999-2021 Charles E. Campbell {{{1 @@ -31,6 +34,10 @@ set cpo&vim " --------------------------------------------------------------------- " Public Interface: {{{1 +" Commands Launch/URL {{{2 +command -complete=shellcmd -nargs=1 Launch call netrw#Launch(trim()) +command -complete=file -nargs=1 Open call netrw#Open(trim()) +" " }}} " Local Browsing Autocmds: {{{2 augroup FileExplorer au!