Merge pull request #20945 from erw7/feat-more-exception-info

feat(api): show more exception info
This commit is contained in:
zeertzjq
2023-01-16 19:28:17 +08:00
committed by GitHub
14 changed files with 66 additions and 50 deletions

View File

@@ -159,6 +159,8 @@ The following changes to existing APIs or features add new behavior.
resulting in a nvim binary which only could be run headless or embedded resulting in a nvim binary which only could be run headless or embedded
in an external process. As of this version, TUI is always available. in an external process. As of this version, TUI is always available.
• API calls now show more information about where an exception happened.
============================================================================== ==============================================================================
REMOVED FEATURES *news-removed* REMOVED FEATURES *news-removed*

View File

@@ -150,7 +150,18 @@ bool try_end(Error *err)
xfree(msg); xfree(msg);
} }
} else if (did_throw) { } else if (did_throw) {
api_set_error(err, kErrorTypeException, "%s", current_exception->value); if (*current_exception->throw_name != NUL) {
if (current_exception->throw_lnum != 0) {
api_set_error(err, kErrorTypeException, "%s, line %" PRIdLINENR ": %s",
current_exception->throw_name, current_exception->throw_lnum,
current_exception->value);
} else {
api_set_error(err, kErrorTypeException, "%s: %s",
current_exception->throw_name, current_exception->value);
}
} else {
api_set_error(err, kErrorTypeException, "%s", current_exception->value);
}
discard_current_exception(); discard_current_exception();
} }

View File

@@ -773,7 +773,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool clea
tl_ret = try_leave(&tstate, &err); tl_ret = try_leave(&tstate, &err);
if (!tl_ret && ERROR_SET(&err)) { if (!tl_ret && ERROR_SET(&err)) {
msg_putchar('\n'); msg_putchar('\n');
msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); msg_scroll = true;
msg_puts_attr(err.msg, HL_ATTR(HLF_E)|MSG_HIST);
api_clear_error(&err); api_clear_error(&err);
redrawcmd(); redrawcmd();
} }
@@ -881,7 +882,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool clea
if (!tl_ret && ERROR_SET(&err)) { if (!tl_ret && ERROR_SET(&err)) {
msg_putchar('\n'); msg_putchar('\n');
semsg(e_autocmd_err, err.msg); emsg(err.msg);
did_emsg = false; did_emsg = false;
api_clear_error(&err); api_clear_error(&err);
} }
@@ -2544,7 +2545,8 @@ static void do_autocmd_cmdlinechanged(int firstc)
bool tl_ret = try_leave(&tstate, &err); bool tl_ret = try_leave(&tstate, &err);
if (!tl_ret && ERROR_SET(&err)) { if (!tl_ret && ERROR_SET(&err)) {
msg_putchar('\n'); msg_putchar('\n');
msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); msg_scroll = true;
msg_puts_attr(err.msg, HL_ATTR(HLF_E)|MSG_HIST);
api_clear_error(&err); api_clear_error(&err);
redrawcmd(); redrawcmd();
} }

View File

@@ -1002,7 +1002,6 @@ EXTERN char e_fnametoolong[] INIT(= N_("E856: Filename too long"));
EXTERN char e_float_as_string[] INIT(= N_("E806: using Float as a String")); EXTERN char e_float_as_string[] INIT(= N_("E806: using Float as a String"));
EXTERN char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit another buffer now")); EXTERN char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit another buffer now"));
EXTERN char e_autocmd_err[] INIT(= N_("E5500: autocmd has thrown an exception: %s"));
EXTERN char e_cmdmap_err[] INIT(= N_("E5520: <Cmd> mapping must end with <CR>")); EXTERN char e_cmdmap_err[] INIT(= N_("E5520: <Cmd> mapping must end with <CR>"));
EXTERN char e_cmdmap_repeated[] EXTERN char e_cmdmap_repeated[]
INIT(= N_("E5521: <Cmd> mapping must end with <CR> before second <Cmd>")); INIT(= N_("E5521: <Cmd> mapping must end with <CR> before second <Cmd>"));

View File

