Skip to content

Commit 3d7e567

Browse files
Konfektsaccarosium
authored andcommitted
runtime(netrw): simplify gx file handling
It did not work very well, at least on Debian 12, and I am not sure Git Bash and WSL, for example, were taken care of as maintenance stalled. The whole logic was somewhat convoluted with some parts repeatedly invoking failed commands. The file handling was outdated, for example, nowadays Netscape is rarely used, and also opinionated, for example mainly Microsoft Paint and Gimp for Image files. Instead, let's use (xdg-)open and similar commands on other systems which respects the user's preferences. closes: #15721 Co-authored-by: Luca Saccarola <96259932+saccarosium@users.noreply.github.com> Signed-off-by: Konfekt <Konfekt@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
1 parent a04003a commit 3d7e567

4 files changed

Lines changed: 199 additions & 126 deletions

File tree

runtime/autoload/netrw.vim

Lines changed: 37 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
" 2024 Sep 19 by Vim Project: mf-selection highlight uses wrong pattern (#15700)
2828
" 2024 Sep 21 by Vim Project: remove extraneous closing bracket (#15718)
2929
" 2024 Oct 21 by Vim Project: remove netrwFileHandlers (#15895)
30+
" 2024 Oct 27 by Vim Project: clean up gx mapping (#15721)
3031
" }}}
3132
" Former Maintainer: Charles E Campbell
3233
" GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim
@@ -538,7 +539,6 @@ if !exists("g:netrw_sort_sequence")
538539
endif
539540
call s:NetrwInit("g:netrw_special_syntax" , 0)
540541
call s:NetrwInit("g:netrw_ssh_browse_reject", '^total\s\+\d\+$')
541-
call s:NetrwInit("g:netrw_suppress_gx_mesg", 1)
542542
call s:NetrwInit("g:netrw_use_noswf" , 1)
543543
call s:NetrwInit("g:netrw_sizestyle" ,"b")
544544
" Default values - t-w ---------- {{{3
@@ -5406,25 +5406,6 @@ fun! netrw#BrowseX(fname,remote)
54065406
" call Decho("fname<".fname.">",'~'.expand("<slnum>"))
54075407
" call Decho("exten<".exten."> "."netrwFileHandlers#NFH_".exten."():exists=".exists("*netrwFileHandlers#NFH_".exten),'~'.expand("<slnum>"))
54085408

5409-
" set up redirection (avoids browser messages)
5410-
" by default, g:netrw_suppress_gx_mesg is true
5411-
if g:netrw_suppress_gx_mesg
5412-
if &srr =~ "%s"
5413-
if has("win32")
5414-
let redir= substitute(&srr,"%s","nul","")
5415-
else
5416-
let redir= substitute(&srr,"%s","/dev/null","")
5417-
endif
5418-
elseif has("win32")
5419-
let redir= &srr . "nul"
5420-
else
5421-
let redir= &srr . "/dev/null"
5422-
endif
5423-
else
5424-
let redir= ""
5425-
endif
5426-
" call Decho("set up redirection: redir{".redir."} srr{".&srr."}",'~'.expand("<slnum>"))
5427-
54285409
" extract any viewing options. Assumes that they're set apart by spaces.
54295410
if exists("g:netrw_browsex_viewer")
54305411
" call Decho("extract any viewing options from g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>"))
@@ -5447,86 +5428,14 @@ fun! netrw#BrowseX(fname,remote)
54475428
" call Decho("viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("<slnum>"))
54485429
endif
54495430

