mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge #8371 'API: more reliable/descriptive VimL errors'
This commit is contained in:
commit
8d40b3617c
@ -40,9 +40,10 @@ function s:msgpack_init_python() abort
|
|||||||
return s:msgpack_python_type
|
return s:msgpack_python_type
|
||||||
endif
|
endif
|
||||||
let s:msgpack_python_initialized = 1
|
let s:msgpack_python_initialized = 1
|
||||||
for suf in ['', '3']
|
for suf in (has('win32') ? ['3'] : ['', '3'])
|
||||||
try
|
try
|
||||||
execute 'python' . suf
|
execute 'python' . suf
|
||||||
|
\. "\n"
|
||||||
\. "def shada_dict_strftime():\n"
|
\. "def shada_dict_strftime():\n"
|
||||||
\. " import datetime\n"
|
\. " import datetime\n"
|
||||||
\. " import vim\n"
|
\. " import vim\n"
|
||||||
@ -60,12 +61,15 @@ function s:msgpack_init_python() abort
|
|||||||
\. " fmt = vim.eval('a:format')\n"
|
\. " fmt = vim.eval('a:format')\n"
|
||||||
\. " timestr = vim.eval('a:string')\n"
|
\. " timestr = vim.eval('a:string')\n"
|
||||||
\. " timestamp = datetime.datetime.strptime(timestr, fmt)\n"
|
\. " timestamp = datetime.datetime.strptime(timestr, fmt)\n"
|
||||||
\. " timestamp = int(timestamp.timestamp())\n"
|
\. " try:\n"
|
||||||
|
\. " timestamp = int(timestamp.timestamp())\n"
|
||||||
|
\. " except:\n"
|
||||||
|
\. " timestamp = int(timestamp.strftime('%s'))\n"
|
||||||
\. " if timestamp > 2 ** 31:\n"
|
\. " if timestamp > 2 ** 31:\n"
|
||||||
\. " tsabs = abs(timestamp)"
|
\. " tsabs = abs(timestamp)\n"
|
||||||
\. " return ('{\"_TYPE\": v:msgpack_types.integer,'\n"
|
\. " return ('{\"_TYPE\": v:msgpack_types.integer,'\n"
|
||||||
\. " + '\"_VAL\": [{sign},{v1},{v2},{v3}]}').format(\n"
|
\. " + '\"_VAL\": [{sign},{v1},{v2},{v3}]}').format(\n"
|
||||||
\. " sign=1 if timestamp >= 0 else -1,\n"
|
\. " sign=(1 if timestamp >= 0 else -1),\n"
|
||||||
\. " v1=((tsabs >> 62) & 0x3),\n"
|
\. " v1=((tsabs >> 62) & 0x3),\n"
|
||||||
\. " v2=((tsabs >> 31) & (2 ** 31 - 1)),\n"
|
\. " v2=((tsabs >> 31) & (2 ** 31 - 1)),\n"
|
||||||
\. " v3=(tsabs & (2 ** 31 - 1)))\n"
|
\. " v3=(tsabs & (2 ** 31 - 1)))\n"
|
||||||
|
@ -120,9 +120,7 @@ bool try_end(Error *err)
|
|||||||
// try_enter/try_leave.
|
// try_enter/try_leave.
|
||||||
trylevel--;
|
trylevel--;
|
||||||
|
|
||||||
// Without this it stops processing all subsequent VimL commands and
|
// Set by emsg(), affects aborting(). See also enter_cleanup().
|
||||||
// generates strange error messages if I e.g. try calling Test() in a
|
|
||||||
// cycle
|
|
||||||
did_emsg = false;
|
did_emsg = false;
|
||||||
|
|
||||||
if (got_int) {
|
if (got_int) {
|
||||||
|
@ -46,8 +46,7 @@
|
|||||||
|
|
||||||
/// Executes an ex-command.
|
/// Executes an ex-command.
|
||||||
///
|
///
|
||||||
/// On parse error: forwards the Vim error; does not update v:errmsg.
|
/// On execution error: fails with VimL error, does not update v:errmsg.
|
||||||
/// On runtime error: forwards the Vim error; does not update v:errmsg.
|
|
||||||
///
|
///
|
||||||
/// @param command Ex-command string
|
/// @param command Ex-command string
|
||||||
/// @param[out] err Error details (Vim error), if any
|
/// @param[out] err Error details (Vim error), if any
|
||||||
@ -103,7 +102,8 @@ Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Error *err)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Passes input keys to Nvim.
|
/// Passes input keys to Nvim.
|
||||||
/// On VimL error: Does not fail, but updates v:errmsg.
|
///
|
||||||
|
/// On execution error: does not fail, but updates v:errmsg.
|
||||||
///
|
///
|
||||||
/// @param keys to be typed
|
/// @param keys to be typed
|
||||||
/// @param mode mapping options
|
/// @param mode mapping options
|
||||||
@ -169,7 +169,8 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Passes keys to Nvim as raw user-input.
|
/// Passes keys to Nvim as raw user-input.
|
||||||
/// On VimL error: Does not fail, but updates v:errmsg.
|
///
|
||||||
|
/// On execution error: does not fail, but updates v:errmsg.
|
||||||
///
|
///
|
||||||
/// Unlike `nvim_feedkeys`, this uses a lower-level input buffer and the call
|
/// Unlike `nvim_feedkeys`, this uses a lower-level input buffer and the call
|
||||||
/// is not deferred. This is the most reliable way to send real user input.
|
/// is not deferred. This is the most reliable way to send real user input.
|
||||||
@ -213,8 +214,7 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt,
|
|||||||
/// Executes an ex-command and returns its (non-error) output.
|
/// Executes an ex-command and returns its (non-error) output.
|
||||||
/// Shell |:!| output is not captured.
|
/// Shell |:!| output is not captured.
|
||||||
///
|
///
|
||||||
/// On parse error: forwards the Vim error; does not update v:errmsg.
|
/// On execution error: fails with VimL error, does not update v:errmsg.
|
||||||
/// On runtime error: forwards the Vim error; does not update v:errmsg.
|
|
||||||
///
|
///
|
||||||
/// @param command Ex-command string
|
/// @param command Ex-command string
|
||||||
/// @param[out] err Error details (Vim error), if any
|
/// @param[out] err Error details (Vim error), if any
|
||||||
@ -259,7 +259,8 @@ theend:
|
|||||||
|
|
||||||
/// Evaluates a VimL expression (:help expression).
|
/// Evaluates a VimL expression (:help expression).
|
||||||
/// Dictionaries and Lists are recursively expanded.
|
/// Dictionaries and Lists are recursively expanded.
|
||||||
/// On VimL error: Returns a generic error; v:errmsg is not updated.
|
///
|
||||||
|
/// On execution error: fails with VimL error, does not update v:errmsg.
|
||||||
///
|
///
|
||||||
/// @param expr VimL expression string
|
/// @param expr VimL expression string
|
||||||
/// @param[out] err Error details, if any
|
/// @param[out] err Error details, if any
|
||||||
@ -267,22 +268,42 @@ theend:
|
|||||||
Object nvim_eval(String expr, Error *err)
|
Object nvim_eval(String expr, Error *err)
|
||||||
FUNC_API_SINCE(1)
|
FUNC_API_SINCE(1)
|
||||||
{
|
{
|
||||||
|
static int recursive = 0; // recursion depth
|
||||||
Object rv = OBJECT_INIT;
|
Object rv = OBJECT_INIT;
|
||||||
// Evaluate the expression
|
|
||||||
|
// `msg_list` controls the collection of abort-causing non-exception errors,
|
||||||
|
// which would otherwise be ignored. This pattern is from do_cmdline().
|
||||||
|
struct msglist **saved_msg_list = msg_list;
|
||||||
|
struct msglist *private_msg_list;
|
||||||
|
msg_list = &private_msg_list;
|
||||||
|
private_msg_list = NULL;
|
||||||
|
|
||||||
|
// Initialize `force_abort` and `suppress_errthrow` at the top level.
|
||||||
|
if (!recursive) {
|
||||||
|
force_abort = false;
|
||||||
|
suppress_errthrow = false;
|
||||||
|
current_exception = NULL;
|
||||||
|
// `did_emsg` is set by emsg(), which cancels execution.
|
||||||
|
did_emsg = false;
|
||||||
|
}
|
||||||
|
recursive++;
|
||||||
try_start();
|
try_start();
|
||||||
|
|
||||||
typval_T rettv;
|
typval_T rettv;
|
||||||
if (eval0((char_u *)expr.data, &rettv, NULL, true) == FAIL) {
|
int ok = eval0((char_u *)expr.data, &rettv, NULL, true);
|
||||||
api_set_error(err, kErrorTypeException, "Failed to evaluate expression");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!try_end(err)) {
|
if (!try_end(err)) {
|
||||||
// No errors, convert the result
|
if (ok == FAIL) {
|
||||||
rv = vim_to_object(&rettv);
|
// Should never happen, try_end() should get the error. #8371
|
||||||
|
api_set_error(err, kErrorTypeException, "Failed to evaluate expression");
|
||||||
|
} else {
|
||||||
|
rv = vim_to_object(&rettv);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the Vim object
|
|
||||||
tv_clear(&rettv);
|
tv_clear(&rettv);
|
||||||
|
msg_list = saved_msg_list; // Restore the exception context.
|
||||||
|
recursive--;
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@ -314,7 +335,9 @@ Object nvim_execute_lua(String code, Array args, Error *err)
|
|||||||
/// @return Result of the function call
|
/// @return Result of the function call
|
||||||
static Object _call_function(String fn, Array args, dict_T *self, Error *err)
|
static Object _call_function(String fn, Array args, dict_T *self, Error *err)
|
||||||
{
|
{
|
||||||
|
static int recursive = 0; // recursion depth
|
||||||
Object rv = OBJECT_INIT;
|
Object rv = OBJECT_INIT;
|
||||||
|
|
||||||
if (args.size > MAX_FUNC_ARGS) {
|
if (args.size > MAX_FUNC_ARGS) {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
api_set_error(err, kErrorTypeValidation,
|
||||||
"Function called with too many arguments");
|
"Function called with too many arguments");
|
||||||
@ -330,20 +353,36 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `msg_list` controls the collection of abort-causing non-exception errors,
|
||||||
|
// which would otherwise be ignored. This pattern is from do_cmdline().
|
||||||
|
struct msglist **saved_msg_list = msg_list;
|
||||||
|
struct msglist *private_msg_list;
|
||||||
|
msg_list = &private_msg_list;
|
||||||
|
private_msg_list = NULL;
|
||||||
|
|
||||||
|
// Initialize `force_abort` and `suppress_errthrow` at the top level.
|
||||||
|
if (!recursive) {
|
||||||
|
force_abort = false;
|
||||||
|
suppress_errthrow = false;
|
||||||
|
current_exception = NULL;
|
||||||
|
// `did_emsg` is set by emsg(), which cancels execution.
|
||||||
|
did_emsg = false;
|
||||||
|
}
|
||||||
|
recursive++;
|
||||||
try_start();
|
try_start();
|
||||||
// Call the function
|
|
||||||
typval_T rettv;
|
typval_T rettv;
|
||||||
int dummy;
|
int dummy;
|
||||||
int r = call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size,
|
// call_func() retval is deceptive, ignore it. Instead we set `msg_list`
|
||||||
vim_args, NULL, curwin->w_cursor.lnum,
|
// (see above) to capture abort-causing non-exception errors.
|
||||||
curwin->w_cursor.lnum, &dummy, true, NULL, self);
|
(void)call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size,
|
||||||
if (r == FAIL) {
|
vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||||
api_set_error(err, kErrorTypeException, "Error calling function");
|
&dummy, true, NULL, self);
|
||||||
}
|
|
||||||
if (!try_end(err)) {
|
if (!try_end(err)) {
|
||||||
rv = vim_to_object(&rettv);
|
rv = vim_to_object(&rettv);
|
||||||
}
|
}
|
||||||
tv_clear(&rettv);
|
tv_clear(&rettv);
|
||||||
|
msg_list = saved_msg_list; // Restore the exception context.
|
||||||
|
recursive--;
|
||||||
|
|
||||||
free_vim_args:
|
free_vim_args:
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
@ -355,7 +394,7 @@ free_vim_args:
|
|||||||
|
|
||||||
/// Calls a VimL function with the given arguments.
|
/// Calls a VimL function with the given arguments.
|
||||||
///
|
///
|
||||||
/// On VimL error: Returns a generic error; v:errmsg is not updated.
|
/// On execution error: fails with VimL error, does not update v:errmsg.
|
||||||
///
|
///
|
||||||
/// @param fn Function to call
|
/// @param fn Function to call
|
||||||
/// @param args Function arguments packed in an Array
|
/// @param args Function arguments packed in an Array
|
||||||
@ -369,6 +408,8 @@ Object nvim_call_function(String fn, Array args, Error *err)
|
|||||||
|
|
||||||
/// Calls a VimL |Dictionary-function| with the given arguments.
|
/// Calls a VimL |Dictionary-function| with the given arguments.
|
||||||
///
|
///
|
||||||
|
/// On execution error: fails with VimL error, does not update v:errmsg.
|
||||||
|
///
|
||||||
/// @param dict Dictionary, or String evaluating to a VimL |self| dict
|
/// @param dict Dictionary, or String evaluating to a VimL |self| dict
|
||||||
/// @param fn Name of the function defined on the VimL dict
|
/// @param fn Name of the function defined on the VimL dict
|
||||||
/// @param args Function arguments packed in an Array
|
/// @param args Function arguments packed in an Array
|
||||||
@ -934,27 +975,26 @@ Array nvim_get_api_info(uint64_t channel_id)
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call many api methods atomically
|
/// Calls many API methods atomically.
|
||||||
///
|
///
|
||||||
/// This has two main usages: Firstly, to perform several requests from an
|
/// This has two main usages:
|
||||||
/// async context atomically, i.e. without processing requests from other rpc
|
/// 1. To perform several requests from an async context atomically, i.e.
|
||||||
/// clients or redrawing or allowing user interaction in between. Note that api
|
/// without interleaving redraws, RPC requests from other clients, or user
|
||||||
/// methods that could fire autocommands or do event processing still might do
|
/// interactions (however API methods may trigger autocommands or event
|
||||||
/// so. For instance invoking the :sleep command might call timer callbacks.
|
/// processing which have such side-effects, e.g. |:sleep| may wake timers).
|
||||||
/// Secondly, it can be used to reduce rpc overhead (roundtrips) when doing
|
/// 2. To minimize RPC overhead (roundtrips) of a sequence of many requests.
|
||||||
/// many requests in sequence.
|
|
||||||
///
|
///
|
||||||
/// @param calls an array of calls, where each call is described by an array
|
/// @param calls an array of calls, where each call is described by an array
|
||||||
/// with two elements: the request name, and an array of arguments.
|
/// with two elements: the request name, and an array of arguments.
|
||||||
/// @param[out] err Details of a validation error of the nvim_multi_request call
|
/// @param[out] err Details of a validation error of the nvim_multi_request call
|
||||||
/// itself, i e malformatted `calls` parameter. Errors from called methods will
|
/// itself, i.e. malformed `calls` parameter. Errors from called methods will
|
||||||
/// be indicated in the return value, see below.
|
/// be indicated in the return value, see below.
|
||||||
///
|
///
|
||||||
/// @return an array with two elements. The first is an array of return
|
/// @return an array with two elements. The first is an array of return
|
||||||
/// values. The second is NIL if all calls succeeded. If a call resulted in
|
/// values. The second is NIL if all calls succeeded. If a call resulted in
|
||||||
/// an error, it is a three-element array with the zero-based index of the call
|
/// an error, it is a three-element array with the zero-based index of the call
|
||||||
/// which resulted in an error, the error type and the error message. If an
|
/// which resulted in an error, the error type and the error message. If an
|
||||||
/// error ocurred, the values from all preceding calls will still be returned.
|
/// error occurred, the values from all preceding calls will still be returned.
|
||||||
Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
|
Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
|
||||||
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
|
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
|
||||||
{
|
{
|
||||||
|
@ -6241,20 +6241,21 @@ bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
|
|||||||
/// invoked function uses them. It is called like this:
|
/// invoked function uses them. It is called like this:
|
||||||
/// new_argcount = argv_func(current_argcount, argv, called_func_argcount)
|
/// new_argcount = argv_func(current_argcount, argv, called_func_argcount)
|
||||||
///
|
///
|
||||||
/// Return FAIL when the function can't be called, OK otherwise.
|
/// @return FAIL if function cannot be called, else OK (even if an error
|
||||||
/// Also returns OK when an error was encountered while executing the function.
|
/// occurred while executing the function! Set `msg_list` to capture
|
||||||
|
/// the error, see do_cmdline()).
|
||||||
int
|
int
|
||||||
call_func(
|
call_func(
|
||||||
const char_u *funcname, // name of the function
|
const char_u *funcname, // name of the function
|
||||||
int len, // length of "name"
|
int len, // length of "name"
|
||||||
typval_T *rettv, // return value goes here
|
typval_T *rettv, // [out] value goes here
|
||||||
int argcount_in, // number of "argvars"
|
int argcount_in, // number of "argvars"
|
||||||
typval_T *argvars_in, // vars for arguments, must have "argcount"
|
typval_T *argvars_in, // vars for arguments, must have "argcount"
|
||||||
// PLUS ONE elements!
|
// PLUS ONE elements!
|
||||||
ArgvFunc argv_func, // function to fill in argvars
|
ArgvFunc argv_func, // function to fill in argvars
|
||||||
linenr_T firstline, // first line of range
|
linenr_T firstline, // first line of range
|
||||||
linenr_T lastline, // last line of range
|
linenr_T lastline, // last line of range
|
||||||
int *doesrange, // return: function handled range
|
int *doesrange, // [out] function handled range
|
||||||
bool evaluate,
|
bool evaluate,
|
||||||
partial_T *partial, // optional, can be NULL
|
partial_T *partial, // optional, can be NULL
|
||||||
dict_T *selfdict_in // Dictionary for "self"
|
dict_T *selfdict_in // Dictionary for "self"
|
||||||
@ -6428,21 +6429,25 @@ call_func(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// Give an error message with a function name. Handle <SNR> things.
|
||||||
* Give an error message with a function name. Handle <SNR> things.
|
///
|
||||||
* "ermsg" is to be passed without translation, use N_() instead of _().
|
/// @param ermsg must be passed without translation (use N_() instead of _()).
|
||||||
*/
|
/// @param name function name
|
||||||
static void emsg_funcname(char *ermsg, char_u *name)
|
static void emsg_funcname(char *ermsg, char_u *name)
|
||||||
{
|
{
|
||||||
char_u *p;
|
char_u *p;
|
||||||
|
|
||||||
if (*name == K_SPECIAL)
|
if (*name == K_SPECIAL) {
|
||||||
p = concat_str((char_u *)"<SNR>", name + 3);
|
p = concat_str((char_u *)"<SNR>", name + 3);
|
||||||
else
|
} else {
|
||||||
p = name;
|
p = name;
|
||||||
|
}
|
||||||
|
|
||||||
EMSG2(_(ermsg), p);
|
EMSG2(_(ermsg), p);
|
||||||
if (p != name)
|
|
||||||
|
if (p != name) {
|
||||||
xfree(p);
|
xfree(p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -28,22 +28,21 @@
|
|||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "ex_eval.c.generated.h"
|
# include "ex_eval.c.generated.h"
|
||||||
#endif
|
#endif
|
||||||
/*
|
|
||||||
* Exception handling terms:
|
// Exception handling terms:
|
||||||
*
|
//
|
||||||
* :try ":try" command \
|
// :try ":try" command ─┐
|
||||||
* ... try block |
|
// ... try block │
|
||||||
* :catch RE ":catch" command |
|
// :catch RE ":catch" command │
|
||||||
* ... catch clause |- try conditional
|
// ... catch clause ├─ try conditional
|
||||||
* :finally ":finally" command |
|
// :finally ":finally" command │
|
||||||
* ... finally clause |
|
// ... finally clause │
|
||||||
* :endtry ":endtry" command /
|
// :endtry ":endtry" command ─┘
|
||||||
*
|
//
|
||||||
* The try conditional may have any number of catch clauses and at most one
|
// The try conditional may have any number of catch clauses and at most one
|
||||||
* finally clause. A ":throw" command can be inside the try block, a catch
|
// finally clause. A ":throw" command can be inside the try block, a catch
|
||||||
* clause, the finally clause, or in a function called or script sourced from
|
// clause, the finally clause, or in a function called or script sourced from
|
||||||
* there or even outside the try conditional. Try conditionals may be nested.
|
// there or even outside the try conditional. Try conditionals may be nested.
|
||||||
*/
|
|
||||||
|
|
||||||
// Configuration whether an exception is thrown on error or interrupt. When
|
// Configuration whether an exception is thrown on error or interrupt. When
|
||||||
// the preprocessor macros below evaluate to FALSE, an error (did_emsg) or
|
// the preprocessor macros below evaluate to FALSE, an error (did_emsg) or
|
||||||
|
@ -11,6 +11,7 @@ local ok = helpers.ok
|
|||||||
local meths = helpers.meths
|
local meths = helpers.meths
|
||||||
local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv
|
local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv
|
||||||
local set_session = helpers.set_session
|
local set_session = helpers.set_session
|
||||||
|
local expect_err = helpers.expect_err
|
||||||
|
|
||||||
describe('server -> client', function()
|
describe('server -> client', function()
|
||||||
local cid
|
local cid
|
||||||
@ -221,9 +222,8 @@ describe('server -> client', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('returns an error if the request failed', function()
|
it('returns an error if the request failed', function()
|
||||||
local status, err = pcall(eval, "rpcrequest(vim, 'does-not-exist')")
|
expect_err('Vim:Invalid method name',
|
||||||
eq(false, status)
|
eval, "rpcrequest(vim, 'does-not-exist')")
|
||||||
ok(nil ~= string.match(err, 'Failed to evaluate expression'))
|
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -250,7 +250,7 @@ describe('server -> client', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
after_each(function()
|
after_each(function()
|
||||||
funcs.jobstop(jobid)
|
pcall(funcs.jobstop, jobid)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if helpers.pending_win32(pending) then return end
|
if helpers.pending_win32(pending) then return end
|
||||||
@ -261,7 +261,7 @@ describe('server -> client', function()
|
|||||||
eq({'notification', 'pong', {}}, next_msg())
|
eq({'notification', 'pong', {}}, next_msg())
|
||||||
eq("done!",funcs.rpcrequest(jobid, "write_stderr", "fluff\n"))
|
eq("done!",funcs.rpcrequest(jobid, "write_stderr", "fluff\n"))
|
||||||
eq({'notification', 'stderr', {0, {'fluff', ''}}}, next_msg())
|
eq({'notification', 'stderr', {0, {'fluff', ''}}}, next_msg())
|
||||||
funcs.rpcrequest(jobid, "exit")
|
pcall(funcs.rpcrequest, jobid, "exit")
|
||||||
eq({'notification', 'stderr', {0, {''}}}, next_msg())
|
eq({'notification', 'stderr', {0, {''}}}, next_msg())
|
||||||
eq({'notification', 'exit', {0, 0}}, next_msg())
|
eq({'notification', 'exit', {0, 0}}, next_msg())
|
||||||
end)
|
end)
|
||||||
@ -308,8 +308,8 @@ describe('server -> client', function()
|
|||||||
it('via ipv4 address', function()
|
it('via ipv4 address', function()
|
||||||
local server = spawn(nvim_argv)
|
local server = spawn(nvim_argv)
|
||||||
set_session(server)
|
set_session(server)
|
||||||
local address = funcs.serverstart("127.0.0.1:")
|
local status, address = pcall(funcs.serverstart, "127.0.0.1:")
|
||||||
if #address == 0 then
|
if not status then
|
||||||
pending('no ipv4 stack', function() end)
|
pending('no ipv4 stack', function() end)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@ -320,8 +320,8 @@ describe('server -> client', function()
|
|||||||
it('via ipv6 address', function()
|
it('via ipv6 address', function()
|
||||||
local server = spawn(nvim_argv)
|
local server = spawn(nvim_argv)
|
||||||
set_session(server)
|
set_session(server)
|
||||||
local address = funcs.serverstart('::1:')
|
local status, address = pcall(funcs.serverstart, '::1:')
|
||||||
if #address == 0 then
|
if not status then
|
||||||
pending('no ipv6 stack', function() end)
|
pending('no ipv6 stack', function() end)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -5,6 +5,7 @@ local global_helpers = require('test.helpers')
|
|||||||
local NIL = helpers.NIL
|
local NIL = helpers.NIL
|
||||||
local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq
|
local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq
|
||||||
local command = helpers.command
|
local command = helpers.command
|
||||||
|
local eval = helpers.eval
|
||||||
local funcs = helpers.funcs
|
local funcs = helpers.funcs
|
||||||
local iswin = helpers.iswin
|
local iswin = helpers.iswin
|
||||||
local meth_pcall = helpers.meth_pcall
|
local meth_pcall = helpers.meth_pcall
|
||||||
@ -40,20 +41,20 @@ describe('api', function()
|
|||||||
os.remove(fname)
|
os.remove(fname)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("parse error: fails (specific error), does NOT update v:errmsg", function()
|
it('VimL validation error: fails with specific error', function()
|
||||||
-- Most API methods return generic errors (or no error) if a VimL
|
|
||||||
-- expression fails; nvim_command returns the VimL error details.
|
|
||||||
local status, rv = pcall(nvim, "command", "bogus_command")
|
local status, rv = pcall(nvim, "command", "bogus_command")
|
||||||
eq(false, status) -- nvim_command() failed.
|
eq(false, status) -- nvim_command() failed.
|
||||||
eq("E492:", string.match(rv, "E%d*:")) -- VimL error was returned.
|
eq("E492:", string.match(rv, "E%d*:")) -- VimL error was returned.
|
||||||
eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
|
eq('', nvim('eval', 'v:errmsg')) -- v:errmsg was not updated.
|
||||||
|
eq('', eval('v:exception'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("runtime error: fails (specific error)", function()
|
it('VimL execution error: fails with specific error', function()
|
||||||
local status, rv = pcall(nvim, "command_output", "buffer 23487")
|
local status, rv = pcall(nvim, "command_output", "buffer 23487")
|
||||||
eq(false, status) -- nvim_command() failed.
|
eq(false, status) -- nvim_command() failed.
|
||||||
eq("E86: Buffer 23487 does not exist", string.match(rv, "E%d*:.*"))
|
eq("E86: Buffer 23487 does not exist", string.match(rv, "E%d*:.*"))
|
||||||
eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
|
eq('', eval('v:errmsg')) -- v:errmsg was not updated.
|
||||||
|
eq('', eval('v:exception'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -109,21 +110,21 @@ describe('api', function()
|
|||||||
eq(':!echo foo\r\n\nfoo'..win_lf..'\n', nvim('command_output', [[!echo foo]]))
|
eq(':!echo foo\r\n\nfoo'..win_lf..'\n', nvim('command_output', [[!echo foo]]))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("parse error: fails (specific error), does NOT update v:errmsg", function()
|
it('VimL validation error: fails with specific error', function()
|
||||||
local status, rv = pcall(nvim, "command_output", "bogus commannnd")
|
local status, rv = pcall(nvim, "command_output", "bogus commannnd")
|
||||||
eq(false, status) -- nvim_command_output() failed.
|
eq(false, status) -- nvim_command_output() failed.
|
||||||
eq("E492: Not an editor command: bogus commannnd",
|
eq("E492: Not an editor command: bogus commannnd",
|
||||||
string.match(rv, "E%d*:.*"))
|
string.match(rv, "E%d*:.*"))
|
||||||
eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
|
eq('', eval('v:errmsg')) -- v:errmsg was not updated.
|
||||||
-- Verify NO hit-enter prompt.
|
-- Verify NO hit-enter prompt.
|
||||||
eq({mode='n', blocking=false}, nvim("get_mode"))
|
eq({mode='n', blocking=false}, nvim("get_mode"))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("runtime error: fails (specific error)", function()
|
it('VimL execution error: fails with specific error', function()
|
||||||
local status, rv = pcall(nvim, "command_output", "buffer 42")
|
local status, rv = pcall(nvim, "command_output", "buffer 42")
|
||||||
eq(false, status) -- nvim_command_output() failed.
|
eq(false, status) -- nvim_command_output() failed.
|
||||||
eq("E86: Buffer 42 does not exist", string.match(rv, "E%d*:.*"))
|
eq("E86: Buffer 42 does not exist", string.match(rv, "E%d*:.*"))
|
||||||
eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
|
eq('', eval('v:errmsg')) -- v:errmsg was not updated.
|
||||||
-- Verify NO hit-enter prompt.
|
-- Verify NO hit-enter prompt.
|
||||||
eq({mode='n', blocking=false}, nvim("get_mode"))
|
eq({mode='n', blocking=false}, nvim("get_mode"))
|
||||||
end)
|
end)
|
||||||
@ -145,11 +146,10 @@ describe('api', function()
|
|||||||
eq(2, request("vim_eval", "1+1"))
|
eq(2, request("vim_eval", "1+1"))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("VimL error: fails (generic error), does NOT update v:errmsg", function()
|
it("VimL error: returns error details, does NOT update v:errmsg", function()
|
||||||
local status, rv = pcall(nvim, "eval", "bogus expression")
|
expect_err('E121: Undefined variable: bogus', request,
|
||||||
eq(false, status) -- nvim_eval() failed.
|
'nvim_eval', 'bogus expression')
|
||||||
ok(nil ~= string.find(rv, "Failed to evaluate expression"))
|
eq('', eval('v:errmsg')) -- v:errmsg was not updated.
|
||||||
eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
|
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -160,12 +160,39 @@ describe('api', function()
|
|||||||
eq(17, nvim('call_function', 'eval', {17}))
|
eq(17, nvim('call_function', 'eval', {17}))
|
||||||
eq('foo', nvim('call_function', 'simplify', {'this/./is//redundant/../../../foo'}))
|
eq('foo', nvim('call_function', 'simplify', {'this/./is//redundant/../../../foo'}))
|
||||||
end)
|
end)
|
||||||
it("VimL error: fails (generic error), does NOT update v:errmsg", function()
|
|
||||||
local status, rv = pcall(nvim, "call_function", "bogus function", {"arg1"})
|
it("VimL validation error: returns specific error, does NOT update v:errmsg", function()
|
||||||
eq(false, status) -- nvim_call_function() failed.
|
expect_err('E117: Unknown function: bogus function', request,
|
||||||
ok(nil ~= string.find(rv, "Error calling function"))
|
'nvim_call_function', 'bogus function', {'arg1'})
|
||||||
eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
|
expect_err('E119: Not enough arguments for function: atan', request,
|
||||||
|
'nvim_call_function', 'atan', {})
|
||||||
|
eq('', eval('v:exception'))
|
||||||
|
eq('', eval('v:errmsg')) -- v:errmsg was not updated.
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it("VimL error: returns error details, does NOT update v:errmsg", function()
|
||||||
|
expect_err('E808: Number or Float required', request,
|
||||||
|
'nvim_call_function', 'atan', {'foo'})
|
||||||
|
expect_err('Invalid channel stream "xxx"', request,
|
||||||
|
'nvim_call_function', 'chanclose', {999, 'xxx'})
|
||||||
|
expect_err('E900: Invalid channel id', request,
|
||||||
|
'nvim_call_function', 'chansend', {999, 'foo'})
|
||||||
|
eq('', eval('v:exception'))
|
||||||
|
eq('', eval('v:errmsg')) -- v:errmsg was not updated.
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("VimL exception: returns exception details, does NOT update v:errmsg", function()
|
||||||
|
source([[
|
||||||
|
function! Foo() abort
|
||||||
|
throw 'wtf'
|
||||||
|
endfunction
|
||||||
|
]])
|
||||||
|
expect_err('wtf', request,
|
||||||
|
'nvim_call_function', 'Foo', {})
|
||||||
|
eq('', eval('v:exception'))
|
||||||
|
eq('', eval('v:errmsg')) -- v:errmsg was not updated.
|
||||||
|
end)
|
||||||
|
|
||||||
it('validates args', function()
|
it('validates args', function()
|
||||||
local too_many_args = { 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x' }
|
local too_many_args = { 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x' }
|
||||||
source([[
|
source([[
|
||||||
@ -216,7 +243,7 @@ describe('api', function()
|
|||||||
'nvim_call_dict_function', 'g:d', 'baz', {1,2})
|
'nvim_call_dict_function', 'g:d', 'baz', {1,2})
|
||||||
expect_err('Not a function: meep', request,
|
expect_err('Not a function: meep', request,
|
||||||
'nvim_call_dict_function', 'g:d', 'meep', {1,2})
|
'nvim_call_dict_function', 'g:d', 'meep', {1,2})
|
||||||
expect_err('Error calling function', request,
|
expect_err('E117: Unknown function: f', request,
|
||||||
'nvim_call_dict_function', { f = '' }, 'f', {1,2})
|
'nvim_call_dict_function', { f = '' }, 'f', {1,2})
|
||||||
expect_err('Not a function: f', request,
|
expect_err('Not a function: f', request,
|
||||||
'nvim_call_dict_function', "{ 'f': '' }", 'f', {1,2})
|
'nvim_call_dict_function', "{ 'f': '' }", 'f', {1,2})
|
||||||
|
@ -15,6 +15,7 @@ local curwinmeths = helpers.curwinmeths
|
|||||||
local curtabmeths = helpers.curtabmeths
|
local curtabmeths = helpers.curtabmeths
|
||||||
local get_pathsep = helpers.get_pathsep
|
local get_pathsep = helpers.get_pathsep
|
||||||
local rmdir = helpers.rmdir
|
local rmdir = helpers.rmdir
|
||||||
|
local expect_err = helpers.expect_err
|
||||||
|
|
||||||
local fname = 'Xtest-functional-eval-buf_functions'
|
local fname = 'Xtest-functional-eval-buf_functions'
|
||||||
local fname2 = fname .. '.2'
|
local fname2 = fname .. '.2'
|
||||||
@ -296,8 +297,8 @@ describe('setbufvar() function', function()
|
|||||||
eq('Vim(call):E461: Illegal variable name: b:',
|
eq('Vim(call):E461: Illegal variable name: b:',
|
||||||
exc_exec('call setbufvar(1, "", 0)'))
|
exc_exec('call setbufvar(1, "", 0)'))
|
||||||
eq(true, bufmeths.get_var(buf1, 'number'))
|
eq(true, bufmeths.get_var(buf1, 'number'))
|
||||||
funcs.setbufvar(1, 'changedtick', true)
|
expect_err('Vim:E46: Cannot change read%-only variable "b:changedtick"',
|
||||||
-- eq(true, bufmeths.get_var(buf1, 'changedtick'))
|
funcs.setbufvar, 1, 'changedtick', true)
|
||||||
eq(2, funcs.getbufvar(1, 'changedtick'))
|
eq(2, funcs.getbufvar(1, 'changedtick'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
@ -6,6 +6,7 @@ local clear = helpers.clear
|
|||||||
local funcs = helpers.funcs
|
local funcs = helpers.funcs
|
||||||
local command = helpers.command
|
local command = helpers.command
|
||||||
local exc_exec = helpers.exc_exec
|
local exc_exec = helpers.exc_exec
|
||||||
|
local expect_err = helpers.expect_err
|
||||||
|
|
||||||
before_each(clear)
|
before_each(clear)
|
||||||
|
|
||||||
@ -40,9 +41,11 @@ describe('setmatches()', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails with -1 if highlight group is not defined', function()
|
it('fails with -1 if highlight group is not defined', function()
|
||||||
eq(-1, funcs.setmatches({{group=1, pattern=2, id=3, priority=4}}))
|
expect_err('E28: No such highlight group name: 1', funcs.setmatches,
|
||||||
|
{{group=1, pattern=2, id=3, priority=4}})
|
||||||
eq({}, funcs.getmatches())
|
eq({}, funcs.getmatches())
|
||||||
eq(-1, funcs.setmatches({{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}}))
|
expect_err('E28: No such highlight group name: 1', funcs.setmatches,
|
||||||
|
{{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}})
|
||||||
eq({}, funcs.getmatches())
|
eq({}, funcs.getmatches())
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
@ -5,6 +5,7 @@ local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths
|
|||||||
local iswin = helpers.iswin
|
local iswin = helpers.iswin
|
||||||
local ok = helpers.ok
|
local ok = helpers.ok
|
||||||
local matches = helpers.matches
|
local matches = helpers.matches
|
||||||
|
local expect_err = helpers.expect_err
|
||||||
|
|
||||||
local function clear_serverlist()
|
local function clear_serverlist()
|
||||||
for _, server in pairs(funcs.serverlist()) do
|
for _, server in pairs(funcs.serverlist()) do
|
||||||
@ -86,22 +87,23 @@ describe('server', function()
|
|||||||
|
|
||||||
local expected = {}
|
local expected = {}
|
||||||
local v4 = '127.0.0.1:12345'
|
local v4 = '127.0.0.1:12345'
|
||||||
s = funcs.serverstart(v4)
|
local status, _ = pcall(funcs.serverstart, v4)
|
||||||
if #s > 0 then
|
if status then
|
||||||
table.insert(expected, v4)
|
table.insert(expected, v4)
|
||||||
funcs.serverstart(v4) -- exists already; ignore
|
pcall(funcs.serverstart, v4) -- exists already; ignore
|
||||||
end
|
end
|
||||||
|
|
||||||
local v6 = '::1:12345'
|
local v6 = '::1:12345'
|
||||||
s = funcs.serverstart(v6)
|
status, _ = pcall(funcs.serverstart, v6)
|
||||||
if #s > 0 then
|
if status then
|
||||||
table.insert(expected, v6)
|
table.insert(expected, v6)
|
||||||
funcs.serverstart(v6) -- exists already; ignore
|
pcall(funcs.serverstart, v6) -- exists already; ignore
|
||||||
end
|
end
|
||||||
eq(expected, funcs.serverlist())
|
eq(expected, funcs.serverlist())
|
||||||
clear_serverlist()
|
clear_serverlist()
|
||||||
|
|
||||||
funcs.serverstart('127.0.0.1:65536') -- invalid port
|
expect_err('Failed to start server: invalid argument',
|
||||||
|
funcs.serverstart, '127.0.0.1:65536') -- invalid port
|
||||||
eq({}, funcs.serverlist())
|
eq({}, funcs.serverlist())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ describe('Test getting and setting file permissions', function()
|
|||||||
|
|
||||||
it('file permissions', function()
|
it('file permissions', function()
|
||||||
eq('', call('getfperm', tempfile))
|
eq('', call('getfperm', tempfile))
|
||||||
eq(0, call('setfperm', tempfile, 'r------'))
|
eq(0, call('setfperm', tempfile, 'r--------'))
|
||||||
|
|
||||||
call('writefile', {'one'}, tempfile)
|
call('writefile', {'one'}, tempfile)
|
||||||
eq(9, call('len', call('getfperm', tempfile)))
|
eq(9, call('len', call('getfperm', tempfile)))
|
||||||
|
@ -51,12 +51,12 @@ describe('luaeval()', function()
|
|||||||
end)
|
end)
|
||||||
describe('recursive lua values', function()
|
describe('recursive lua values', function()
|
||||||
it('are successfully transformed', function()
|
it('are successfully transformed', function()
|
||||||
funcs.luaeval('rawset(_G, "d", {})')
|
command('lua rawset(_G, "d", {})')
|
||||||
funcs.luaeval('rawset(d, "d", d)')
|
command('lua rawset(d, "d", d)')
|
||||||
eq('\n{\'d\': {...@0}}', funcs.execute('echo luaeval("d")'))
|
eq('\n{\'d\': {...@0}}', funcs.execute('echo luaeval("d")'))
|
||||||
|
|
||||||
funcs.luaeval('rawset(_G, "l", {})')
|
command('lua rawset(_G, "l", {})')
|
||||||
funcs.luaeval('table.insert(l, l)')
|
command('lua table.insert(l, l)')
|
||||||
eq('\n[[...@0]]', funcs.execute('echo luaeval("l")'))
|
eq('\n[[...@0]]', funcs.execute('echo luaeval("l")'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user