@@ -166,7 +166,7 @@ describe('API', function()
echo nvim_exec('echo Avast_ye_hades(''ahoy!'')', 1) echo nvim_exec('echo Avast_ye_hades(''ahoy!'')', 1)
]], true)) ]], true))
eq('Vim(call):E5555: API call: Vim(echo):E121: Undefined variable: s:pirate', matches('Vim%(echo%):E121: Undefined variable: s:pirate$',
pcall_err(request, 'nvim_exec', [[ pcall_err(request, 'nvim_exec', [[
let s:pirate = 'script-scoped varrrrr' let s:pirate = 'script-scoped varrrrr'
call nvim_exec('echo s:pirate', 1) call nvim_exec('echo s:pirate', 1)
@@ -208,12 +208,12 @@ describe('API', function()
end) end)
it('execution error', function() it('execution error', function()
eq('Vim:E492: Not an editor command: bogus_command', eq('nvim_exec(): Vim:E492: Not an editor command: bogus_command',
pcall_err(request, 'nvim_exec', 'bogus_command', false)) pcall_err(request, 'nvim_exec', 'bogus_command', false))
eq('', nvim('eval', 'v:errmsg')) -- v:errmsg was not updated. eq('', nvim('eval', 'v:errmsg')) -- v:errmsg was not updated.
eq('', eval('v:exception')) eq('', eval('v:exception'))
eq('Vim(buffer):E86: Buffer 23487 does not exist', eq('nvim_exec(): Vim(buffer):E86: Buffer 23487 does not exist',
pcall_err(request, 'nvim_exec', 'buffer 23487', false)) pcall_err(request, 'nvim_exec', 'buffer 23487', false))
eq('', eval('v:errmsg')) -- v:errmsg was not updated. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
eq('', eval('v:exception')) eq('', eval('v:exception'))
@@ -485,7 +485,7 @@ describe('API', function()
throw 'wtf' throw 'wtf'
endfunction endfunction
]]) ]])
eq('wtf', pcall_err(request, 'nvim_call_function', 'Foo', {})) eq('function Foo, line 1: wtf', pcall_err(request, 'nvim_call_function', 'Foo', {}))
eq('', eval('v:exception')) eq('', eval('v:exception'))
eq('', eval('v:errmsg')) -- v:errmsg was not updated. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
end) end)

View File

@@ -424,13 +424,13 @@ describe('autocmd', function()
end) end)
it('gives E814 when there are no other floating windows', function() it('gives E814 when there are no other floating windows', function()
eq('Vim(close):E814: Cannot close window, only autocmd window would remain', eq('BufAdd Autocommands for "Xa.txt": Vim(close):E814: Cannot close window, only autocmd window would remain',
pcall_err(command, 'doautoall BufAdd')) pcall_err(command, 'doautoall BufAdd'))
end) end)
it('gives E814 when there are other floating windows', function() it('gives E814 when there are other floating windows', function()
meths.open_win(0, true, {width = 10, height = 10, relative = 'editor', row = 10, col = 10}) meths.open_win(0, true, {width = 10, height = 10, relative = 'editor', row = 10, col = 10})
eq('Vim(close):E814: Cannot close window, only autocmd window would remain', eq('BufAdd Autocommands for "Xa.txt": Vim(close):E814: Cannot close window, only autocmd window would remain',
pcall_err(command, 'doautoall BufAdd')) pcall_err(command, 'doautoall BufAdd'))
end) end)
end) end)
@@ -476,14 +476,14 @@ describe('autocmd', function()
it('during RecordingLeave event', function() it('during RecordingLeave event', function()
command([[autocmd RecordingLeave * let v:event.regname = '']]) command([[autocmd RecordingLeave * let v:event.regname = '']])
eq('Vim(let):E46: Cannot change read-only variable "v:event.regname"', eq('RecordingLeave Autocommands for "*": Vim(let):E46: Cannot change read-only variable "v:event.regname"',
pcall_err(command, 'normal! qqq')) pcall_err(command, 'normal! qqq'))
end) end)
it('during TermClose event', function() it('during TermClose event', function()
command('autocmd TermClose * let v:event.status = 0') command('autocmd TermClose * let v:event.status = 0')
command('terminal') command('terminal')
eq('Vim(let):E46: Cannot change read-only variable "v:event.status"', eq('TermClose Autocommands for "*": Vim(let):E46: Cannot change read-only variable "v:event.status"',
pcall_err(command, 'bdelete!')) pcall_err(command, 'bdelete!'))
end) end)
end) end)

View File

