Skip to content

Commit

Permalink
runtime(netrw): simplify gx file handling
Browse files Browse the repository at this point in the history
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: vim#15721

Co-authored-by: Luca Saccarola <[email protected]>
Signed-off-by: Konfekt <[email protected]>
Signed-off-by: Christian Brabandt <[email protected]>
  • Loading branch information
2 people authored and chrisbra committed Oct 27, 2024
1 parent a04003a commit 3d7e567
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 126 deletions.
136 changes: 37 additions & 99 deletions runtime/autoload/netrw.vim
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
" 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)
" }}}
" Former Maintainer: Charles E Campbell
" GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim
Expand Down Expand Up @@ -538,7 +539,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
Expand Down Expand Up @@ -5406,25 +5406,6 @@ fun! netrw#BrowseX(fname,remote)
" call Decho("fname<".fname.">",'~'.expand("<slnum>"))
" call Decho("exten<".exten."> "."netrwFileHandlers#NFH_".exten."():exists=".exists("*netrwFileHandlers#NFH_".exten),'~'.expand("<slnum>"))

" 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("<slnum>"))

" 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("<slnum>"))
Expand All @@ -5447,86 +5428,14 @@ fun! netrw#BrowseX(fname,remote)
" call Decho("viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("<slnum>"))
endif

" execute the file handler
" call Decho("execute the file handler (if any)",'~'.expand("<slnum>"))
if exists("g:netrw_browsex_viewer") && executable(viewer)
" call Decho("(netrw#BrowseX) g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>"))
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("<slnum>"))
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("<slnum>"))
if executable("start")
" call Decho("(netrw#BrowseX) win32unix+start",'~'.expand("<slnum>"))
call s:NetrwExe('sil !start rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(winfname,1))
elseif executable("rundll32")
" call Decho("(netrw#BrowseX) win32unix+rundll32",'~'.expand("<slnum>"))
call s:NetrwExe('sil !rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(winfname,1))
elseif executable("cygstart")
" call Decho("(netrw#BrowseX) win32unix+cygstart",'~'.expand("<slnum>"))
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("<slnum>"))
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("<slnum>"))
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("<slnum>"))
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("<slnum>"))
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("<slnum>"))
call s:NetrwExe("sil !open ".s:ShellEscape(fname,1)." ".redir)
let ret= v:shell_error
exe 'Launch' viewer viewopt shellescape(fname, 1)
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)
" though shellescape(..., 1) is used in Open, it's insufficient
exe 'Open' escape(fname, '#%')
endif

" restoring redraw! after external file handlers
redraw!

" cleanup: remove temporary file,
" delete current buffer if success with handler,
" return to prior buffer (directory listing)
Expand Down Expand Up @@ -5563,12 +5472,37 @@ fun! netrw#GX()
if &ft == "netrw"
let fname= s:NetrwGetWord()
else
let fname= expand((exists("g:netrw_gx")? g:netrw_gx : '<cfile>'))
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("<cWORD>"), '\<' .. 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("<cfile>")
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()
Expand Down Expand Up @@ -6733,6 +6667,7 @@ fun! s:NetrwMaps(islocal)
nnoremap <buffer> <silent> <nowait> U :<c-u>call <SID>NetrwBookHistHandler(5,b:netrw_curdir)<cr>
nnoremap <buffer> <silent> <nowait> v :call <SID>NetrwSplit(2)<cr>
nnoremap <buffer> <silent> <nowait> x :<c-u>call netrw#BrowseX(<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord()),1)<cr>
nmap <buffer> <nowait> gx x
if !hasmapto('<Plug>NetrwHideEdit')
nmap <buffer> <c-h> <Plug>NetrwHideEdit
endif
Expand Down Expand Up @@ -12156,13 +12091,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("<slnum>"))
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("<slnum>"))
exe a:cmd
Expand Down
103 changes: 77 additions & 26 deletions runtime/doc/pi_netrw.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*pi_netrw.txt* For Vim version 9.1. Last change: 2024 Oct 21
*pi_netrw.txt* For Vim version 9.1. Last change: 2024 Oct 27