5450-
" execute the file handler
5451-
" call Decho("execute the file handler (if any)",'~'.expand("<slnum>"))
54525431
if exists("g:netrw_browsex_viewer") && executable(viewer)
54535432
" call Decho("(netrw#BrowseX) g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>"))
5454-
call s:NetrwExe("sil !".viewer." ".viewopt.s:ShellEscape(fname,1).redir)
5455-
let ret= v:shell_error
5456-
5457-
elseif has("win32")
5458-
" call Decho("(netrw#BrowseX) win".(has("win32")? "32" : "64"),'~'.expand("<slnum>"))
5459-
if executable("start")
5460-
call s:NetrwExe('sil! !start rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(fname,1))
5461-
elseif executable("rundll32")
5462-
call s:NetrwExe('sil! !rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(fname,1))
5463-
else
5464-
call netrw#ErrorMsg(s:WARNING,"rundll32 not on path",74)
5465-
endif
5466-
let ret= v:shell_error
5467-
5468-
elseif has("win32unix")
5469-
let winfname= 'c:\cygwin'.substitute(fname,'/','\\','g')
5470-
" call Decho("(netrw#BrowseX) cygwin: winfname<".s:ShellEscape(winfname,1).">",'~'.expand("<slnum>"))
5471-
if executable("start")
5472-
" call Decho("(netrw#BrowseX) win32unix+start",'~'.expand("<slnum>"))
5473-
call s:NetrwExe('sil !start rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(winfname,1))
5474-
elseif executable("rundll32")
5475-
" call Decho("(netrw#BrowseX) win32unix+rundll32",'~'.expand("<slnum>"))
5476-
call s:NetrwExe('sil !rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(winfname,1))
5477-
elseif executable("cygstart")
5478-
" call Decho("(netrw#BrowseX) win32unix+cygstart",'~'.expand("<slnum>"))
5479-
call s:NetrwExe('sil !cygstart '.s:ShellEscape(fname,1))
5480-
else
5481-
call netrw#ErrorMsg(s:WARNING,"rundll32 not on path",74)
5482-
endif
5483-
let ret= v:shell_error
5484-
5485-
elseif has("unix") && $DESKTOP_SESSION == "mate" && executable("atril")
5486-
" call Decho("(netrw#BrowseX) unix and atril",'~'.expand("<slnum>"))
5487-
if a:fname =~ '^https\=://'
5488-
" atril does not appear to understand how to handle html -- so use gvim to edit the document
5489-
let use_ctrlo= 0
5490-
" call Decho("fname<".fname.">")
5491-
" call Decho("a:fname<".a:fname.">")
5492-
call s:NetrwExe("sil! !gvim ".fname.' -c "keepj keepalt file '.fnameescape(a:fname).'"')
5493-
5494-
else
5495-
call s:NetrwExe("sil !atril ".s:ShellEscape(fname,1).redir)
5496-
endif
5497-
let ret= v:shell_error
5498-
5499-
elseif has("unix") && executable("kfmclient") && s:CheckIfKde()
5500-
" call Decho("(netrw#BrowseX) unix and kfmclient",'~'.expand("<slnum>"))
5501-
call s:NetrwExe("sil !kfmclient exec ".s:ShellEscape(fname,1)." ".redir)
5502-
let ret= v:shell_error
5503-
5504-
elseif has("unix") && executable("exo-open") && executable("xdg-open") && executable("setsid")
5505-
" call Decho("(netrw#BrowseX) unix, exo-open, xdg-open",'~'.expand("<slnum>"))
5506-
call s:NetrwExe("sil !setsid xdg-open ".s:ShellEscape(fname,1).redir.'&')
5507-
let ret= v:shell_error
5508-
5509-
elseif has("unix") && executable("xdg-open")
5510-
" call Decho("(netrw#BrowseX) unix and xdg-open",'~'.expand("<slnum>"))
5511-
call s:NetrwExe("sil !xdg-open ".s:ShellEscape(fname,1).redir.'&')
5512-
let ret= v:shell_error
5513-
5514-
elseif has("macunix") && executable("open")
5515-
" call Decho("(netrw#BrowseX) macunix and open",'~'.expand("<slnum>"))
5516-
call s:NetrwExe("sil !open ".s:ShellEscape(fname,1)." ".redir)
5517-
let ret= v:shell_error
5433+
exe 'Launch' viewer viewopt shellescape(fname, 1)
55185434
else
5519-
call netrw#ErrorMsg(s:ERROR, "Couldn't find a program to open '".a:fname."'", 0)
5520-
let ret=0
5521-
endif
5522-
5523-
if ret
5524-
call netrw#ErrorMsg(s:ERROR, "Failed to open '".a:fname."'", 0)
5435+
" though shellescape(..., 1) is used in Open, it's insufficient
5436+
exe 'Open' escape(fname, '#%')
55255437
endif
55265438

5527-
" restoring redraw! after external file handlers
5528-
redraw!
5529-
55305439
" cleanup: remove temporary file,
55315440
" delete current buffer if success with handler,
55325441
" return to prior buffer (directory listing)
@@ -5563,12 +5472,37 @@ fun! netrw#GX()
55635472
if &ft == "netrw"
55645473
let fname= s:NetrwGetWord()
55655474
else
5566-
let fname= expand((exists("g:netrw_gx")? g:netrw_gx : '<cfile>'))
5475+
let fname= exists("g:netrw_gx")? expand(g:netrw_gx) : s:GetURL()
55675476
endif
55685477
" call Dret("netrw#GX <".fname.">")
55695478
return fname
55705479
endfun
55715480

5481+
fun! s:GetURL() abort
5482+
let URL = ''
5483+
if exists('*Netrw_get_URL_' .. &filetype)
5484+
let URL = call('Netrw_get_URL_' .. &filetype, [])
5485+
endif
5486+
if !empty(URL) | return URL | endif
5487+
" URLs end in letter, digit or forward slash
5488+
let URL = matchstr(expand("<cWORD>"), '\<' .. g:netrw_regex_url .. '\ze[^A-Za-z0-9/]*$')
5489+
if !empty(URL) | return URL | endif
5490+
5491+
" Is it a file in the current work dir ...
5492+
let file = expand("<cfile>")
5493+
if filereadable(file) | return file | endif
5494+
" ... or in that of the current buffer?
5495+
let path = fnamemodify(expand('%'), ':p')
5496+
if isdirectory(path)
5497+
let dir = path
5498+
elseif filereadable(path)
5499+
let dir = fnamemodify(path, ':h')
5500+
endif
5501+
if exists('dir') && filereadable(dir..'/'..file) | return dir..'/'..file | endif
5502+
5503+
return ''
5504+
endf
5505+
55725506
" ---------------------------------------------------------------------
55735507
" netrw#BrowseXVis: used by gx in visual mode to select a file for browsing {{{2
55745508
fun! netrw#BrowseXVis()
@@ -6733,6 +6667,7 @@ fun! s:NetrwMaps(islocal)
67336667
nnoremap <buffer> <silent> <nowait> U :<c-u>call <SID>NetrwBookHistHandler(5,b:netrw_curdir)<cr>
67346668
nnoremap <buffer> <silent> <nowait> v :call <SID>NetrwSplit(2)<cr>
67356669
nnoremap <buffer> <silent> <nowait> x :<c-u>call netrw#BrowseX(<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord()),1)<cr>
6670+
nmap <buffer> <nowait> gx x
67366671
if !hasmapto('<Plug>NetrwHideEdit')
67376672
nmap <buffer> <c-h> <Plug>NetrwHideEdit
67386673
endif
@@ -12156,13 +12091,16 @@ endfun
1215612091
" s:NetrwExe: executes a string using "!" {{{2
1215712092
fun! s:NetrwExe(cmd)
1215812093
" call Dfunc("s:NetrwExe(a:cmd<".a:cmd.">)")
12159-
if has("win32") && &shell !~? 'cmd\|pwsh\|powershell' && !g:netrw_cygwin
12094+
if has("win32")
1216012095
" call Decho("using win32:",expand("<slnum>"))
1216112096
let savedShell=[&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash]
1216212097
set shell& shellcmdflag& shellxquote& shellxescape&
1216312098
set shellquote& shellpipe& shellredir& shellslash&
12164-
exe a:cmd
12165-
let [&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash] = savedShell
12099+
try
12100+
exe a:cmd
12101+
finally
12102+
let [&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash] = savedShell
12103+
endtry
1216612104
else
1216712105
" call Decho("exe ".a:cmd,'~'.expand("<slnum>"))
1216812106
exe a:cmd

runtime/doc/pi_netrw.txt

Lines changed: 77 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*pi_netrw.txt* For Vim version 9.1. Last change: 2024 Oct 21
1+
*pi_netrw.txt* For Vim version 9.1. Last change: 2024 Oct 27
22

33
------------------------------------------------
44
NETRW REFERENCE MANUAL by Charles E. Campbell
@@ -8,7 +8,7 @@ Author: Charles E. Campbell <NcampObell@SdrPchip.AorgM-NOSPAM>
88

99
Copyright: Copyright (C) 2017 Charles E Campbell *netrw-copyright*
1010
The VIM LICENSE applies to the files in this package, including
11-
netrw.vim, pi_netrw.txt, netrwFileHandlers.vim, netrwSettings.vim, and
11+
netrw.vim, pi_netrw.txt, netrwSettings.vim, and
1212
syntax/netrw.vim. Like anything else that's free, netrw.vim and its
1313
associated files are provided *as is* and comes with no warranty of
1414
any kind, either expressed or implied. No guarantees of
@@ -1469,42 +1469,93 @@ With either form of the command, netrw will first ask for confirmation
14691469
that the removal is in fact what you want to do. If netrw doesn't have
14701470
permission to remove a file, it will issue an error message.
14711471

1472-
*netrw-gx*
1472+
*netrw-gx* *Open* *Launch*
14731473
CUSTOMIZING BROWSING WITH A SPECIAL HANDLER *netrw-x* *netrw-handler* {{{2
14741474

14751475
Certain files, such as html, gif, jpeg, (word/office) doc, etc, files, are
14761476
best seen with a special handler (ie. a tool provided with your computer's
1477-
operating system). Netrw allows one to invoke such special handlers by: >
1477+
operating system). Netrw allows one to invoke such special handlers by:
14781478

1479-
* when Exploring, hit the "x" key
1480-
* when editing, hit gx with the cursor atop the special filename
1481-
< (latter not available if the |g:netrw_nogx| variable exists)
1479+
* hitting gx with the cursor atop the file path or alternatively x
1480+
in a netrw buffer; the former can be disabled by defining the
1481+
|g:netrw_nogx| variable
1482+
* when in command line, typing :Open <path>
14821483

1483-
Netrw determines which special handler by the following method:
1484+
One may also use visual mode (see |visual-start|) to select the text that the
1485+
special handler will use. Normally gx checks for a close-by URL or file name
1486+
to pick up the text under the cursor; one may change what |expand()| uses via the
1487+
|g:netrw_gx| variable (options include "<cword>", "<cWORD>"). Note that
1488+
expand("<cfile>") depends on the |'isfname'| setting. Alternatively, one may
1489+
select the text to be used by gx by making a visual selection (see
1490+
|visual-block|) and then pressing gx.
14841491

1485-
* if |g:netrw_browsex_viewer| exists, then it will be used to attempt to
1486-
view files. Examples of useful settings (place into your <.vimrc>): >
1492+
The selection function can be adapted for each filetype by adding a function
1493+
Netrw_get_URL_<filetype>, where <filetype> is given by &filetype.
1494+
The function should return the URL or file name to be used by gx, and will
1495+
fall back to the default behavior if it returns an empty string.
1496+
For example, special handlers for links Markdown and HTML are
1497+
>
1498+
" make gx work on concealed links regardless of exact cursor position
1499+
function Netrw_get_URL_markdown()
1500+
" markdown URL such as [link text](http://ya.ru 'yandex search')
1501+
try
1502+
let save_view = winsaveview()
1503+
if searchpair('\[.\{-}\](', '', ')\zs', 'cbW', '', line('.')) > 0
1504+
return matchstr(getline('.')[col('.')-1:], '\[.\{-}\](\zs' .. g:netrw_regex_url .. '\ze\(\s\+.\{-}\)\?)')
1505+
endif
1506+
finally
1507+
call winrestview(save_view)
1508+
return ''
1509+
endtry
1510+
endfunction
1511+
1512+
function Netrw_get_URL_html()
1513+
" HTML URL such as <a href='http://www.python.org'>Python is here</a>
1514+
" <a href="http://www.python.org"/>
1515+
try
1516+
let save_view = winsaveview()
1517+
if searchpair('<a\s\+href=', '', '\%(</a>\|/>\)\zs', 'cbW', '', line('.')) > 0
1518+
return matchstr(getline('.')[col('.') - 1 : ],
1519+
\ 'href=["'.."'"..']\?\zs\S\{-}\ze["'.."'"..']\?/\?>')
1520+
endif
1521+
finally
1522+
call winrestview(save_view)
1523+
return ''
1524+
endtry
1525+
endfunction
1526+
<
14871527

1488-
:let g:netrw_browsex_viewer= "kfmclient exec"
1489-
< or >
1490-
:let g:netrw_browsex_viewer= "xdg-open"
1528+
Other than a file path, the text under the cursor may be a URL. Netrw uses
1529+
by default the following regular expression to determine if the text under the
1530+
cursor is a URL:
1531+
>
1532+
g:netrw_regex_url = '\%(\%(http\|ftp\|irc\)s\?\|file\)://\S\{-}'
14911533
<
1534+
1535+
Netrw determines which special handler by the following method:
1536+
1537+
* if |g:netrw_browsex_viewer| exists, then it will be used to attempt to
1538+
view files. Examples of useful settings (place into your <.vimrc>):
14921539
If the viewer you wish to use does not support handling of a remote URL
14931540
directory, set |g:netrw_browsex_support_remote| to 0.
1494-
* for Windows 32 or 64, the URL and FileProtocolHandler dlls are used.
1495-
* for Gnome (with gnome-open): gnome-open is used.
1496-
* for KDE (with kfmclient) : kfmclient is used
1497-
* for Mac OS X : open is used.
1541+
* otherwise:
14981542

1499-
The gx mapping extends to all buffers; apply "gx" while atop a word and netrw
1500-
will apply a special handler to it (like "x" works when in a netrw buffer).
1501-
One may also use visual mode (see |visual-start|) to select the text that the
1502-
special handler will use. Normally gx uses expand("<cfile>") to pick up the
1503-
text under the cursor; one may change what |expand()| uses via the
1504-
|g:netrw_gx| variable (options include "<cword>", "<cWORD>"). Note that
1505-
expand("<cfile>") depends on the |'isfname'| setting. Alternatively, one may
1506-
select the text to be used by gx by making a visual selection (see
1507-
|visual-block|) and then pressing gx.
1543+
* for Windows : explorer.exe is used
1544+
* for Mac OS X : open is used.
1545+
* for Linux : xdg-open is used.
1546+
1547+
To open a file <filepath> by the appropriate handler, type
1548+
1549+
:Open <filepath>
1550+
1551+
No escaping, neither for the shell, nor for Vim's command-line is needed.
1552+
1553+
To launch a specific application <app> <args>, often <args> being <filepath>,
1554+
1555+
:Launch <app> <args>.
1556+
1557+
Since <args> can be arbitrarily complex, in particular contain many file
1558+
paths, the escaping is left to the user.
15081559

15091560
Associated setting variables:
15101561
|g:netrw_gx| control how gx picks up the text under the cursor

runtime/doc/tags

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5566,6 +5566,7 @@ KeyInputPre autocmd.txt /*KeyInputPre*
55665566
Kibaale uganda.txt /*Kibaale*
55675567
Korean mbyte.txt /*Korean*
55685568
L motion.txt /*L*
5569+
Launch pi_netrw.txt /*Launch*
55695570
Linux-backspace options.txt /*Linux-backspace*
55705571
List eval.txt /*List*
55715572
Lists eval.txt /*Lists*
@@ -5621,6 +5622,7 @@ OS390-open-source os_390.txt /*OS390-open-source*
56215622
Object vim9class.txt /*Object*
56225623
OffTheSpot mbyte.txt /*OffTheSpot*
56235624
OnTheSpot mbyte.txt /*OnTheSpot*
5625+
Open pi_netrw.txt /*Open*
56245626
Operator-pending intro.txt /*Operator-pending*
56255627
Operator-pending-mode intro.txt /*Operator-pending-mode*
56265628
OptionSet autocmd.txt /*OptionSet*

0 commit comments

Comments
 (0)