From c729d6d154e097b439ff264b9736604824f4a5f4 Mon Sep 17 00:00:00 2001 From: Luca Saccarola Date: Sat, 25 Jan 2025 16:07:12 +0100 Subject: [PATCH] runtime: decouple Open and Launch commands and gx mapping from netrw closes: #16494 fixes: ##16486 Signed-off-by: Christian Brabandt --- runtime/autoload/dist/vim9.vim | 127 +++++++++++++++-- runtime/doc/eval.txt | 37 ++++- runtime/doc/index.txt | 2 - runtime/doc/tags | 14 +- runtime/doc/version9.txt | 4 +- runtime/pack/dist/opt/netrw/doc/netrw.txt | 129 +----------------- .../dist/opt/netrw/plugin/netrwPlugin.vim | 21 --- runtime/plugin/openPlugin.vim | 24 ++++ 8 files changed, 192 insertions(+), 166 deletions(-) create mode 100644 runtime/plugin/openPlugin.vim diff --git a/runtime/autoload/dist/vim9.vim b/runtime/autoload/dist/vim9.vim index 2277f24533..a2b1451b02 100644 --- a/runtime/autoload/dist/vim9.vim +++ b/runtime/autoload/dist/vim9.vim @@ -2,19 +2,128 @@ vim9script # Vim runtime support library # -# Maintainer: The Vim Project -# Last Change: 2023 Oct 25 +# Maintainer: The Vim Project +# Last Change: 2025 Jan 24 export def IsSafeExecutable(filetype: string, executable: string): bool - if empty(exepath(executable)) - return v:false + if empty(exepath(executable)) + return v:false + endif + var cwd = getcwd() + return get(g:, filetype .. '_exec', get(g:, 'plugin_exec', 0)) + && (fnamemodify(exepath(executable), ':p:h') !=# cwd + || (split($PATH, has('win32') ? ';' : ':')->index(cwd) != -1 + && cwd != '.')) +enddef + +def Redir(): string + if get(g:, 'netrw_suppress_gx_mesg', true) + if &srr =~# "%s" + return printf(&srr, has("win32") ? "nul" : "/dev/null") + elseif &srr =~# '>&\?$' + return &srr .. (has("win32") ? "nul" : "/dev/null") + else + return &srr .. (has("win32") ? "> nul" : "> /dev/null") endif - var cwd = getcwd() - return get(g:, filetype .. '_exec', get(g:, 'plugin_exec', 0)) - && (fnamemodify(exepath(executable), ':p:h') !=# cwd - || (split($PATH, has('win32') ? ';' : ':')->index(cwd) != -1 - && cwd != '.')) + endif + return '' +enddef + +if has('unix') + if has('win32unix') + # Cygwin provides cygstart + if executable('cygstart') + export def Launch(args: string) + execute 'silent ! cygstart --hide' args Redir() | redraw! + enddef + 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/ + export def Launch(args: string) + execute 'silent !start "" //b' args Redir() | redraw! + enddef + else + # imitate /usr/bin/start script for other environments and hope for the best + export def Launch(args: string) + execute 'silent !cmd //c start "" //b' args Redir() | redraw! + enddef + endif + elseif exists('$WSL_DISTRO_NAME') # use cmd.exe to start GUI apps in WSL + export def Launch(args: string) + execute 'silent !' .. + ((args =~? '\v<\f+\.(exe|com|bat|cmd)>') ? + $'cmd.exe /c start /b {args} {Redir()}' : + $'nohup {args} {Redir()} &') + | redraw! + enddef + else + export def Launch(args: string) + execute ':silent ! nohup' args Redir() (has('gui_running') ? '' : '&') | redraw! + enddef + endif +elseif has('win32') + export def Launch(args: string) + execute 'silent !' .. (&shell =~? '\' ? '' : 'cmd.exe /c') + 'start "" /b' args Redir() | redraw! + enddef +else + export def Launch(dummy: string) + echom 'No common launcher found' + enddef +endif + +var os_viewer = null_string +# Git Bash +if has('win32unix') + # (cyg)start suffices + os_viewer = '' +# Windows / WSL +elseif executable('explorer.exe') + os_viewer = 'explorer.exe' +# Linux / BSD +elseif executable('xdg-open') + os_viewer = 'xdg-open' +# MacOS +elseif executable('open') + os_viewer = 'open' +endif + +def Viewer(): string + # g:Openprg could be a string of program + its arguments, test if first + # argument is executable + var user_viewer = get(g:, "Openprg", get(g:, "netrw_browsex_viewer", "")) + + # Take care of an off-by-one check for "for" too + if executable(trim(user_viewer)) + return user_viewer + endif + + var args = split(user_viewer, '\s\+\zs') + var viewer = get(args, 0, '') + + for arg in args[1 :] + if executable(trim(viewer)) + return user_viewer + endif + + viewer ..= arg + endfor + + if os_viewer == null + echoerr "No program to open this path found. See :help Open for more information." + endif + + return os_viewer +enddef + +export def Open(file: string) + Launch($"{Viewer()} {shellescape(file, 1)}") enddef # Uncomment this line to check for compilation errors early # defcompile + +# vim: ts=8 sts=2 sw=2 et diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 2348cdddb1..094b650c7b 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 9.1. Last change: 2025 Jan 02 +*eval.txt* For Vim version 9.1. Last change: 2025 Jan 25 VIM REFERENCE MANUAL by Bram Moolenaar @@ -4907,5 +4907,40 @@ executable. It takes the following arguments: filetype string executable string + *dist#vim9#Open()* *:Open* + *g:Openprg* +dist#vim9#Open(file: string) ~ + +Opens `path` with the system default handler (macOS `open`, Windows +`explorer.exe`, Linux `xdg-open`, …). If the variable |g:Openprg| exists the +string specified in the variable is used instead. + +NOTE: Escaping of the path is automatically applied. + +Usage: >vim + :call dist#vim9#Open() + :Open +< + + *dist#vim9#Launch()* *:Launch* +dist#vim9#Launch(file: string) ~ + +Launches with the appropriate system programs. Intended for launching +GUI programs within Vim. + +NOTE: escaping of is left to the user + +Examples: >vim + vim9script + + import autoload 'dist/vim9.vim' + # Execute 'makeprg' into another xterm window + vim9.Launch('xterm ' .. expandcmd(&makeprg)) +< + +Usage: >vim + :call dist#vim9#Launch() + :Launch . +< vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 079f9ea390..06a24e0465 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -803,8 +803,6 @@ tag char note action in Normal mode ~ |gu| gu{motion} 2 make Nmove text lowercase |gv| gv reselect the previous Visual area |gw| gw{motion} 2 format Nmove text and keep cursor -|netrw-gx| gx execute application for file name under the - cursor (only with |netrw| plugin) |g@| g@{motion} call 'operatorfunc' |g~| g~{motion} 2 swap case for Nmove text |g| g 1 same as "gj" diff --git a/runtime/doc/tags b/runtime/doc/tags index 0b9253203b..e667a33d59 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -2133,7 +2133,7 @@ $quote eval.txt /*$quote* :LP pi_logipat.txt /*:LP* :LPE pi_logipat.txt /*:LPE* :LPF pi_logipat.txt /*:LPF* -:Launch pi_netrw.txt /*:Launch* +:Launch eval.txt /*:Launch* :Lexplore pi_netrw.txt /*:Lexplore* :Lfilter quickfix.txt /*:Lfilter* :LogiPat pi_logipat.txt /*:LogiPat* @@ -2151,7 +2151,7 @@ $quote eval.txt /*$quote* :Ntree pi_netrw.txt /*:Ntree* :Nw pi_netrw.txt /*:Nw* :Nwrite pi_netrw.txt /*:Nwrite* -:Open pi_netrw.txt /*:Open* +:Open eval.txt /*:Open* :Over terminal.txt /*:Over* :P various.txt /*:P* :Pexplore pi_netrw.txt /*:Pexplore* @@ -6879,6 +6879,8 @@ disable-menus gui.txt /*disable-menus* discard editing.txt /*discard* dist#vim eval.txt /*dist#vim* dist#vim9 eval.txt /*dist#vim9* +dist#vim9#Launch() eval.txt /*dist#vim9#Launch()* +dist#vim9#Open() eval.txt /*dist#vim9#Open()* distribute-script usr_51.txt /*distribute-script* distributed-plugins usr_05.txt /*distributed-plugins* distribution intro.txt /*distribution* @@ -7527,6 +7529,7 @@ g:NetrwTopLvlMenu pi_netrw.txt /*g:NetrwTopLvlMenu* g:Netrw_UserMaps pi_netrw.txt /*g:Netrw_UserMaps* g:Netrw_corehandler pi_netrw.txt /*g:Netrw_corehandler* g:Netrw_funcref pi_netrw.txt /*g:Netrw_funcref* +g:Openprg eval.txt /*g:Openprg* g:actual_curbuf options.txt /*g:actual_curbuf* g:actual_curwin options.txt /*g:actual_curwin* g:ada#Comment ft_ada.txt /*g:ada#Comment* @@ -7630,8 +7633,6 @@ g:netrw_altv pi_netrw.txt /*g:netrw_altv* g:netrw_banner pi_netrw.txt /*g:netrw_banner* g:netrw_bannerbackslash pi_netrw.txt /*g:netrw_bannerbackslash* g:netrw_browse_split pi_netrw.txt /*g:netrw_browse_split* -g:netrw_browsex_support_remote pi_netrw.txt /*g:netrw_browsex_support_remote* -g:netrw_browsex_viewer pi_netrw.txt /*g:netrw_browsex_viewer* g:netrw_bufsettings pi_netrw.txt /*g:netrw_bufsettings* g:netrw_chgperm pi_netrw.txt /*g:netrw_chgperm* g:netrw_chgwin pi_netrw.txt /*g:netrw_chgwin* @@ -7660,7 +7661,6 @@ g:netrw_ftp_timelist_cmd pi_netrw.txt /*g:netrw_ftp_timelist_cmd* g:netrw_ftpextracmd pi_netrw.txt /*g:netrw_ftpextracmd* g:netrw_ftpmode pi_netrw.txt /*g:netrw_ftpmode* g:netrw_glob_escape pi_netrw.txt /*g:netrw_glob_escape* -g:netrw_gx pi_netrw.txt /*g:netrw_gx* g:netrw_hide pi_netrw.txt /*g:netrw_hide* g:netrw_home pi_netrw.txt /*g:netrw_home* g:netrw_http_cmd pi_netrw.txt /*g:netrw_http_cmd* @@ -7686,7 +7686,6 @@ g:netrw_menu pi_netrw.txt /*g:netrw_menu* g:netrw_mkdir_cmd pi_netrw.txt /*g:netrw_mkdir_cmd* g:netrw_mousemaps pi_netrw.txt /*g:netrw_mousemaps* g:netrw_nobeval pi_netrw.txt /*g:netrw_nobeval* -g:netrw_nogx pi_netrw.txt /*g:netrw_nogx* g:netrw_preview pi_netrw.txt /*g:netrw_preview* g:netrw_rcp_cmd pi_netrw.txt /*g:netrw_rcp_cmd* g:netrw_remote_mkdir pi_netrw.txt /*g:netrw_remote_mkdir* @@ -9031,8 +9030,6 @@ netrw-gitignore pi_netrw.txt /*netrw-gitignore* netrw-gn pi_netrw.txt /*netrw-gn* netrw-gp pi_netrw.txt /*netrw-gp* netrw-grep pi_netrw.txt /*netrw-grep* -netrw-gx pi_netrw.txt /*netrw-gx* -netrw-handler pi_netrw.txt /*netrw-handler* netrw-help pi_netrw.txt /*netrw-help* netrw-hexplore pi_netrw.txt /*netrw-hexplore* netrw-hide pi_netrw.txt /*netrw-hide* @@ -9161,7 +9158,6 @@ netrw-vexplore pi_netrw.txt /*netrw-vexplore* netrw-windows-netrc pi_netrw.txt /*netrw-windows-netrc* netrw-windows-s pi_netrw.txt /*netrw-windows-s* netrw-write pi_netrw.txt /*netrw-write* -netrw-x pi_netrw.txt /*netrw-x* netrw-xfer pi_netrw.txt /*netrw-xfer* netrw.txt pi_netrw.txt /*netrw.txt* netrw.vim pi_netrw.txt /*netrw.vim* diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 2f2ab5ae07..f77499aeba 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.1. Last change: 2025 Jan 23 +*version9.txt* For Vim version 9.1. Last change: 2025 Jan 25 VIM REFERENCE MANUAL by Bram Moolenaar @@ -41626,6 +41626,8 @@ Changed~ - |v:stacktrace| The stack trace of the exception most recently caught and not finished - New option value "nosort" for 'completeopt' +- add |dist#vim9#Launch()| and |dist#vim9#Open()| to the |vim-script-library| + and decouple it from |netrw| *added-9.2* Added ~ diff --git a/runtime/pack/dist/opt/netrw/doc/netrw.txt b/runtime/pack/dist/opt/netrw/doc/netrw.txt index cd5777ec9d..4afe538191 100644 --- a/runtime/pack/dist/opt/netrw/doc/netrw.txt +++ b/runtime/pack/dist/opt/netrw/doc/netrw.txt @@ -403,9 +403,6 @@ settings are described below, in |netrw-browser-options|, and in *g:netrw_menu* =0 disable netrw's menu =1 (default) netrw's menu enabled - *g:netrw_nogx* if this variable exists, then the "gx" map will not - be available (see |netrw-gx|) - *g:netrw_uid* (ftp) user-id, retained on a per-vim-session basis *s:netrw_passwd* (ftp) password, retained on a per-vim-session basis @@ -1113,7 +1110,7 @@ QUICK REFERENCE: MAPS *netrw-browse-maps* {{{2 U Change to subsequently-visited directory |netrw-U| v Enter the file/directory under the cursor in a new |netrw-v| browser window. A vertical split is used. - x View file with an associated program |netrw-x| + x View file with an associated program. (see |:Open|) X Execute filename under cursor via |system()| |netrw-X| % Open a new file in netrw's current directory |netrw-%| @@ -1466,106 +1463,6 @@ 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. -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: - - * 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. - -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 "", ""). 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 - return '' - finally - call winrestview(save_view) - 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 - return '' - finally - call winrestview(save_view) - 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. - 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 @@ -2585,14 +2482,6 @@ your browsing preferences. (see also: |netrw-settings|) |netrw-C| |netrw-cr| |netrw-ctrl-r| - *g:netrw_browsex_viewer* specify user's preference for a viewer: > - "kfmclient exec" - "gnome-open" -< - *g:netrw_browsex_support_remote* - specify if the specified viewer supports a - remote URL. (see |netrw-handler|). - *g:netrw_chgperm* Unix/Linux: "chmod PERM FILENAME" Windows: "cacls FILENAME /e /p PERM" Used to change access permission for a file. @@ -2615,12 +2504,11 @@ your browsing preferences. (see also: |netrw-settings|) *g:Netrw_corehandler* Allows one to specify something additional to do when handling files via netrw's - browser's "x" command (see |netrw-x|). If - present, g:Netrw_corehandler specifies - either one or more function references - (see |Funcref|). (the capital g:Netrw... - is required its holding a function reference) - + browser's "x" command. If present, + g:Netrw_corehandler specifies either one or + more function references (see |Funcref|). + (the capital g:Netrw... is required its + holding a function reference) *g:netrw_ctags* ="ctags" The default external program used to create @@ -2769,11 +2657,6 @@ your browsing preferences. (see also: |netrw-settings|) These characters in directory names are escaped before applying glob() - *g:netrw_gx* ="" - This option controls how gx (|netrw-gx|) picks - up the text under the cursor. See |expand()| - for possibilities. - *g:netrw_hide* Controlled by the "a" map (see |netrw-a|) =0 : show all =1 : show not-hidden files diff --git a/runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim b/runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim index 8d10c00153..388a7f2ba3 100644 --- a/runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim +++ b/runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim @@ -20,12 +20,6 @@ let g:loaded_netrwPlugin = "v175" let s:keepcpo = &cpo set cpo&vim -" Commands Launch/URL: {{{ - -command -complete=shellcmd -nargs=1 Launch call netrw#Launch(trim()) -command -complete=file -nargs=1 Open call netrw#Open(trim()) - -" }}} " Local Browsing Autocmds: {{{ augroup FileExplorer @@ -85,21 +79,6 @@ command! -bang NetrwClean call netrw#Clean(0) " }}} " Maps: {{{ -if !exists("g:netrw_nogx") - if maparg('gx','n') == "" - if !hasmapto('NetrwBrowseX') - nmap gx NetrwBrowseX - endif - nno NetrwBrowseX :call netrw#BrowseX(netrw#GX(),netrw#CheckIfRemote(netrw#GX())) - endif - if maparg('gx','x') == "" - if !hasmapto('NetrwBrowseXVis') - xmap gx NetrwBrowseXVis - endif - xno NetrwBrowseXVis :call netrw#BrowseXVis() - endif -endif - if exists("g:netrw_usetab") && g:netrw_usetab if maparg('','n') == "" nmap NetrwShrink diff --git a/runtime/plugin/openPlugin.vim b/runtime/plugin/openPlugin.vim new file mode 100644 index 0000000000..103b33cd69 --- /dev/null +++ b/runtime/plugin/openPlugin.vim @@ -0,0 +1,24 @@ +vim9script + +# Vim runtime support library +# +# Maintainer: The Vim Project +# Last Change: 2025 Jan 24 + +import autoload 'dist/vim9.vim' + +command -complete=shellcmd -nargs=1 Launch vim9.Launch(trim()) +command -complete=file -nargs=1 Open vim9.Open(trim()) + +const no_gx = get(g:, "nogx", get(g:, "netrw_nogx", false)) +if !no_gx + if maparg('gx', 'n') == "" + const file = get(g:, 'netrw_gx', '') + nnoremap gx vim9.Open(expand(file)) + endif + if maparg('gx', 'x') == "" + xnoremap gx vim9.Open(getregion(getpos('v'), getpos('.'), { type: mode() })->join()) + endif +endif + +# vim: ts=8 sts=2 sw=2 et