------------------------------------------------
NETRW REFERENCE MANUAL by Charles E. Campbell
Expand All @@ -8,7 +8,7 @@ Author: Charles E. Campbell <[email protected]>

Copyright: Copyright (C) 2017 Charles E Campbell *netrw-copyright*
The VIM LICENSE applies to the files in this package, including
netrw.vim, pi_netrw.txt, netrwFileHandlers.vim, netrwSettings.vim, and
netrw.vim, pi_netrw.txt, netrwSettings.vim, and
syntax/netrw.vim. Like anything else that's free, netrw.vim and its
associated files are provided *as is* and comes with no warranty of
any kind, either expressed or implied. No guarantees of
Expand Down Expand Up @@ -1469,42 +1469,93 @@ 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*
*netrw-gx* *Open* *Launch*
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 <path>

Netrw determines which special handler by the following method:
One may also use visual mode (see |visual-start|) to select the text that 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 "<cword>", "<cWORD>"). Note that
expand("<cfile>") 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.

* if |g:netrw_browsex_viewer| exists, then it will be used to attempt to
view files. Examples of useful settings (place into your <.vimrc>): >
The selection function can be adapted for each filetype by adding a function
Netrw_get_URL_<filetype>, where <filetype> is given by &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 <a href='http://www.python.org'>Python is here</a>
" <a href="http://www.python.org"/>
try
let save_view = winsaveview()
if searchpair('<a\s\+href=', '', '\%(</a>\|/>\)\zs', 'cbW', '', line('.')) > 0
return matchstr(getline('.')[col('.') - 1 : ],
\ 'href=["'.."'"..']\?\zs\S\{-}\ze["'.."'"..']\?/\?>')
endif
finally
call winrestview(save_view)
return ''
endtry
endfunction
<

:let g:netrw_browsex_viewer= "kfmclient exec"
< or >
:let g:netrw_browsex_viewer= "xdg-open"
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:
>
g:netrw_regex_url = '\%(\%(http\|ftp\|irc\)s\?\|file\)://\S\{-}'
<

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.
* 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.
* otherwise:

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("<cfile>") to pick up the
text under the cursor; one may change what |expand()| uses via the
|g:netrw_gx| variable (options include "<cword>", "<cWORD>"). Note that
expand("<cfile>") 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.
* for Windows : explorer.exe is used
* for Mac OS X : open is used.
* for Linux : xdg-open is used.

To open a file <filepath> by the appropriate handler, type

:Open <filepath>

No escaping, neither for the shell, nor for Vim's command-line is needed.

To launch a specific application <app> <args>, often <args> being <filepath>,

:Launch <app> <args>.

Since <args> can be arbitrarily complex, in particular contain many file
paths, the escaping is left to the user.

Associated setting variables:
|g:netrw_gx| control how gx picks up the text under the cursor
Expand Down
2 changes: 2 additions & 0 deletions runtime/doc/tags
Original file line number Diff line number Diff line change
Expand Up @@ -5566,6 +5566,7 @@ KeyInputPre autocmd.txt /*KeyInputPre*
Kibaale uganda.txt /*Kibaale*
Korean mbyte.txt /*Korean*
L motion.txt /*L*
Launch pi_netrw.txt /*Launch*
Linux-backspace options.txt /*Linux-backspace*
List eval.txt /*List*
Lists eval.txt /*Lists*
Expand Down Expand Up @@ -5621,6 +5622,7 @@ OS390-open-source os_390.txt /*OS390-open-source*
Object vim9class.txt /*Object*
OffTheSpot mbyte.txt /*OffTheSpot*
OnTheSpot mbyte.txt /*OnTheSpot*
Open pi_netrw.txt /*Open*
Operator-pending intro.txt /*Operator-pending*
Operator-pending-mode intro.txt /*Operator-pending-mode*
OptionSet autocmd.txt /*OptionSet*
Expand Down
Loading

0 comments on commit 3d7e567

Please sign in to comment.