mirror of
https://github.com/neovim/neovim.git
synced 2024-12-23 20:55:18 -07:00
fix(vim.ui): open() may wait indefinitely #28325
Problem: vim.ui.open "locks up" Nvim if the spawned process does not terminate. #27986 Solution: - Change `vim.ui.open()`: - Do not call `wait()`. - Return a `SystemObj`. The caller can decide if it wants to `wait()`. - Change `gx` to `wait()` only a short time. - Allows `gx` to show a message if the command fails, without the risk of waiting forever.
This commit is contained in:
parent
4ec8fd43bf
commit
57adf8c6e0
@ -1812,6 +1812,7 @@ vim.system({cmd}, {opts}, {on_exit}) *vim.system()*
|
||||
|
||||
Return: ~
|
||||
(`vim.SystemObj`) Object with the fields:
|
||||
• cmd (string[]) Command name and args
|
||||
• pid (integer) Process ID
|
||||
• wait (fun(timeout: integer|nil): SystemCompleted) Wait for the
|
||||
process to complete. Upon timeout the process is sent the KILL
|
||||
@ -2568,16 +2569,21 @@ vim.ui.open({path}) *vim.ui.open()*
|
||||
Expands "~/" and environment variables in filesystem paths.
|
||||
|
||||
Examples: >lua
|
||||
-- Asynchronous.
|
||||
vim.ui.open("https://neovim.io/")
|
||||
vim.ui.open("~/path/to/file")
|
||||
vim.ui.open("$VIMRUNTIME")
|
||||
-- Synchronous (wait until the process exits).
|
||||
local cmd, err = vim.ui.open("$VIMRUNTIME")
|
||||
if cmd then
|
||||
cmd:wait()
|
||||
end
|
||||
<
|
||||
|
||||
Parameters: ~
|
||||
• {path} (`string`) Path or URL to open
|
||||
|
||||
Return (multiple): ~
|
||||
(`vim.SystemCompleted?`) Command result, or nil if not found.
|
||||
(`vim.SystemObj?`) Command object, or nil if not found.
|
||||
(`string?`) Error message on failure
|
||||
|
||||
See also: ~
|
||||
|
@ -95,10 +95,19 @@ do
|
||||
{ silent = true, expr = true, desc = ':help v_@-default' }
|
||||
)
|
||||
|
||||
--- Map |gx| to call |vim.ui.open| on the identifier under the cursor
|
||||
--- Map |gx| to call |vim.ui.open| on the <cfile> at cursor.
|
||||
do
|
||||
local function do_open(uri)
|
||||
local _, err = vim.ui.open(uri)
|
||||
local cmd, err = vim.ui.open(uri)
|
||||
local rv = cmd and cmd:wait(1000) or nil
|
||||
if cmd and rv and rv.code ~= 0 then
|
||||
err = ('vim.ui.open: command %s (%d): %s'):format(
|
||||
(rv.code == 124 and 'timeout' or 'failed'),
|
||||
rv.code,
|
||||
vim.inspect(cmd.cmd)
|
||||
)
|
||||
end
|
||||
|
||||
if err then
|
||||
vim.notify(err, vim.log.levels.ERROR)
|
||||
end
|
||||
|
@ -121,6 +121,7 @@ vim.log = {
|
||||
--- asynchronously. Receives SystemCompleted object, see return of SystemObj:wait().
|
||||
---
|
||||
--- @return vim.SystemObj Object with the fields:
|
||||
--- - cmd (string[]) Command name and args
|
||||
--- - pid (integer) Process ID
|
||||
--- - wait (fun(timeout: integer|nil): SystemCompleted) Wait for the process to complete. Upon
|
||||
--- timeout the process is sent the KILL signal (9) and the exit code is set to 124. Cannot
|
||||
|
@ -18,6 +18,7 @@ local uv = vim.uv
|
||||
--- @field stderr? string
|
||||
|
||||
--- @class vim.SystemState
|
||||
--- @field cmd string[]
|
||||
--- @field handle? uv.uv_process_t
|
||||
--- @field timer? uv.uv_timer_t
|
||||
--- @field pid? integer
|
||||
@ -56,6 +57,7 @@ local function close_handles(state)
|
||||
end
|
||||
|
||||
--- @class vim.SystemObj
|
||||
--- @field cmd string[]
|
||||
--- @field pid integer
|
||||
--- @field private _state vim.SystemState
|
||||
--- @field wait fun(self: vim.SystemObj, timeout?: integer): vim.SystemCompleted
|
||||
@ -68,6 +70,7 @@ local SystemObj = {}
|
||||
--- @return vim.SystemObj
|
||||
local function new_systemobj(state)
|
||||
return setmetatable({
|
||||
cmd = state.cmd,
|
||||
pid = state.pid,
|
||||
_state = state,
|
||||
}, { __index = SystemObj })
|
||||
|
@ -615,7 +615,8 @@ M[ms.window_showDocument] = function(_, result, ctx, _)
|
||||
|
||||
if result.external then
|
||||
-- TODO(lvimuser): ask the user for confirmation
|
||||
local ret, err = vim.ui.open(uri)
|
||||
local cmd, err = vim.ui.open(uri)
|
||||
local ret = cmd and cmd:wait(2000) or nil
|
||||
|
||||
if ret == nil or ret.code ~= 0 then
|
||||
return {
|
||||
|
@ -114,14 +114,19 @@ end
|
||||
--- Examples:
|
||||
---
|
||||
--- ```lua
|
||||
--- -- Asynchronous.
|
||||
--- vim.ui.open("https://neovim.io/")
|
||||
--- vim.ui.open("~/path/to/file")
|
||||
--- vim.ui.open("$VIMRUNTIME")
|
||||
--- -- Synchronous (wait until the process exits).
|
||||
--- local cmd, err = vim.ui.open("$VIMRUNTIME")
|
||||
--- if cmd then
|
||||
--- cmd:wait()
|
||||
--- end
|
||||
--- ```
|
||||
---
|
||||
---@param path string Path or URL to open
|
||||
---
|
||||
---@return vim.SystemCompleted|nil # Command result, or nil if not found.
|
||||
---@return vim.SystemObj|nil # Command object, or nil if not found.
|
||||
---@return string|nil # Error message on failure
|
||||
---
|
||||
---@see |vim.system()|
|
||||
@ -152,13 +157,7 @@ function M.open(path)
|
||||
return nil, 'vim.ui.open: no handler found (tried: explorer.exe, xdg-open)'
|
||||
end
|
||||
|
||||
local rv = vim.system(cmd, { text = true, detach = true }):wait()
|
||||
if rv.code ~= 0 then
|
||||
local msg = ('vim.ui.open: command failed (%d): %s'):format(rv.code, vim.inspect(cmd))
|
||||
return rv, msg
|
||||
end
|
||||
|
||||
return rv, nil
|
||||
return vim.system(cmd, { text = true, detach = true }), nil
|
||||
end
|
||||
|
||||
return M
|
||||
|
@ -1,6 +1,6 @@
|
||||
local t = require('test.functional.testutil')()
|
||||
local eq = t.eq
|
||||
local matches = t.matches
|
||||
local ok = t.ok
|
||||
local exec_lua = t.exec_lua
|
||||
local clear = t.clear
|
||||
local feed = t.feed
|
||||
@ -138,13 +138,12 @@ describe('vim.ui', function()
|
||||
describe('open()', function()
|
||||
it('validation', function()
|
||||
if is_os('win') or not is_ci('github') then
|
||||
exec_lua [[vim.system = function() return { wait=function() return { code=3} end } end]]
|
||||
exec_lua [[vim.system = function() return { wait=function() return { code=3 } end } end]]
|
||||
end
|
||||
if not is_os('bsd') then
|
||||
matches(
|
||||
'vim.ui.open: command failed %(%d%): { "[^"]+", .*"non%-existent%-file" }',
|
||||
exec_lua [[local _, err = vim.ui.open('non-existent-file') ; return err]]
|
||||
)
|
||||
local rv =
|
||||
exec_lua [[local cmd = vim.ui.open('non-existent-file'); return cmd:wait(100).code]]
|
||||
ok(type(rv) == 'number' and rv ~= 0, 'nonzero exit code', rv)
|
||||
end
|
||||
|
||||
exec_lua [[
|
||||
|
Loading…
Reference in New Issue
Block a user