@@ -73,7 +73,7 @@ describe('cmdline autocommands', function()
{1:~ }| {1:~ }|
{4: }| {4: }|
: | : |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} |
:^ | :^ |
]]) ]])
@@ -82,9 +82,9 @@ describe('cmdline autocommands', function()
| |
{4: }| {4: }|
: | : |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} |
:put ='lorem ipsum' | :put ='lorem ipsum' |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):very error} | {2:CmdlineLeave Autocommands for "*": Vim(echoerr):very error} |
| |
{3:Press ENTER or type command to continue}^ | {3:Press ENTER or type command to continue}^ |
]]) ]])
@@ -111,9 +111,9 @@ describe('cmdline autocommands', function()
lorem ipsum | lorem ipsum |
{4: }| {4: }|
: | : |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} |
:put ='lorem ipsum' | :put ='lorem ipsum' |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} | {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} |
:put ='lorem ipsum'^ | :put ='lorem ipsum'^ |
]]) ]])
@@ -123,9 +123,9 @@ describe('cmdline autocommands', function()
lorem ipsum | lorem ipsum |
{4: }| {4: }|
: | : |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} |
:put ='lorem ipsum' | :put ='lorem ipsum' |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} | {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} |
:put ='lorem ipsum^' | :put ='lorem ipsum^' |
]]) ]])
@@ -134,22 +134,22 @@ describe('cmdline autocommands', function()
screen:expect([[ screen:expect([[
{4: }| {4: }|
: | : |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} |
:put ='lorem ipsum' | :put ='lorem ipsum' |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} | {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} |
:put ='lorem ipsum.' | :put ='lorem ipsum.' |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} | {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} |
:put ='lorem ipsum.^' | :put ='lorem ipsum.^' |
]]) ]])
feed('<cr>') feed('<cr>')
screen:expect([[ screen:expect([[
:put ='lorem ipsum' | :put ='lorem ipsum' |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} | {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} |
:put ='lorem ipsum.' | :put ='lorem ipsum.' |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} | {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} |
:put ='lorem ipsum.' | :put ='lorem ipsum.' |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):very error} | {2:CmdlineLeave Autocommands for "*": Vim(echoerr):very error} |
| |
{3:Press ENTER or type command to continue}^ | {3:Press ENTER or type command to continue}^ |
]]) ]])

View File

@@ -24,7 +24,7 @@ describe('autocmd TermClose', function()
local function test_termclose_delete_own_buf() local function test_termclose_delete_own_buf()
command('autocmd TermClose * bdelete!') command('autocmd TermClose * bdelete!')
command('terminal') command('terminal')
matches('^Vim%(bdelete%):E937: Attempt to delete a buffer that is in use: term://', matches('^TermClose Autocommands for "%*": Vim%(bdelete%):E937: Attempt to delete a buffer that is in use: term://',
pcall_err(command, 'bdelete!')) pcall_err(command, 'bdelete!'))
assert_alive() assert_alive()
end end

View File

@@ -89,14 +89,14 @@ describe(':cquit', function()
end) end)
it('exits with redir msg for multiple exit codes after :cquit 1 2', function() it('exits with redir msg for multiple exit codes after :cquit 1 2', function()
test_cq('cquit 1 2', nil, 'Vim(cquit):E488: Trailing characters: 2: cquit 1 2') test_cq('cquit 1 2', nil, 'nvim_exec(): Vim(cquit):E488: Trailing characters: 2: cquit 1 2')
end) end)
it('exits with redir msg for non-number exit code after :cquit X', function() it('exits with redir msg for non-number exit code after :cquit X', function()
test_cq('cquit X', nil, 'Vim(cquit):E488: Trailing characters: X: cquit X') test_cq('cquit X', nil, 'nvim_exec(): Vim(cquit):E488: Trailing characters: X: cquit X')
end) end)
it('exits with redir msg for negative exit code after :cquit -1', function() it('exits with redir msg for negative exit code after :cquit -1', function()
test_cq('cquit -1', nil, 'Vim(cquit):E488: Trailing characters: -1: cquit -1') test_cq('cquit -1', nil, 'nvim_exec(): Vim(cquit):E488: Trailing characters: -1: cquit -1')
end) end)
end) end)

View File

