diff options
| author | Justin M. Keyes <justinkz@gmail.com> | 2018-05-10 19:18:58 +0200 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-05-10 19:18:58 +0200 | 
| commit | 8d40b3617c8bb10af5d4d4abcab0dfe77a4e807d (patch) | |
| tree | cded38835bde85bc8a1682303617f84bf2549f7e /src/nvim/api/vim.c | |
| parent | 1cd8517344c0d99ca6fb3246c70f78d271993cf6 (diff) | |
| parent | 966e7abc4960746b4dde618807fb5516d162ae2d (diff) | |
| download | rneovim-8d40b3617c8bb10af5d4d4abcab0dfe77a4e807d.tar.gz rneovim-8d40b3617c8bb10af5d4d4abcab0dfe77a4e807d.tar.bz2 rneovim-8d40b3617c8bb10af5d4d4abcab0dfe77a4e807d.zip  | |
Merge #8371 'API: more reliable/descriptive VimL errors'
Diffstat (limited to 'src/nvim/api/vim.c')
| -rw-r--r-- | src/nvim/api/vim.c | 104 | 
1 files changed, 72 insertions, 32 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 3679d1e569..fd9cf332d5 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -46,8 +46,7 @@  /// Executes an ex-command.  /// -/// On parse error: forwards the Vim error; does not update v:errmsg. -/// On runtime error: forwards the Vim error; does not update v:errmsg. +/// On execution error: fails with VimL error, does not update v:errmsg.  ///  /// @param command  Ex-command string  /// @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. -/// 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 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. -/// 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  /// 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.  /// Shell |:!| output is not captured.  /// -/// On parse error: forwards the Vim error; does not update v:errmsg. -/// On runtime error: forwards the Vim error; does not update v:errmsg. +/// On execution error: fails with VimL error, does not update v:errmsg.  ///  /// @param command  Ex-command string  /// @param[out] err Error details (Vim error), if any @@ -259,7 +259,8 @@ theend:  /// Evaluates a VimL expression (:help expression).  /// 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[out] err Error details, if any @@ -267,22 +268,42 @@ theend:  Object nvim_eval(String expr, Error *err)    FUNC_API_SINCE(1)  { +  static int recursive = 0;  // recursion depth    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();    typval_T rettv; -  if (eval0((char_u *)expr.data, &rettv, NULL, true) == FAIL) { -    api_set_error(err, kErrorTypeException, "Failed to evaluate expression"); -  } +  int ok = eval0((char_u *)expr.data, &rettv, NULL, true);    if (!try_end(err)) { -    // No errors, convert the result -    rv = vim_to_object(&rettv); +    if (ok == FAIL) { +      // 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); +  msg_list = saved_msg_list;  // Restore the exception context. +  recursive--;    return rv;  } @@ -314,7 +335,9 @@ Object nvim_execute_lua(String code, Array args, Error *err)  /// @return Result of the function call  static Object _call_function(String fn, Array args, dict_T *self, Error *err)  { +  static int recursive = 0;  // recursion depth    Object rv = OBJECT_INIT; +    if (args.size > MAX_FUNC_ARGS) {      api_set_error(err, kErrorTypeValidation,                    "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(); -  // Call the function    typval_T rettv;    int dummy; -  int r = call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size, -                    vim_args, NULL, curwin->w_cursor.lnum, -                    curwin->w_cursor.lnum, &dummy, true, NULL, self); -  if (r == FAIL) { -    api_set_error(err, kErrorTypeException, "Error calling function"); -  } +  // call_func() retval is deceptive, ignore it.  Instead we set `msg_list` +  // (see above) to capture abort-causing non-exception errors. +  (void)call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size, +                  vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, +                  &dummy, true, NULL, self);    if (!try_end(err)) {      rv = vim_to_object(&rettv);    }    tv_clear(&rettv); +  msg_list = saved_msg_list;  // Restore the exception context. +  recursive--;  free_vim_args:    while (i > 0) { @@ -355,7 +394,7 @@ free_vim_args:  /// 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 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.  /// +/// On execution error: fails with VimL error, does not update v:errmsg. +///  /// @param dict Dictionary, or String evaluating to a VimL |self| dict  /// @param fn Name of the function defined on the VimL dict  /// @param args Function arguments packed in an Array @@ -934,27 +975,26 @@ Array nvim_get_api_info(uint64_t channel_id)    return rv;  } -/// Call many api methods atomically +/// Calls many API methods atomically.  /// -/// This has two main usages: Firstly, to perform several requests from an -/// async context atomically, i.e. without processing requests from other rpc -/// clients or redrawing or allowing user interaction in between. Note that api -/// methods that could fire autocommands or do event processing still might do -/// so. For instance invoking the :sleep command might call timer callbacks. -/// Secondly, it can be used to reduce rpc overhead (roundtrips) when doing -/// many requests in sequence. +/// This has two main usages: +/// 1. To perform several requests from an async context atomically, i.e. +///    without interleaving redraws, RPC requests from other clients, or user +///    interactions (however API methods may trigger autocommands or event +///    processing which have such side-effects, e.g. |:sleep| may wake timers). +/// 2. To minimize RPC overhead (roundtrips) of a sequence of many requests.  ///  /// @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.  /// @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.  ///  /// @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  /// 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 -/// 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)    FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY  {  | 
