From 3f15e57b2614c7849c5bbc1f32c7bd90f2a917b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= Date: Wed, 26 Jun 2024 14:59:46 +0200 Subject: [PATCH] feat(vim.ui): configurable "gx" / vim.ui.open() tool Problem: User cannot configure the tool used by `vim.ui.open` (or `gx`). With netrw this was supported by `g:netrw_browsex_viewer`. Solution: Introduce `opts.cmd`. Users that want to set this globally can monkey-patch `vim.ui.open` in the same way described at `:help vim.paste()`. Fixes https://github.com/neovim/neovim/issues/29488 Co-authored-by: Justin M. Keyes --- runtime/doc/lua.txt | 6 +++++- runtime/doc/news.txt | 5 ++++- runtime/lua/vim/ui.lua | 23 ++++++++++++++--------- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 0beee1507a..1283aac5ca 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -2559,7 +2559,7 @@ vim.ui.input({opts}, {on_confirm}) *vim.ui.input()* typed (it might be an empty string if nothing was entered), or `nil` if the user aborted the dialog. -vim.ui.open({path}) *vim.ui.open()* +vim.ui.open({path}, {opt}) *vim.ui.open()* Opens `path` with the system default handler (macOS `open`, Windows `explorer.exe`, Linux `xdg-open`, …), or returns (but does not show) an error message on failure. @@ -2570,6 +2570,8 @@ vim.ui.open({path}) *vim.ui.open()* -- Asynchronous. vim.ui.open("https://neovim.io/") vim.ui.open("~/path/to/file") + -- Use the "osurl" command to handle the path or URL. + vim.ui.open("gh#neovim/neovim!29490", { cmd = { 'osurl' } }) -- Synchronous (wait until the process exits). local cmd, err = vim.ui.open("$VIMRUNTIME") if cmd then @@ -2579,6 +2581,8 @@ vim.ui.open({path}) *vim.ui.open()* Parameters: ~ • {path} (`string`) Path or URL to open + • {opt} (`{ cmd?: string[] }?`) Options + • cmd string[]|nil Command used to open the path or URL. Return (multiple): ~ (`vim.SystemObj?`) Command object, or nil if not found. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 5884659ab7..515aef753d 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -194,7 +194,10 @@ TUI UI -• TODO +• |vim.ui.open()| (by default bound to |gx|) accepts an `opt.cmd` parameter + which controls the tool used to open the given path or URL. If you want to + globally set this, you can override vim.ui.open using the same approach + described at |vim.paste()|. ============================================================================== CHANGED FEATURES *news-changed* diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua index 3617e1d702..532decf5e9 100644 --- a/runtime/lua/vim/ui.lua +++ b/runtime/lua/vim/ui.lua @@ -117,6 +117,8 @@ end --- -- Asynchronous. --- vim.ui.open("https://neovim.io/") --- vim.ui.open("~/path/to/file") +--- -- Use the "osurl" command to handle the path or URL. +--- vim.ui.open("gh#neovim/neovim!29490", { cmd = { 'osurl' } }) --- -- Synchronous (wait until the process exits). --- local cmd, err = vim.ui.open("$VIMRUNTIME") --- if cmd then @@ -125,12 +127,14 @@ end --- ``` --- ---@param path string Path or URL to open +---@param opt? { cmd?: string[] } Options +--- - cmd string[]|nil Command used to open the path or URL. --- ---@return vim.SystemObj|nil # Command object, or nil if not found. ---@return nil|string # Error message on failure, or nil on success. --- ---@see |vim.system()| -function M.open(path) +function M.open(path, opt) vim.validate({ path = { path, 'string' }, }) @@ -139,12 +143,13 @@ function M.open(path) path = vim.fs.normalize(path) end - local cmd --- @type string[] - local opts --- @type vim.SystemOpts + opt = opt or {} + local cmd ---@type string[] + local job_opt = { text = true, detach = true } --- @type vim.SystemOpts - opts = { text = true, detach = true } - - if vim.fn.has('mac') == 1 then + if opt.cmd then + cmd = vim.list_extend(opt.cmd --[[@as string[] ]], { path }) + elseif vim.fn.has('mac') == 1 then cmd = { 'open', path } elseif vim.fn.has('win32') == 1 then if vim.fn.executable('rundll32') == 1 then @@ -154,8 +159,8 @@ function M.open(path) end elseif vim.fn.executable('xdg-open') == 1 then cmd = { 'xdg-open', path } - opts.stdout = false - opts.stderr = false + job_opt.stdout = false + job_opt.stderr = false elseif vim.fn.executable('wslview') == 1 then cmd = { 'wslview', path } elseif vim.fn.executable('explorer.exe') == 1 then @@ -164,7 +169,7 @@ function M.open(path) return nil, 'vim.ui.open: no handler found (tried: wslview, explorer.exe, xdg-open)' end - return vim.system(cmd, opts), nil + return vim.system(cmd, job_opt), nil end --- Returns all URLs at cursor, if any.