aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2024-05-24 19:18:11 +0000
committerJosh Rahm <joshuarahm@gmail.com>2024-05-24 19:18:11 +0000
commitff7ed8f586589d620a806c3758fac4a47a8e7e15 (patch)
tree729bbcb92231538fa61dab6c3d890b025484b7f5 /src/nvim/eval.c
parent376914f419eb08fdf4c1a63a77e1f035898a0f10 (diff)
parent28c04948a1c887a1cc0cb64de79fa32631700466 (diff)
downloadrneovim-ff7ed8f586589d620a806c3758fac4a47a8e7e15.tar.gz
rneovim-ff7ed8f586589d620a806c3758fac4a47a8e7e15.tar.bz2
rneovim-ff7ed8f586589d620a806c3758fac4a47a8e7e15.zip
Merge remote-tracking branch 'upstream/master' into mix_20240309
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c467
1 files changed, 275 insertions, 192 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 7e3060100c..64883e69a2 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -17,6 +17,7 @@
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
+#include "nvim/change.h"
#include "nvim/channel.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand_defs.h"
@@ -763,8 +764,8 @@ void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip)
return;
}
- if (getline_equal(eap->getline, eap->cookie, getsourceline)) {
- evalarg->eval_getline = eap->getline;
+ if (getline_equal(eap->ea_getline, eap->cookie, getsourceline)) {
+ evalarg->eval_getline = eap->ea_getline;
evalarg->eval_cookie = eap->cookie;
}
}
@@ -968,12 +969,12 @@ int skip_expr(char **pp, evalarg_T *const evalarg)
/// Convert "tv" to a string.
///
-/// @param convert when true convert a List into a sequence of lines.
+/// @param join_list when true convert a List into a sequence of lines.
///
/// @return an allocated string.
-static char *typval2string(typval_T *tv, bool convert)
+static char *typval2string(typval_T *tv, bool join_list)
{
- if (convert && tv->v_type == VAR_LIST) {
+ if (join_list && tv->v_type == VAR_LIST) {
garray_T ga;
ga_init(&ga, (int)sizeof(char), 80);
if (tv->vval.v_list != NULL) {
@@ -984,24 +985,28 @@ static char *typval2string(typval_T *tv, bool convert)
}
ga_append(&ga, NUL);
return (char *)ga.ga_data;
+ } else if (tv->v_type == VAR_LIST || tv->v_type == VAR_DICT) {
+ return encode_tv2string(tv, NULL);
}
return xstrdup(tv_get_string(tv));
}
/// Top level evaluation function, returning a string.
///
-/// @param convert when true convert a List into a sequence of lines.
+/// @param join_list when true convert a List into a sequence of lines.
///
/// @return pointer to allocated memory, or NULL for failure.
-char *eval_to_string(char *arg, bool convert)
+char *eval_to_string_eap(char *arg, bool join_list, exarg_T *eap)
{
typval_T tv;
char *retval;
- if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
+ evalarg_T evalarg;
+ fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
+ if (eval0(arg, &tv, NULL, &evalarg) == FAIL) {
retval = NULL;
} else {
- retval = typval2string(&tv, convert);
+ retval = typval2string(&tv, join_list);
tv_clear(&tv);
}
clear_evalarg(&EVALARG_EVALUATE, NULL);
@@ -1009,6 +1014,11 @@ char *eval_to_string(char *arg, bool convert)
return retval;
}
+char *eval_to_string(char *arg, bool join_list)
+{
+ return eval_to_string_eap(arg, join_list, NULL);
+}
+
/// Call eval_to_string() without using current local variables and using
/// textlock.
///
@@ -1372,112 +1382,24 @@ Object eval_foldtext(win_T *wp)
return retval;
}
-/// Get an lvalue
+/// Get the lval of a list/dict/blob subitem starting at "p". Loop
+/// until no more [idx] or .key is following.
///
-/// Lvalue may be
-/// - variable: "name", "na{me}"
-/// - dictionary item: "dict.key", "dict['key']"
-/// - list item: "list[expr]"
-/// - list slice: "list[expr:expr]"
-///
-/// Indexing only works if trying to use it with an existing List or Dictionary.
-///
-/// @param[in] name Name to parse.
-/// @param rettv Pointer to the value to be assigned or NULL.
-/// @param[out] lp Lvalue definition. When evaluation errors occur `->ll_name`
-/// is NULL.
-/// @param[in] unlet True if using `:unlet`. This results in slightly
-/// different behaviour when something is wrong; must end in
-/// space or cmd separator.
-/// @param[in] skip True when skipping.
/// @param[in] flags @see GetLvalFlags.
-/// @param[in] fne_flags Flags for find_name_end().
///
-/// @return A pointer to just after the name, including indexes. Returns NULL
-/// for a parsing error, but it is still needed to free items in lp.
-char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const bool unlet,
- const bool skip, const int flags, const int fne_flags)
- FUNC_ATTR_NONNULL_ARG(1, 3)
+/// @return A pointer to the character after the subscript on success or NULL on
+/// failure.
+static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv, hashtab_T *ht,
+ dictitem_T *v, int unlet, int flags)
{
- bool empty1 = false;
int quiet = flags & GLV_QUIET;
-
- // Clear everything in "lp".
- CLEAR_POINTER(lp);
-
- if (skip) {
- // When skipping just find the end of the name.
- lp->ll_name = name;
- return (char *)find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags);
- }
-
- // Find the end of the name.
- char *expr_start;
- char *expr_end;
- char *p = (char *)find_name_end(name, (const char **)&expr_start,
- (const char **)&expr_end,
- fne_flags);
- if (expr_start != NULL) {
- // Don't expand the name when we already know there is an error.
- if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p)
- && *p != '[' && *p != '.') {
- semsg(_(e_trailing_arg), p);
- return NULL;
- }
-
- lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p);
- lp->ll_name = lp->ll_exp_name;
- if (lp->ll_exp_name == NULL) {
- // Report an invalid expression in braces, unless the
- // expression evaluation has been cancelled due to an
- // aborting error, an interrupt, or an exception.
- if (!aborting() && !quiet) {
- emsg_severe = true;
- semsg(_(e_invarg2), name);
- return NULL;
- }
- lp->ll_name_len = 0;
- } else {
- lp->ll_name_len = strlen(lp->ll_name);
- }
- } else {
- lp->ll_name = name;
- lp->ll_name_len = (size_t)(p - lp->ll_name);
- }
-
- // Without [idx] or .key we are done.
- if ((*p != '[' && *p != '.') || lp->ll_name == NULL) {
- return p;
- }
-
- hashtab_T *ht = NULL;
-
- // Only pass &ht when we would write to the variable, it prevents autoload
- // as well.
- dictitem_T *v = find_var(lp->ll_name, lp->ll_name_len,
- (flags & GLV_READ_ONLY) ? NULL : &ht,
- flags & GLV_NO_AUTOLOAD);
- if (v == NULL && !quiet) {
- semsg(_("E121: Undefined variable: %.*s"),
- (int)lp->ll_name_len, lp->ll_name);
- }
- if (v == NULL) {
- return NULL;
- }
-
- lp->ll_tv = &v->di_tv;
-
- if (tv_is_luafunc(lp->ll_tv)) {
- // For v:lua just return a pointer to the "." after the "v:lua".
- // If the caller is trans_function_name() it will check for a Lua function name.
- return p;
- }
-
- // Loop until no more [idx] or .key is following.
typval_T var1;
var1.v_type = VAR_UNKNOWN;
typval_T var2;
var2.v_type = VAR_UNKNOWN;
+ bool empty1 = false;
+
+ // Loop until no more [idx] or .key is following.
while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) {
if (*p == '.' && lp->ll_tv->v_type != VAR_DICT) {
if (!quiet) {
@@ -1512,6 +1434,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
char *key = NULL;
if (*p == '.') {
key = p + 1;
+
for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {}
if (len == 0) {
if (!quiet) {
@@ -1730,6 +1653,116 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
return p;
}
+/// Get an lvalue
+///
+/// Lvalue may be
+/// - variable: "name", "na{me}"
+/// - dictionary item: "dict.key", "dict['key']"
+/// - list item: "list[expr]"
+/// - list slice: "list[expr:expr]"
+///
+/// Indexing only works if trying to use it with an existing List or Dictionary.
+///
+/// @param[in] name Name to parse.
+/// @param rettv Pointer to the value to be assigned or NULL.
+/// @param[out] lp Lvalue definition. When evaluation errors occur `->ll_name`
+/// is NULL.
+/// @param[in] unlet True if using `:unlet`. This results in slightly
+/// different behaviour when something is wrong; must end in
+/// space or cmd separator.
+/// @param[in] skip True when skipping.
+/// @param[in] flags @see GetLvalFlags.
+/// @param[in] fne_flags Flags for find_name_end().
+///
+/// @return A pointer to just after the name, including indexes. Returns NULL
+/// for a parsing error, but it is still needed to free items in lp.
+char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const bool unlet,
+ const bool skip, const int flags, const int fne_flags)
+ FUNC_ATTR_NONNULL_ARG(1, 3)
+{
+ int quiet = flags & GLV_QUIET;
+
+ // Clear everything in "lp".
+ CLEAR_POINTER(lp);
+
+ if (skip) {
+ // When skipping just find the end of the name.
+ lp->ll_name = name;
+ return (char *)find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags);
+ }
+
+ // Find the end of the name.
+ char *expr_start;
+ char *expr_end;
+ char *p = (char *)find_name_end(name, (const char **)&expr_start,
+ (const char **)&expr_end,
+ fne_flags);
+ if (expr_start != NULL) {
+ // Don't expand the name when we already know there is an error.
+ if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p)
+ && *p != '[' && *p != '.') {
+ semsg(_(e_trailing_arg), p);
+ return NULL;
+ }
+
+ lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p);
+ lp->ll_name = lp->ll_exp_name;
+ if (lp->ll_exp_name == NULL) {
+ // Report an invalid expression in braces, unless the
+ // expression evaluation has been cancelled due to an
+ // aborting error, an interrupt, or an exception.
+ if (!aborting() && !quiet) {
+ emsg_severe = true;
+ semsg(_(e_invarg2), name);
+ return NULL;
+ }
+ lp->ll_name_len = 0;
+ } else {
+ lp->ll_name_len = strlen(lp->ll_name);
+ }
+ } else {
+ lp->ll_name = name;
+ lp->ll_name_len = (size_t)(p - lp->ll_name);
+ }
+
+ // Without [idx] or .key we are done.
+ if ((*p != '[' && *p != '.') || lp->ll_name == NULL) {
+ return p;
+ }
+
+ hashtab_T *ht = NULL;
+
+ // Only pass &ht when we would write to the variable, it prevents autoload
+ // as well.
+ dictitem_T *v = find_var(lp->ll_name, lp->ll_name_len,
+ (flags & GLV_READ_ONLY) ? NULL : &ht,
+ flags & GLV_NO_AUTOLOAD);
+ if (v == NULL && !quiet) {
+ semsg(_("E121: Undefined variable: %.*s"),
+ (int)lp->ll_name_len, lp->ll_name);
+ }
+ if (v == NULL) {
+ return NULL;
+ }
+
+ lp->ll_tv = &v->di_tv;
+
+ if (tv_is_luafunc(lp->ll_tv)) {
+ // For v:lua just return a pointer to the "." after the "v:lua".
+ // If the caller is trans_function_name() it will check for a Lua function name.
+ return p;
+ }
+
+ // If the next character is a "." or a "[", then process the subitem.
+ p = get_lval_subscript(lp, p, name, rettv, ht, v, unlet, flags);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ lp->ll_name_len = (size_t)(p - lp->ll_name);
+ return p;
+}
+
/// Clear lval "lp" that was filled by get_lval().
void clear_lval(lval_T *lp)
{
@@ -1892,7 +1925,7 @@ void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, evalarg_T *const
*errp = true; // Default: there is an error.
- const char *expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon);
+ const char *expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon, false);
if (expr == NULL) {
return fi;
}
@@ -4766,6 +4799,88 @@ bool set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack)
return abort;
}
+/// Mark the dict "dd" with "copyID".
+/// Also see set_ref_in_item().
+static bool set_ref_in_item_dict(dict_T *dd, int copyID, ht_stack_T **ht_stack,
+ list_stack_T **list_stack)
+{
+ if (dd == NULL || dd->dv_copyID == copyID) {
+ return false;
+ }
+
+ // Didn't see this dict yet.
+ dd->dv_copyID = copyID;
+ if (ht_stack == NULL) {
+ return set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack);
+ }
+
+ ht_stack_T *const newitem = xmalloc(sizeof(ht_stack_T));
+ newitem->ht = &dd->dv_hashtab;
+ newitem->prev = *ht_stack;
+ *ht_stack = newitem;
+
+ QUEUE *w = NULL;
+ DictWatcher *watcher = NULL;
+ QUEUE_FOREACH(w, &dd->watchers, {
+ watcher = tv_dict_watcher_node_data(w);
+ set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack);
+ })
+
+ return false;
+}
+
+/// Mark the list "ll" with "copyID".
+/// Also see set_ref_in_item().
+static bool set_ref_in_item_list(list_T *ll, int copyID, ht_stack_T **ht_stack,
+ list_stack_T **list_stack)
+{
+ if (ll == NULL || ll->lv_copyID == copyID) {
+ return false;
+ }
+
+ // Didn't see this list yet.
+ ll->lv_copyID = copyID;
+ if (list_stack == NULL) {
+ return set_ref_in_list_items(ll, copyID, ht_stack);
+ }
+
+ list_stack_T *const newitem = xmalloc(sizeof(list_stack_T));
+ newitem->list = ll;
+ newitem->prev = *list_stack;
+ *list_stack = newitem;
+
+ return false;
+}
+
+/// Mark the partial "pt" with "copyID".
+/// Also see set_ref_in_item().
+static bool set_ref_in_item_partial(partial_T *pt, int copyID, ht_stack_T **ht_stack,
+ list_stack_T **list_stack)
+{
+ if (pt == NULL || pt->pt_copyID == copyID) {
+ return false;
+ }
+
+ // Didn't see this partial yet.
+ pt->pt_copyID = copyID;
+
+ bool abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID);
+
+ if (pt->pt_dict != NULL) {
+ typval_T dtv;
+
+ dtv.v_type = VAR_DICT;
+ dtv.vval.v_dict = pt->pt_dict;
+ abort = abort || set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+ }
+
+ for (int i = 0; i < pt->pt_argc; i++) {
+ abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID, ht_stack, list_stack);
+ }
+
+ return abort;
+}
+
/// Mark all lists and dicts referenced through typval "tv" with "copyID".
///
/// @param tv Typval content will be marked.
@@ -4780,71 +4895,15 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack
bool abort = false;
switch (tv->v_type) {
- case VAR_DICT: {
- dict_T *dd = tv->vval.v_dict;
- if (dd != NULL && dd->dv_copyID != copyID) {
- // Didn't see this dict yet.
- dd->dv_copyID = copyID;
- if (ht_stack == NULL) {
- abort = set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack);
- } else {
- ht_stack_T *const newitem = xmalloc(sizeof(ht_stack_T));
- newitem->ht = &dd->dv_hashtab;
- newitem->prev = *ht_stack;
- *ht_stack = newitem;
- }
-
- QUEUE *w = NULL;
- DictWatcher *watcher = NULL;
- QUEUE_FOREACH(w, &dd->watchers, {
- watcher = tv_dict_watcher_node_data(w);
- set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack);
- })
- }
- break;
- }
-
- case VAR_LIST: {
- list_T *ll = tv->vval.v_list;
- if (ll != NULL && ll->lv_copyID != copyID) {
- // Didn't see this list yet.
- ll->lv_copyID = copyID;
- if (list_stack == NULL) {
- abort = set_ref_in_list_items(ll, copyID, ht_stack);
- } else {
- list_stack_T *const newitem = xmalloc(sizeof(list_stack_T));
- newitem->list = ll;
- newitem->prev = *list_stack;
- *list_stack = newitem;
- }
- }
- break;
- }
-
- case VAR_PARTIAL: {
- partial_T *pt = tv->vval.v_partial;
-
- // A partial does not have a copyID, because it cannot contain itself.
- if (pt != NULL) {
- abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID);
- if (pt->pt_dict != NULL) {
- typval_T dtv;
-
- dtv.v_type = VAR_DICT;
- dtv.vval.v_dict = pt->pt_dict;
- abort = abort || set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
- }
-
- for (int i = 0; i < pt->pt_argc; i++) {
- abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID,
- ht_stack, list_stack);
- }
- }
- break;
- }
+ case VAR_DICT:
+ return set_ref_in_item_dict(tv->vval.v_dict, copyID, ht_stack, list_stack);
+ case VAR_LIST:
+ return set_ref_in_item_list(tv->vval.v_list, copyID, ht_stack, list_stack);
case VAR_FUNC:
abort = set_ref_in_func(tv->vval.v_string, NULL, copyID);
break;
+ case VAR_PARTIAL:
+ return set_ref_in_item_partial(tv->vval.v_partial, copyID, ht_stack, list_stack);
case VAR_UNKNOWN:
case VAR_BOOL:
case VAR_SPECIAL:
@@ -6697,7 +6756,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
if (charcol) {
len = mb_charlen(ml_get(pos.lnum));
} else {
- len = (int)strlen(ml_get(pos.lnum));
+ len = ml_get_len(pos.lnum);
}
// We accept "$" for the column number: last column.
@@ -6787,7 +6846,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
if (charcol) {
pos.col = (colnr_T)mb_charlen(get_cursor_line_ptr());
} else {
- pos.col = (colnr_T)strlen(get_cursor_line_ptr());
+ pos.col = get_cursor_line_len();
}
}
return &pos;
@@ -7517,29 +7576,44 @@ int check_luafunc_name(const char *const str, const bool paren)
return (int)(p - str);
}
-/// Return the character "str[index]" where "index" is the character index. If
-/// "index" is out of range NULL is returned.
+/// Return the character "str[index]" where "index" is the character index,
+/// including composing characters.
+/// If "index" is out of range NULL is returned.
char *char_from_string(const char *str, varnumber_T index)
{
- size_t nbyte = 0;
varnumber_T nchar = index;
- if (str == NULL || index < 0) {
+ if (str == NULL) {
return NULL;
}
size_t slen = strlen(str);
- while (nchar > 0 && nbyte < slen) {
- nbyte += (size_t)utf_ptr2len(str + nbyte);
- nchar--;
+
+ // do the same as for a list: a negative index counts from the end
+ if (index < 0) {
+ int clen = 0;
+
+ for (size_t nbyte = 0; nbyte < slen; clen++) {
+ nbyte += (size_t)utfc_ptr2len(str + nbyte);
+ }
+ nchar = clen + index;
+ if (nchar < 0) {
+ // unlike list: index out of range results in empty string
+ return NULL;
+ }
+ }
+
+ size_t nbyte = 0;
+ for (; nchar > 0 && nbyte < slen; nchar--) {
+ nbyte += (size_t)utfc_ptr2len(str + nbyte);
}
if (nbyte >= slen) {
return NULL;
}
- return xmemdupz(str + nbyte, (size_t)utf_ptr2len(str + nbyte));
+ return xmemdupz(str + nbyte, (size_t)utfc_ptr2len(str + nbyte));
}
/// Get the byte index for character index "idx" in string "str" with length
-/// "str_len".
+/// "str_len". Composing characters are included.
/// If going over the end return "str_len".
/// If "idx" is negative count from the end, -1 is the last character.
/// When going over the start return -1.
@@ -7550,7 +7624,7 @@ static ssize_t char_idx2byte(const char *str, size_t str_len, varnumber_T idx)
if (nchar >= 0) {
while (nchar > 0 && nbyte < str_len) {
- nbyte += (size_t)utf_ptr2len(str + nbyte);
+ nbyte += (size_t)utfc_ptr2len(str + nbyte);
nchar--;
}
} else {
@@ -7567,7 +7641,8 @@ static ssize_t char_idx2byte(const char *str, size_t str_len, varnumber_T idx)
return (ssize_t)nbyte;
}
-/// Return the slice "str[first:last]" using character indexes.
+/// Return the slice "str[first : last]" using character indexes. Composing
+/// characters are included.
///
/// @param exclusive true for slice().
///
@@ -7589,7 +7664,7 @@ char *string_slice(const char *str, varnumber_T first, varnumber_T last, bool ex
end_byte = char_idx2byte(str, slen, last);
if (!exclusive && end_byte >= 0 && end_byte < (ssize_t)slen) {
// end index is inclusive
- end_byte += utf_ptr2len(str + end_byte);
+ end_byte += utfc_ptr2len(str + end_byte);
}
}
@@ -7876,8 +7951,8 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char
.channel_id = LUA_INTERNAL_CALL,
};
bool should_free;
- // should_free is ignored as script_sctx will be resolved to a fnmae
- // & new_script_item will consume it.
+ // should_free is ignored as script_ctx will be resolved to a fname
+ // and new_script_item() will consume it.
char *sc_name = get_scriptname(last_set, &should_free);
new_script_item(sc_name, &current_sctx.sc_sid);
}
@@ -8083,10 +8158,12 @@ void ex_echo(exarg_T *eap)
// Call msg_start() after eval1(), evaluating the expression
// may cause a message to appear.
if (eap->cmdidx == CMD_echo) {
- // Mark the saved text as finishing the line, so that what
- // follows is displayed on a new line when scrolling back
- // at the more prompt.
- msg_sb_eol();
+ if (!msg_didout) {
+ // Mark the saved text as finishing the line, so that what
+ // follows is displayed on a new line when scrolling back
+ // at the more prompt.
+ msg_sb_eol();
+ }
msg_start();
}
} else if (eap->cmdidx == CMD_echo) {
@@ -8182,7 +8259,7 @@ void ex_execute(exarg_T *eap)
did_emsg = save_did_emsg;
}
} else if (eap->cmdidx == CMD_execute) {
- do_cmdline(ga.ga_data, eap->getline, eap->cookie, DOCMD_NOWAIT|DOCMD_VERBOSE);
+ do_cmdline(ga.ga_data, eap->ea_getline, eap->cookie, DOCMD_NOWAIT|DOCMD_VERBOSE);
}
}
@@ -8752,7 +8829,7 @@ void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)
/// an empty typval_T.
typval_T eval_call_provider(char *provider, char *method, list_T *arguments, bool discard)
{
- if (!eval_has_provider(provider)) {
+ if (!eval_has_provider(provider, false)) {
semsg("E319: No \"%s\" provider found. Run \":checkhealth provider\"",
provider);
return (typval_T){
@@ -8810,7 +8887,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo
}
/// Checks if provider for feature `feat` is enabled.
-bool eval_has_provider(const char *feat)
+bool eval_has_provider(const char *feat, bool throw_if_fast)
{
if (!strequal(feat, "clipboard")
&& !strequal(feat, "python3")
@@ -8823,6 +8900,11 @@ bool eval_has_provider(const char *feat)
return false;
}
+ if (throw_if_fast && !nlua_is_deferred_safe()) {
+ semsg(e_luv_api_disabled, "Vimscript function");
+ return false;
+ }
+
char name[32]; // Normalized: "python3_compiled" => "python3".
snprintf(name, sizeof(name), "%s", feat);
strchrsub(name, '_', '\0'); // Chop any "_xx" suffix.
@@ -8885,6 +8967,7 @@ void invoke_prompt_callback(void)
// Add a new line for the prompt before invoking the callback, so that
// text can always be inserted above the last line.
ml_append(lnum, "", 0, false);
+ appended_lines_mark(lnum, 1);
curwin->w_cursor.lnum = lnum + 1;
curwin->w_cursor.col = 0;