@@ -40,59 +40,59 @@ describe('named marks', function()
it("errors when set out of range with :mark", function() it("errors when set out of range with :mark", function()
command("edit " .. file1) command("edit " .. file1)
local err = pcall_err(helpers.exec_capture, "1000mark x") local err = pcall_err(helpers.exec_capture, "1000mark x")
eq("Vim(mark):E16: Invalid range: 1000mark x", err) eq("nvim_exec(): Vim(mark):E16: Invalid range: 1000mark x", err)
end) end)
it("errors when set out of range with :k", function() it("errors when set out of range with :k", function()
command("edit " .. file1) command("edit " .. file1)
local err = pcall_err(helpers.exec_capture, "1000kx") local err = pcall_err(helpers.exec_capture, "1000kx")
eq("Vim(k):E16: Invalid range: 1000kx", err) eq("nvim_exec(): Vim(k):E16: Invalid range: 1000kx", err)
end) end)
it("errors on unknown mark name with :mark", function() it("errors on unknown mark name with :mark", function()
command("edit " .. file1) command("edit " .. file1)
local err = pcall_err(helpers.exec_capture, "mark #") local err = pcall_err(helpers.exec_capture, "mark #")
eq("Vim(mark):E191: Argument must be a letter or forward/backward quote", err) eq("nvim_exec(): Vim(mark):E191: Argument must be a letter or forward/backward quote", err)
end) end)
it("errors on unknown mark name with '", function() it("errors on unknown mark name with '", function()
command("edit " .. file1) command("edit " .. file1)
local err = pcall_err(helpers.exec_capture, "normal! '#") local err = pcall_err(helpers.exec_capture, "normal! '#")
eq("Vim(normal):E78: Unknown mark", err) eq("nvim_exec(): Vim(normal):E78: Unknown mark", err)
end) end)
it("errors on unknown mark name with `", function() it("errors on unknown mark name with `", function()
command("edit " .. file1) command("edit " .. file1)
local err = pcall_err(helpers.exec_capture, "normal! `#") local err = pcall_err(helpers.exec_capture, "normal! `#")
eq("Vim(normal):E78: Unknown mark", err) eq("nvim_exec(): Vim(normal):E78: Unknown mark", err)
end) end)
it("errors when moving to a mark that is not set with '", function() it("errors when moving to a mark that is not set with '", function()
command("edit " .. file1) command("edit " .. file1)
local err = pcall_err(helpers.exec_capture, "normal! 'z") local err = pcall_err(helpers.exec_capture, "normal! 'z")
eq("Vim(normal):E20: Mark not set", err) eq("nvim_exec(): Vim(normal):E20: Mark not set", err)
err = pcall_err(helpers.exec_capture, "normal! '.") err = pcall_err(helpers.exec_capture, "normal! '.")
eq("Vim(normal):E20: Mark not set", err) eq("nvim_exec(): Vim(normal):E20: Mark not set", err)
end) end)
it("errors when moving to a mark that is not set with `", function() it("errors when moving to a mark that is not set with `", function()
command("edit " .. file1) command("edit " .. file1)
local err = pcall_err(helpers.exec_capture, "normal! `z") local err = pcall_err(helpers.exec_capture, "normal! `z")
eq("Vim(normal):E20: Mark not set", err) eq("nvim_exec(): Vim(normal):E20: Mark not set", err)
err = pcall_err(helpers.exec_capture, "normal! `>") err = pcall_err(helpers.exec_capture, "normal! `>")
eq("Vim(normal):E20: Mark not set", err) eq("nvim_exec(): Vim(normal):E20: Mark not set", err)
end) end)
it("errors when moving to a global mark that is not set with '", function() it("errors when moving to a global mark that is not set with '", function()
command("edit " .. file1) command("edit " .. file1)
local err = pcall_err(helpers.exec_capture, "normal! 'Z") local err = pcall_err(helpers.exec_capture, "normal! 'Z")
eq("Vim(normal):E20: Mark not set", err) eq("nvim_exec(): Vim(normal):E20: Mark not set", err)
end) end)
it("errors when moving to a global mark that is not set with `", function() it("errors when moving to a global mark that is not set with `", function()
command("edit " .. file1) command("edit " .. file1)
local err = pcall_err(helpers.exec_capture, "normal! `Z") local err = pcall_err(helpers.exec_capture, "normal! `Z")
eq("Vim(normal):E20: Mark not set", err) eq("nvim_exec(): Vim(normal):E20: Mark not set", err)
end) end)
it("can move to them using '", function() it("can move to them using '", function()
@@ -153,7 +153,7 @@ describe('named marks', function()
command("next") command("next")
command("bw! " .. file1 ) command("bw! " .. file1 )
local err = pcall_err(helpers.exec_capture, "normal! 'A") local err = pcall_err(helpers.exec_capture, "normal! 'A")
eq("Vim(normal):E92: Buffer 1 not found", err) eq("nvim_exec(): Vim(normal):E92: Buffer 1 not found", err)
os.remove(file1) os.remove(file1)
end) end)

View File

@@ -10,6 +10,7 @@ describe('gf', function()
it('is not allowed when buffer is locked', function() it('is not allowed when buffer is locked', function()
command('au OptionSet diff norm! gf') command('au OptionSet diff norm! gf')
command([[call setline(1, ['Xfile1', 'line2', 'line3', 'line4'])]]) command([[call setline(1, ['Xfile1', 'line2', 'line3', 'line4'])]])
eq('Vim(normal):E788: Not allowed to edit another buffer now', pcall_err(command, 'diffthis')) eq('OptionSet Autocommands for "diff": Vim(normal):E788: Not allowed to edit another buffer now',
pcall_err(command, 'diffthis'))
end) end)
end) end)

View File

@@ -335,17 +335,17 @@ describe('Command-line coloring', function()
:echo "«^ | :echo "«^ |
]]) ]])
end) end)
it('does the right thing when errorring', function() it('does the right thing when erroring', function()
set_color_cb('Echoerring') set_color_cb('Echoerring')
start_prompt('e') start_prompt('e')
screen:expect([[ screen:expect([[
| |
{EOB:~ }| {EOB:~ }|
{EOB:~ }|
{MSEP: }| {MSEP: }|
: | : |
{ERR:E5407: Callback has thrown an exception:}| {ERR:E5407: Callback has thrown an exception:}|
{ERR: Vim(echoerr):HERE} | {ERR: function DoPrompt[3]..Echoerring, line }|
{ERR:1: Vim(echoerr):HERE} |
:e^ | :e^ |
]]) ]])
end) end)
@@ -400,10 +400,10 @@ describe('Command-line coloring', function()
screen:expect([[ screen:expect([[
| |
{EOB:~ }| {EOB:~ }|
{EOB:~ }|
{MSEP: }| {MSEP: }|
: | : |
{ERR:E5407: Callback has thrown an exception:}| {ERR:E5407: Callback has thrown an exception:}|
{ERR: function DoPrompt[3]..Throwing, line 1:}|
{ERR: ABC} | {ERR: ABC} |
:e^ | :e^ |
]]) ]])

View File

@@ -5,6 +5,7 @@ local neq, eq, command = helpers.neq, helpers.eq, helpers.command
local clear, curbufmeths = helpers.clear, helpers.curbufmeths local clear, curbufmeths = helpers.clear, helpers.curbufmeths
local exc_exec, expect, eval = helpers.exc_exec, helpers.expect, helpers.eval local exc_exec, expect, eval = helpers.exc_exec, helpers.expect, helpers.eval
local insert, pcall_err = helpers.insert, helpers.pcall_err local insert, pcall_err = helpers.insert, helpers.pcall_err
local matches = helpers.matches
local meths = helpers.meths local meths = helpers.meths
describe('eval-API', function() describe('eval-API', function()
@@ -49,7 +50,7 @@ describe('eval-API', function()
it('cannot change texts if textlocked', function() it('cannot change texts if textlocked', function()
command("autocmd TextYankPost <buffer> ++once call nvim_buf_set_lines(0, 0, -1, v:false, [])") command("autocmd TextYankPost <buffer> ++once call nvim_buf_set_lines(0, 0, -1, v:false, [])")
eq('Vim(call):E5555: API call: E565: Not allowed to change text or change window', matches('Vim%(call%):E5555: API call: E565: Not allowed to change text or change window$',
pcall_err(command, "normal! yy")) pcall_err(command, "normal! yy"))
end) end)

View File

@@ -173,9 +173,9 @@ describe('context functions', function()
call('SaveSFuncs') call('SaveSFuncs')
call('DeleteSFuncs') call('DeleteSFuncs')
eq('Vim(call):E117: Unknown function: s:greet', eq('function Greet, line 1: Vim(call):E117: Unknown function: s:greet',
pcall_err(command, [[call Greet('World')]])) pcall_err(command, [[call Greet('World')]]))
eq('Vim(call):E117: Unknown function: s:greet_all', eq('function GreetAll, line 1: Vim(call):E117: Unknown function: s:greet_all',
pcall_err(command, [[call GreetAll('World', 'One', 'Two', 'Three')]])) pcall_err(command, [[call GreetAll('World', 'One', 'Two', 'Three')]]))
call('RestoreFuncs') call('RestoreFuncs')