commit 35af766de6ee2436a4b24495fb862383d16b419d
parent 54b8c99e51bb124d03e21a5d268d60125ef9cc01
Author: Yochem van Rosmalen <git@yochem.nl>
Date: Thu, 24 Jul 2025 05:03:30 +0200
refactor(lua): use vim.system #34707
Diffstat:
9 files changed, 77 insertions(+), 61 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -277,7 +277,7 @@ add_glob_target(
TOUCH_STRATEGY PER_DIR)
add_custom_target(lintcommit
- COMMAND $<TARGET_FILE:nvim_bin> -u NONE -l ${PROJECT_SOURCE_DIR}/scripts/lintcommit.lua main)
+ COMMAND $<TARGET_FILE:nvim_bin> --clean -l ${PROJECT_SOURCE_DIR}/scripts/lintcommit.lua main)
add_dependencies(lintcommit nvim_bin)
add_custom_target(lint)
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
@@ -3895,7 +3895,7 @@ conforming to the https://semver.org spec. Plugins, and plugin managers, can
use this to check available tools and dependencies on the current system.
Example: >lua
- local v = vim.version.parse(vim.fn.system({'tmux', '-V'}), {strict=false})
+ local v = vim.version.parse(vim.system({'tmux', '-V'}):wait().stdout, {strict=false})
if vim.version.gt(v, {3, 2, 0}) then
-- ...
end
diff --git a/runtime/example_init.lua b/runtime/example_init.lua
@@ -76,7 +76,7 @@ vim.api.nvim_create_autocmd('TextYankPost', {
vim.api.nvim_create_user_command('GitBlameLine', function()
local line_number = vim.fn.line('.') -- Get the current line number. See `:h line()`
local filename = vim.api.nvim_buf_get_name(0)
- print(vim.fn.system({ 'git', 'blame', '-L', line_number .. ',+1', filename }))
+ print(vim.system({ 'git', 'blame', '-L', line_number .. ',+1', filename }):wait().stdout)
end, { desc = 'Print the git blame for the current line' })
-- [[ Add optional packages ]]
diff --git a/runtime/lua/vim/health/health.lua b/runtime/lua/vim/health/health.lua
@@ -1,8 +1,13 @@
local M = {}
local health = require('vim.health')
-local shell_error = function()
- return vim.v.shell_error ~= 0
+---Run a system command and return ok and its stdout and stderr combined.
+---@param cmd string[]
+---@return boolean
+---@return string
+local function system(cmd)
+ local result = vim.system(cmd, { text = true }):wait()
+ return result.code == 0, vim.trim(('%s\n%s'):format(result.stdout, result.stderr))
end
local suggest_faq = 'https://github.com/neovim/neovim/blob/master/BUILD.md#building'
@@ -168,10 +173,10 @@ local function check_performance()
end
-- check for slow shell invocation
- local slow_cmd_time = 1.5
- local start_time = vim.fn.reltime()
- vim.fn.system('echo')
- local elapsed_time = vim.fn.reltimefloat(vim.fn.reltime(start_time))
+ local slow_cmd_time = 1.5e9
+ local start_time = vim.uv.hrtime()
+ system({ 'echo' })
+ local elapsed_time = vim.uv.hrtime() - start_time
if elapsed_time > slow_cmd_time then
health.warn(
'Slow shell invocation (took ' .. vim.fn.printf('%.2f', elapsed_time) .. ' seconds).'
@@ -252,17 +257,17 @@ local function check_tmux()
---@param option string
local get_tmux_option = function(option)
- local cmd = 'tmux show-option -qvg ' .. option -- try global scope
- local out = vim.fn.system(vim.fn.split(cmd))
+ local cmd = { 'tmux', 'show-option', '-qvg', option } -- try global scope
+ local ok, out = system(cmd)
local val = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g')
- if shell_error() then
+ if not ok then
health.error('command failed: ' .. cmd .. '\n' .. out)
return 'error'
elseif val == '' then
- cmd = 'tmux show-option -qvgs ' .. option -- try session scope
- out = vim.fn.system(vim.fn.split(cmd))
+ cmd = { 'tmux', 'show-option', '-qvgs', option } -- try session scope
+ ok, out = system(cmd)
val = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g')
- if shell_error() then
+ if not ok then
health.error('command failed: ' .. cmd .. '\n' .. out)
return 'error'
end
@@ -301,16 +306,16 @@ local function check_tmux()
-- check default-terminal and $TERM
health.info('$TERM: ' .. vim.env.TERM)
- local cmd = 'tmux show-option -qvg default-terminal'
- local out = vim.fn.system(vim.fn.split(cmd))
+ local cmd = { 'tmux', 'show-option', '-qvg', 'default-terminal' }
+ local ok, out = system(cmd)
local tmux_default_term = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g')
if tmux_default_term == '' then
- cmd = 'tmux show-option -qvgs default-terminal'
- out = vim.fn.system(vim.fn.split(cmd))
+ cmd = { 'tmux', 'show-option', '-qvgs', 'default-terminal' }
+ ok, out = system(cmd)
tmux_default_term = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g')
end
- if shell_error() then
+ if not ok then
health.error('command failed: ' .. cmd .. '\n' .. out)
elseif tmux_default_term ~= vim.env.TERM then
health.info('default-terminal: ' .. tmux_default_term)
@@ -329,7 +334,7 @@ local function check_tmux()
end
-- check for RGB capabilities
- local info = vim.fn.system({ 'tmux', 'show-messages', '-T' })
+ local _, info = system({ 'tmux', 'show-messages', '-T' })
local has_setrgbb = vim.fn.stridx(info, ' setrgbb: (string)') ~= -1
local has_setrgbf = vim.fn.stridx(info, ' setrgbf: (string)') ~= -1
if not has_setrgbb or not has_setrgbf then
@@ -349,13 +354,13 @@ local function check_terminal()
end
health.start('terminal')
- local cmd = 'infocmp -L'
- local out = vim.fn.system(vim.fn.split(cmd))
+ local cmd = { 'infocmp', '-L' }
+ local ok, out = system(cmd)
local kbs_entry = vim.fn.matchstr(out, 'key_backspace=[^,[:space:]]*')
local kdch1_entry = vim.fn.matchstr(out, 'key_dc=[^,[:space:]]*')
if
- shell_error()
+ not ok
and (
vim.fn.has('win32') == 0
or vim.fn.matchstr(
diff --git a/runtime/lua/vim/provider/health.lua b/runtime/lua/vim/provider/health.lua
@@ -4,8 +4,13 @@ local iswin = vim.fn.has('win32') == 1
local M = {}
local function cmd_ok(cmd)
- local out = vim.fn.system(cmd)
- return vim.v.shell_error == 0, out
+ local result = vim.system(cmd, { text = true }):wait()
+ return result.code == 0, result.stdout
+end
+
+local function cli_version(cmd)
+ local ok, out = cmd_ok(cmd)
+ return ok, vim.version.parse(out, { strict = false })
end
-- Attempts to construct a shell command from an args list.
@@ -127,14 +132,14 @@ local function clipboard()
os.getenv('TMUX')
and vim.fn.executable('tmux') == 1
and vim.fn.executable('pbpaste') == 1
- and not cmd_ok('pbpaste')
+ and not cmd_ok({ 'pbpaste' })
then
- local tmux_version = string.match(vim.fn.system('tmux -V'), '%d+%.%d+')
+ local _, tmux_version = cli_version({ 'tmux', '-V' })
local advice = {
'Install tmux 2.6+. https://superuser.com/q/231130',
'or use tmux with reattach-to-user-namespace. https://superuser.com/a/413233',
}
- health.error('pbcopy does not work with tmux version: ' .. tmux_version, advice)
+ health.error('pbcopy does not work with tmux version: ' .. tostring(tmux_version), advice)
end
local clipboard_tool = vim.fn['provider#clipboard#Executable']() ---@type string
@@ -178,9 +183,8 @@ local function node()
return
end
- -- local node_v = vim.fn.split(system({'node', '-v'}), "\n")[1] or ''
- local ok, node_v = cmd_ok({ 'node', '-v' })
- health.info('Node.js: ' .. node_v)
+ local ok, node_v = cli_version({ 'node', '-v' })
+ health.info('Node.js: ' .. tostring(node_v))
if not ok or vim.version.lt(node_v, '6.0.0') then
health.warn('Nvim node.js host does not support Node ' .. node_v)
-- Skip further checks, they are nonsense if nodejs is too old.
@@ -213,13 +217,14 @@ local function node()
end
local latest_npm_cmd = (
- iswin and 'cmd /c ' .. manager .. ' info neovim --json' or manager .. ' info neovim --json'
+ iswin and { 'cmd', '/c', manager, 'info', 'neovim', '--json' }
+ or { manager, 'info', 'neovim', '--json' }
)
local latest_npm
- ok, latest_npm = cmd_ok(vim.split(latest_npm_cmd, ' '))
+ ok, latest_npm = cmd_ok(latest_npm_cmd)
if not ok or latest_npm:find('^%s$') then
health.error(
- 'Failed to run: ' .. latest_npm_cmd,
+ 'Failed to run: ' .. shellify(latest_npm_cmd),
{ "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' }
)
return
@@ -237,8 +242,8 @@ local function node()
ok, current_npm = cmd_ok(current_npm_cmd)
if not ok then
health.error(
- 'Failed to run: ' .. table.concat(current_npm_cmd, ' '),
- { 'Report this issue with the output of: ', table.concat(current_npm_cmd, ' ') }
+ 'Failed to run: ' .. shellify(current_npm_cmd),
+ { 'Report this issue with the output of: ', shellify(current_npm_cmd) }
)
return
end
@@ -298,7 +303,7 @@ local function perl()
ok, latest_cpan = cmd_ok(latest_cpan_cmd)
if not ok or latest_cpan:find('^%s*$') then
health.error(
- 'Failed to run: ' .. table.concat(latest_cpan_cmd, ' '),
+ 'Failed to run: ' .. shellify(latest_cpan_cmd),
{ "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' }
)
return
@@ -329,8 +334,8 @@ local function perl()
ok, current_cpan = cmd_ok(current_cpan_cmd)
if not ok then
health.error(
- 'Failed to run: ' .. table.concat(current_cpan_cmd, ' '),
- { 'Report this issue with the output of: ', table.concat(current_cpan_cmd, ' ') }
+ 'Failed to run: ' .. shellify(current_cpan_cmd),
+ { 'Report this issue with the output of: ', shellify(current_cpan_cmd) }
)
return
end
@@ -426,12 +431,15 @@ end
--- @param url string
local function download(url)
local has_curl = vim.fn.executable('curl') == 1
- if has_curl and vim.fn.system({ 'curl', '-V' }):find('Protocols:.*https') then
- local out, rc = system({ 'curl', '-sL', url }, { stderr = true, ignore_error = true })
- if rc ~= 0 then
- return 'curl error with ' .. url .. ': ' .. rc
- else
- return out
+ if has_curl then
+ local ok, out = cmd_ok({ 'curl', '-V' })
+ if ok and out:find('Protocols:.*https') then
+ local content, rc = system({ 'curl', '-sL', url }, { stderr = true, ignore_error = true })
+ if rc ~= 0 then
+ return 'curl error with ' .. url .. ': ' .. rc
+ else
+ return content
+ end
end
elseif vim.fn.executable('python') == 1 then
local script = ([[
@@ -872,7 +880,8 @@ local function ruby()
)
return
end
- health.info('Ruby: ' .. system({ 'ruby', '-v' }))
+ local _, ruby_v = cli_version({ 'ruby', '-v' })
+ health.info('Ruby: ' .. tostring(ruby_v))
local host, _ = vim.provider.ruby.detect()
if (not host) or host:find('^%s*$') then
@@ -887,11 +896,14 @@ local function ruby()
end
health.info('Host: ' .. host)
- local latest_gem_cmd = (iswin and 'cmd /c gem list -ra "^^neovim$"' or 'gem list -ra ^neovim$')
- local ok, latest_gem = cmd_ok(vim.split(latest_gem_cmd, ' '))
+ local latest_gem_cmd = (
+ iswin and { 'cmd', '/c', 'gem', 'list', '-ra', '"^^neovim$"' }
+ or { 'gem', 'list', '-ra', '^neovim$' }
+ )
+ local ok, latest_gem = cmd_ok(latest_gem_cmd)
if not ok or latest_gem:find('^%s*$') then
health.error(
- 'Failed to run: ' .. latest_gem_cmd,
+ 'Failed to run: ' .. shellify(latest_gem_cmd),
{ "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' }
)
return
@@ -904,8 +916,8 @@ local function ruby()
ok, current_gem = cmd_ok(current_gem_cmd)
if not ok then
health.error(
- 'Failed to run: ' .. table.concat(current_gem_cmd, ' '),
- { 'Report this issue with the output of: ', table.concat(current_gem_cmd, ' ') }
+ 'Failed to run: ' .. shellify(current_gem_cmd),
+ { 'Report this issue with the output of: ', shellify(current_gem_cmd) }
)
return
end
diff --git a/runtime/lua/vim/provider/perl.lua b/runtime/lua/vim/provider/perl.lua
@@ -25,14 +25,12 @@ function M.detect()
end
-- if perl is available, make sure we have 5.22+
- vim.fn.system({ prog, '-e', 'use v5.22' })
- if vim.v.shell_error ~= 0 then
+ if vim.system({ prog, '-e', 'use v5.22' }):wait().code ~= 0 then
return nil, 'Perl version is too old, 5.22+ required'
end
-- if perl is available, make sure the required module is available
- vim.fn.system({ prog, '-W', '-MNeovim::Ext', '-e', '' })
- if vim.v.shell_error ~= 0 then
+ if vim.system({ prog, '-W', '-MNeovim::Ext', '-e', '' }):wait().code ~= 0 then
return nil, '"Neovim::Ext" cpan module is not installed'
end
return prog, nil
diff --git a/runtime/lua/vim/provider/ruby.lua b/runtime/lua/vim/provider/ruby.lua
@@ -45,8 +45,8 @@ function M.detect()
prog = ''
else
-- neovim-ruby-host could be an rbenv shim for another Ruby version.
- vim.fn.system(p)
- prog = vim.v.shell_error ~= 0 and '' or p
+ local result = vim.system({ p }):wait()
+ prog = result.code ~= 0 and '' or p
end
end
local err = prog == '' and 'missing ruby or ruby-host' or ''
diff --git a/runtime/lua/vim/version.lua b/runtime/lua/vim/version.lua
@@ -6,7 +6,7 @@
--- Example:
---
--- ```lua
---- local v = vim.version.parse(vim.fn.system({'tmux', '-V'}), {strict=false})
+--- local v = vim.version.parse(vim.system({'tmux', '-V'}):wait().stdout, {strict=false})
--- if vim.version.gt(v, {3, 2, 0}) then
--- -- ...
--- end
diff --git a/scripts/lintcommit.lua b/scripts/lintcommit.lua
@@ -27,8 +27,9 @@ local function run(cmd, or_die)
if _trace then
p('run: ' .. vim.inspect(cmd))
end
- local rv = vim.trim(vim.fn.system(cmd)) or ''
- if vim.v.shell_error ~= 0 then
+ local res = vim.system(cmd):wait()
+ local rv = vim.trim(res.stdout)
+ if res.code ~= 0 then
if or_die then
p(rv)
os.exit(1)