From a38451be40ca571e9c55656b19ea8926d5f4524b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 19 Apr 2026 17:37:03 +0200 Subject: [PATCH] fix(excmd): nlua_call_excmd require() failure is a "lua_error" Although `nlua_call_excmd` is semantically for implementing Ex-commands, the `require()` should never fail, so that's a "Lua error". But if the call itself fails (the later `semsg` call), that's an "Ex cmd" error. --- scripts/linterrcodes.lua | 2 +- src/nvim/ex_eval.c | 4 +++- src/nvim/lua/executor.c | 6 +++--- test/functional/ex_cmds/excmd_spec.lua | 15 ++++++++++++++- test/functional/ex_cmds/lsp_spec.lua | 2 +- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/scripts/linterrcodes.lua b/scripts/linterrcodes.lua index e6767fbeba..ed2ca92b6c 100644 --- a/scripts/linterrcodes.lua +++ b/scripts/linterrcodes.lua @@ -48,7 +48,7 @@ local dup_allowed = { E509 = 2, E5101 = 2, E5102 = 2, - E5108 = 6, + E5108 = 5, E5111 = 2, E513 = 2, E521 = 2, diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index cedb58b4d0..ebbb940422 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -378,7 +378,9 @@ bool do_intthrow(cstack_T *cstack) return true; } -/// Get an exception message that is to be stored in current_exception->value. +/// Gets an exception message that is to be stored in current_exception->value. +/// +/// For error exceptions (ET_ERROR), formats the message as "Vim(cmdname):Exx: …". char *get_exception_string(void *value, except_type_T type, char *cmdname, bool *should_free) { char *ret; diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index d770cdbb5e..1771a01565 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1785,8 +1785,7 @@ bool nlua_call_excmd(const char *module, const char *func, exarg_T *eap, const c lua_getglobal(lstate, "require"); lua_pushstring(lstate, module); if (lua_pcall(lstate, 1, 1, 0) != 0) { - semsg("E5108: %s", lua_tostring(lstate, -1)); - lua_pop(lstate, 1); + nlua_error(lstate, "E5108: %s"); return false; } lua_getfield(lstate, -1, func); @@ -1802,7 +1801,8 @@ bool nlua_call_excmd(const char *module, const char *func, exarg_T *eap, const c } if (nlua_pcall(lstate, nargs, 0)) { - semsg("E5108: %s", lua_tostring(lstate, -1)); + // Not "E5108" because this is a logical/application error, not a "Lua error". + emsg(lua_tostring(lstate, -1)); lua_pop(lstate, 1); return false; } diff --git a/test/functional/ex_cmds/excmd_spec.lua b/test/functional/ex_cmds/excmd_spec.lua index 7c67222cb3..4e86420101 100644 --- a/test/functional/ex_cmds/excmd_spec.lua +++ b/test/functional/ex_cmds/excmd_spec.lua @@ -8,7 +8,20 @@ local fn = n.fn local pcall_err = t.pcall_err local assert_alive = n.assert_alive -describe('Ex cmds', function() +describe('nlua_call_excmd excmds', function() + -- Exercise nlua_call_excmd by testing commands implemented with it (:log, :lsp). + + before_each(function() + clear() + end) + + it('error propagation, formatting', function() + t.eq('Vim(lsp):E5800: Invalid :lsp subcommand: bogus', pcall_err(command, 'lsp bogus')) + t.matches('Vim%(log%):E5200: No such log.*', pcall_err(command, 'log bogus')) + end) +end) + +describe('excmds', function() before_each(function() clear() end) diff --git a/test/functional/ex_cmds/lsp_spec.lua b/test/functional/ex_cmds/lsp_spec.lua index 2cef077b30..3922c55b14 100644 --- a/test/functional/ex_cmds/lsp_spec.lua +++ b/test/functional/ex_cmds/lsp_spec.lua @@ -29,7 +29,7 @@ describe(':lsp', function() env = { VIMRUNTIME = 'non-existent' }, } t.matches( - [[Vim%(lsp%):E%d+: .*module 'vim%.lsp' not found:]], + [[.*module 'vim%.lsp' not found:]], vim.split(t.pcall_err(n.command, 'lsp enable dummy'), '\n')[1] ) end)