From 094621e4c92a30dfe8bdca981de5799e22db3f95 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 5 Nov 2026 06:19:50 -0500 Subject: [PATCH] feat(eval): mark getenv() as api-fast #42703 Problem: `vim.env` (`vim.fn.getenv`) fails in a fast context (luv callbacks, LSP rpc). Solution: Mark `getenv()` as |api-fast|. `vim_getenv()` never tickles `os_breakcheck()`. --- runtime/doc/news.txt | 8 ++++---- runtime/doc/vimfn.txt | 3 +++ runtime/lua/vim/log.lua | 3 +-- src/nvim/eval.lua | 1 + test/functional/api/fast_spec.lua | 8 ++++++++ 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 663485b099..9b51b3901a 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -575,10 +575,10 @@ VIMSCRIPT • |v:useractive| indicates user activity. • |serverlist()| with `info=true` returns details for each server (own + peers). • These Vimscript functions may be called from |api-fast| context (e.g. - |vim.uv| timer/handle callbacks): |keytrans()|, |char2nr()|, |nr2char()|, - |str2list()|, |strpart()|, |strcharpart()|, |strgetchar()|, |strchars()|, - |strcharlen()|, |strlen()|, |strdisplaywidth()|, |byteidx()|, |charidx()|, - |utf16idx()|, |tr()|, |trim()|. + |vim.uv| timer/handle callbacks): |getenv()|, |keytrans()|, |char2nr()|, + |nr2char()|, |str2list()|, |strpart()|, |strcharpart()|, |strgetchar()|, + |strchars()|, |strcharlen()|, |strlen()|, |strdisplaywidth()|, |byteidx()|, + |charidx()|, |utf16idx()|, |tr()|, |trim()|. ============================================================================== CHANGED FEATURES *news-changed* diff --git a/runtime/doc/vimfn.txt b/runtime/doc/vimfn.txt index 8cdd7c1aaf..339acee57f 100644 --- a/runtime/doc/vimfn.txt +++ b/runtime/doc/vimfn.txt @@ -3816,6 +3816,9 @@ getenv({name}) *getenv()* is different from a variable set to an empty string. See also |expr-env|. + Attributes: ~ + |api-fast| + Parameters: ~ • {name} (`string`) diff --git a/runtime/lua/vim/log.lua b/runtime/lua/vim/log.lua index 76d10205ce..5f96c87e1a 100644 --- a/runtime/lua/vim/log.lua +++ b/runtime/lua/vim/log.lua @@ -102,8 +102,7 @@ local function default_format_func(min_level, level, ...) local info = debug.getinfo(3, 'Sl') -- Full path (`short_src` is truncated), with $VIMRUNTIME abbreviated. local src = info and (info.source:match('^@(.*)') or info.short_src) or '?' - local vimruntime = vim.uv.os_getenv('VIMRUNTIME') -- Use `vim.uv` for api-fast (e.g. LSP rpc). - local rt_path = vimruntime and vim.fs.relpath(vimruntime, src) + local rt_path = vim.env.VIMRUNTIME and vim.fs.relpath(vim.env.VIMRUNTIME, src) local header = string.format( '[%s][%s] %s:%s', level_names[level], diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 44de5bf0bd..46f69574b2 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -4161,6 +4161,7 @@ M.funcs = { See also |expr-env|. ]=], + fast = true, name = 'getenv', params = { { 'name', 'string' } }, returns = 'string', diff --git a/test/functional/api/fast_spec.lua b/test/functional/api/fast_spec.lua index 4cfca310bf..26604a2f2c 100644 --- a/test/functional/api/fast_spec.lua +++ b/test/functional/api/fast_spec.lua @@ -12,6 +12,7 @@ describe('|api-fast| functions', function() it('callable in a fast event', function() local out = exec_lua(function() local result + vim.uv.os_setenv('XTEST_FAST', 'x') local timer = assert(vim.uv.new_timer()) timer:start(0, 0, function() timer:close() @@ -21,6 +22,7 @@ describe('|api-fast| functions', function() byteidx = vim.fn.byteidx('héllo', 2), char2nr = vim.fn.char2nr('A'), charidx = vim.fn.charidx('héllo', 3), + getenv = vim.env.XTEST_FAST, in_fast = vim.in_fast_event(), keycode = vim.keycode(''), keytrans = vim.fn.keytrans(vim.keycode('')), @@ -55,6 +57,7 @@ describe('|api-fast| functions', function() byteidx = 3, char2nr = 65, charidx = 2, + getenv = 'x', in_fast = true, keycode = '\1', -- keytrans = '', @@ -108,6 +111,11 @@ describe('|api-fast| functions', function() charidx = function() vim.fn.charidx(big, 500000) end, + getenv = function() + -- Unset, so getenv() computes it. + vim.env.VIMRUNTIME = nil + vim.fn.getenv('VIMRUNTIME') + end, keycode = function() vim.keycode('') end,