vim-patch:9.0.0634: evaluating "expr" options has more overhead than needed

Problem:    Evaluating "expr" options has more overhead than needed.
Solution:   Use call_simple_func() for 'foldtext', 'includeexpr', 'printexpr',
            "expr" of 'spellsuggest', 'diffexpr', 'patchexpr', 'balloonexpr',
            'formatexpr', 'indentexpr' and 'charconvert'.

a4e0b9785e

vim-patch:9.0.0635: build error and compiler warnings

Problem:    Build error and compiler warnings.
Solution:   Add missing change.  Add type casts.

3292a22940

Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
zeertzjq 2024-08-01 10:41:08 +08:00
parent f7fde0173a
commit 582bf4f1e1
17 changed files with 164 additions and 67 deletions

View File

@ -369,6 +369,9 @@ Additionally, 'diffexpr' should take care of "icase" and "iwhite" in the
'diffopt' option. 'diffexpr' cannot change the value of 'lines' and
'columns'.
The advantage of using a function call without arguments is that it is faster,
see |expr-option-function|.
Example (this does almost the same as 'diffexpr' being empty): >
set diffexpr=MyDiff()
@ -434,6 +437,9 @@ will have the same effect. These variables are set to the file names used:
v:fname_diff patch file
v:fname_out resulting patched file
The advantage of using a function call without arguments is that it is faster,
see |expr-option-function|.
Example (this does the same as 'patchexpr' being empty): >
set patchexpr=MyPatch()

View File

@ -1315,6 +1315,9 @@ A jump table for the options with a short description can be found at |Q_op|.
v:fname_out name of the output file
Note that v:fname_in and v:fname_out will never be the same.
The advantage of using a function call without arguments is that it is
faster, see |expr-option-function|.
If the 'charconvert' expression starts with s: or |<SID>|, then it is
replaced with the script ID (|local-function|). Example: >vim
set charconvert=s:MyConvert()
@ -2781,6 +2784,9 @@ A jump table for the options with a short description can be found at |Q_op|.
< This will invoke the mylang#Format() function in the
autoload/mylang.vim file in 'runtimepath'. |autoload|
The advantage of using a function call without arguments is that it is
faster, see |expr-option-function|.
The expression is also evaluated when 'textwidth' is set and adding
text beyond that limit. This happens under the same conditions as
when internal formatting is used. Make sure the cursor is kept in the
@ -3416,11 +3422,14 @@ A jump table for the options with a short description can be found at |Q_op|.
If the expression starts with s: or |<SID>|, then it is replaced with
the script ID (|local-function|). Example: >vim
setlocal includeexpr=s:MyIncludeExpr(v:fname)
setlocal includeexpr=<SID>SomeIncludeExpr(v:fname)
setlocal includeexpr=s:MyIncludeExpr()
setlocal includeexpr=<SID>SomeIncludeExpr()
< Otherwise, the expression is evaluated in the context of the script
where the option was set, thus script-local items are available.
It is more efficient if the value is just a function call without
arguments, see |expr-option-function|.
The expression will be evaluated in the |sandbox| when set from a
modeline, see |sandbox-option|.
This option cannot be set in a modeline when 'modelineexpr' is off.
@ -3483,6 +3492,9 @@ A jump table for the options with a short description can be found at |Q_op|.
< Otherwise, the expression is evaluated in the context of the script
where the option was set, thus script-local items are available.
The advantage of using a function call without arguments is that it is
faster, see |expr-option-function|.
The expression must return the number of spaces worth of indent. It
can return "-1" to keep the current indent (this means 'autoindent' is
used for the indent).
@ -5923,9 +5935,11 @@ A jump table for the options with a short description can be found at |Q_op|.
The file is used for all languages.
expr:{expr} Evaluate expression {expr}. Use a function to avoid
trouble with spaces. |v:val| holds the badly spelled
word. The expression must evaluate to a List of
Lists, each with a suggestion and a score.
trouble with spaces. Best is to call a function
without arguments, see |expr-option-function|.
|v:val| holds the badly spelled word. The expression
must evaluate to a List of Lists, each with a
suggestion and a score.
Example:
[['the', 33], ['that', 44]] ~
Set 'verbose' and use |z=| to see the scores that the

View File

@ -786,6 +786,9 @@ vim.bo.channel = vim.o.channel
--- v:fname_out name of the output file
--- Note that v:fname_in and v:fname_out will never be the same.
---
--- The advantage of using a function call without arguments is that it is
--- faster, see `expr-option-function`.
---
--- If the 'charconvert' expression starts with s: or `<SID>`, then it is
--- replaced with the script ID (`local-function`). Example:
---
@ -2521,6 +2524,9 @@ vim.wo.fdt = vim.wo.foldtext
--- This will invoke the mylang#Format() function in the
--- autoload/mylang.vim file in 'runtimepath'. `autoload`
---
--- The advantage of using a function call without arguments is that it is
--- faster, see `expr-option-function`.
---
--- The expression is also evaluated when 'textwidth' is set and adding
--- text beyond that limit. This happens under the same conditions as
--- when internal formatting is used. Make sure the cursor is kept in the
@ -3286,12 +3292,15 @@ vim.go.inc = vim.go.include
--- the script ID (`local-function`). Example:
---
--- ```vim
--- setlocal includeexpr=s:MyIncludeExpr(v:fname)
--- setlocal includeexpr=<SID>SomeIncludeExpr(v:fname)
--- setlocal includeexpr=s:MyIncludeExpr()
--- setlocal includeexpr=<SID>SomeIncludeExpr()
--- ```
--- Otherwise, the expression is evaluated in the context of the script
--- where the option was set, thus script-local items are available.
---
--- It is more efficient if the value is just a function call without
--- arguments, see `expr-option-function`.
---
--- The expression will be evaluated in the `sandbox` when set from a
--- modeline, see `sandbox-option`.
--- This option cannot be set in a modeline when 'modelineexpr' is off.
@ -3366,6 +3375,9 @@ vim.go.is = vim.go.incsearch
--- Otherwise, the expression is evaluated in the context of the script
--- where the option was set, thus script-local items are available.
---
--- The advantage of using a function call without arguments is that it is
--- faster, see `expr-option-function`.
---
--- The expression must return the number of spaces worth of indent. It
--- can return "-1" to keep the current indent (this means 'autoindent' is
--- used for the indent).
@ -6314,9 +6326,11 @@ vim.bo.spo = vim.bo.spelloptions
--- The file is used for all languages.
---
--- expr:{expr} Evaluate expression {expr}. Use a function to avoid
--- trouble with spaces. `v:val` holds the badly spelled
--- word. The expression must evaluate to a List of
--- Lists, each with a suggestion and a score.
--- trouble with spaces. Best is to call a function
--- without arguments, see `expr-option-function|.
--- |v:val` holds the badly spelled word. The expression
--- must evaluate to a List of Lists, each with a
--- suggestion and a score.
--- Example:
--- [['the', 33], ['that', 44]] ~
--- Set 'verbose' and use `z=` to see the scores that the

View File

@ -2198,7 +2198,7 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
curwin = wp;
STRCPY(buf, "b:keymap_name"); // must be writable
emsg_skip++;
char *s = p = eval_to_string(buf, false);
char *s = p = eval_to_string(buf, false, false);
emsg_skip--;
curbuf = old_curbuf;
curwin = old_curwin;

View File

@ -703,7 +703,7 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to,
}
bool err = false;
if (eval_to_bool(p_ccv, &err, NULL, false)) {
if (eval_to_bool(p_ccv, &err, NULL, false, true)) {
err = true;
}
@ -732,7 +732,7 @@ void eval_diff(const char *const origfile, const char *const newfile, const char
}
// errors are ignored
typval_T *tv = eval_expr(p_dex, NULL);
typval_T *tv = eval_expr_ext(p_dex, NULL, true);
tv_free(tv);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
@ -754,7 +754,7 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch
}
// errors are ignored
typval_T *tv = eval_expr(p_pex, NULL);
typval_T *tv = eval_expr_ext(p_pex, NULL, true);
tv_free(tv);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
@ -783,7 +783,8 @@ void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip)
/// @param skip only parse, don't execute
///
/// @return true or false.
bool eval_to_bool(char *arg, bool *error, exarg_T *eap, bool skip)
bool eval_to_bool(char *arg, bool *error, exarg_T *eap, const bool skip,
const bool use_simple_function)
{
typval_T tv;
bool retval = false;
@ -794,7 +795,9 @@ bool eval_to_bool(char *arg, bool *error, exarg_T *eap, bool skip)
if (skip) {
emsg_skip++;
}
if (eval0(arg, &tv, eap, &evalarg) == FAIL) {
int r = use_simple_function ? eval0_simple_funccal(arg, &tv, eap, &evalarg)
: eval0(arg, &tv, eap, &evalarg);
if (r == FAIL) {
*error = true;
} else {
*error = false;
@ -1042,14 +1045,17 @@ static char *typval2string(typval_T *tv, bool join_list)
/// @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_eap(char *arg, bool join_list, exarg_T *eap)
char *eval_to_string_eap(char *arg, const bool join_list, exarg_T *eap,
const bool use_simple_function)
{
typval_T tv;
char *retval;
evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
if (eval0(arg, &tv, NULL, &evalarg) == FAIL) {
int r = use_simple_function ? eval0_simple_funccal(arg, &tv, NULL, &evalarg)
: eval0(arg, &tv, NULL, &evalarg);
if (r == FAIL) {
retval = NULL;
} else {
retval = typval2string(&tv, join_list);
@ -1060,16 +1066,16 @@ char *eval_to_string_eap(char *arg, bool join_list, exarg_T *eap)
return retval;
}
char *eval_to_string(char *arg, bool join_list)
char *eval_to_string(char *arg, const bool join_list, const bool use_simple_function)
{
return eval_to_string_eap(arg, join_list, NULL);
return eval_to_string_eap(arg, join_list, NULL, use_simple_function);
}
/// Call eval_to_string() without using current local variables and using
/// textlock.
///
/// @param use_sandbox when true, use the sandbox.
char *eval_to_string_safe(char *arg, const bool use_sandbox)
char *eval_to_string_safe(char *arg, const bool use_sandbox, const bool use_simple_function)
{
char *retval;
funccal_entry_T funccal_entry;
@ -1079,7 +1085,7 @@ char *eval_to_string_safe(char *arg, const bool use_sandbox)
sandbox++;
}
textlock++;
retval = eval_to_string(arg, false);
retval = eval_to_string(arg, false, use_simple_function);
if (use_sandbox) {
sandbox--;
}
@ -1092,15 +1098,22 @@ char *eval_to_string_safe(char *arg, const bool use_sandbox)
/// Evaluates "expr" silently.
///
/// @return -1 for an error.
varnumber_T eval_to_number(char *expr)
varnumber_T eval_to_number(char *expr, const bool use_simple_function)
{
typval_T rettv;
varnumber_T retval;
char *p = skipwhite(expr);
int r = NOTDONE;
emsg_off++;
if (eval1(&p, &rettv, &EVALARG_EVALUATE) == FAIL) {
if (use_simple_function) {
r = may_call_simple_func(expr, &rettv);
}
if (r == NOTDONE) {
r = eval1(&p, &rettv, &EVALARG_EVALUATE);
}
if (r == FAIL) {
retval = -1;
} else {
retval = tv_get_number_chk(&rettv, NULL);
@ -1116,13 +1129,27 @@ varnumber_T eval_to_number(char *expr)
/// @return an allocated typval_T with the result or
/// NULL when there is an error.
typval_T *eval_expr(char *arg, exarg_T *eap)
{
return eval_expr_ext(arg, eap, false);
}
static typval_T *eval_expr_ext(char *arg, exarg_T *eap, const bool use_simple_function)
{
typval_T *tv = xmalloc(sizeof(*tv));
evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
if (eval0(arg, tv, eap, &evalarg) == FAIL) {
int r = NOTDONE;
if (use_simple_function) {
r = eval0_simple_funccal(arg, tv, eap, &evalarg);
}
if (r == NOTDONE) {
r = eval0(arg, tv, eap, &evalarg);
}
if (r == FAIL) {
XFREE_CLEAR(tv);
}
@ -1208,7 +1235,11 @@ list_T *eval_spell_expr(char *badword, char *expr)
current_sctx = *ctx;
}
if (eval1(&p, &rettv, &EVALARG_EVALUATE) == OK) {
int r = may_call_simple_func(p, &rettv);
if (r == NOTDONE) {
r = eval1(&p, &rettv, &EVALARG_EVALUATE);
}
if (r == OK) {
if (rettv.v_type != VAR_LIST) {
tv_clear(&rettv);
} else {
@ -1360,23 +1391,10 @@ int eval_foldexpr(win_T *wp, int *cp)
*cp = NUL;
typval_T tv;
int r = NOTDONE;
// If the expression is "FuncName()" then we can skip a lot of overhead.
char *parens = strstr(arg, "()");
if (parens != NULL && *skipwhite(parens + 2) == NUL) {
char *p = strncmp(arg, "<SNR>", 5) == 0 ? skipdigits(arg + 5) : arg;
if (to_name_end(p, true) == parens) {
r = call_simple_func(arg, (int)(parens - arg), &tv);
}
}
if (r == NOTDONE) {
r = eval0(arg, &tv, NULL, &EVALARG_EVALUATE);
}
varnumber_T retval;
if (r == FAIL) {
// Evaluate the expression. If the expression is "FuncName()" call the
// function directly.
if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = 0;
} else {
// If the result is a number, just return the number.
@ -1422,7 +1440,7 @@ Object eval_foldtext(win_T *wp)
typval_T tv;
Object retval;
if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = STRING_OBJ(NULL_STRING);
} else {
if (tv.v_type == VAR_LIST) {
@ -2539,9 +2557,10 @@ void clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
}
}
/// The "evaluate" argument: When false, the argument is only parsed but not
/// executed. The function may return OK, but the rettv will be of type
/// VAR_UNKNOWN. The function still returns FAIL for a syntax error.
/// The "eval" functions have an "evalarg" argument: When NULL or
/// "evalarg->eval_flags" does not have EVAL_EVALUATE, then the argument is only
/// parsed but not executed. The functions may return OK, but the rettv will be
/// of type VAR_UNKNOWN. The functions still returns FAIL for a syntax error.
/// Handle zero level expression.
/// This calls eval1() and handles error message and nextcmd.
@ -2600,6 +2619,35 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
return ret;
}
/// If "arg" is a simple function call without arguments then call it and return
/// the result. Otherwise return NOTDONE.
static int may_call_simple_func(char *arg, typval_T *rettv)
{
char *parens = strstr(arg, "()");
int r = NOTDONE;
// If the expression is "FuncName()" then we can skip a lot of overhead.
if (parens != NULL && *skipwhite(parens + 2) == NUL) {
char *p = strncmp(arg, "<SNR>", 5) == 0 ? skipdigits(arg + 5) : arg;
if (to_name_end(p, true) == parens) {
r = call_simple_func(arg, (int)(parens - arg), rettv);
}
}
return r;
}
/// Handle zero level expression with optimization for a simple function call.
/// Same arguments and return value as eval0().
static int eval0_simple_funccal(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
{
int r = may_call_simple_func(arg, rettv);
if (r == NOTDONE) {
r = eval0(arg, rettv, eap, evalarg);
}
return r;
}
/// Handle top level expression:
/// expr2 ? expr1 : expr1
/// expr2 ?? expr1
@ -7412,7 +7460,7 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex
char c1 = *in_end;
*in_end = NUL;
char *temp_result = eval_to_string(expr_start + 1, false);
char *temp_result = eval_to_string(expr_start + 1, false, false);
if (temp_result != NULL) {
retval = xmalloc(strlen(temp_result) + (size_t)(expr_start - in_start)
+ (size_t)(in_end - expr_end) + 1);

View File

@ -89,7 +89,7 @@ char *eval_one_expr_in_str(char *p, garray_T *gap, bool evaluate)
}
if (evaluate) {
*block_end = NUL;
char *expr_val = eval_to_string(block_start, false);
char *expr_val = eval_to_string(block_start, false, false);
*block_end = '}';
if (expr_val == NULL) {
return NULL;

View File

@ -4644,7 +4644,7 @@ static void ex_colorscheme(exarg_T *eap)
char *expr = xstrdup("g:colors_name");
emsg_off++;
char *p = eval_to_string(expr, false);
char *p = eval_to_string(expr, false, false);
emsg_off--;
xfree(expr);

View File

@ -849,7 +849,7 @@ void ex_if(exarg_T *eap)
bool skip = CHECK_SKIP;
bool error;
bool result = eval_to_bool(eap->arg, &error, eap, skip);
bool result = eval_to_bool(eap->arg, &error, eap, skip, false);
if (!skip && !error) {
if (result) {
@ -944,7 +944,7 @@ void ex_else(exarg_T *eap)
if (skip && *eap->arg != '"' && ends_excmd(*eap->arg)) {
semsg(_(e_invexpr2), eap->arg);
} else {
result = eval_to_bool(eap->arg, &error, eap, skip);
result = eval_to_bool(eap->arg, &error, eap, skip, false);
}
// When throwing error exceptions, we want to throw always the first
@ -990,7 +990,7 @@ void ex_while(exarg_T *eap)
int skip = CHECK_SKIP;
if (eap->cmdidx == CMD_while) { // ":while bool-expr"
result = eval_to_bool(eap->arg, &error, eap, skip);
result = eval_to_bool(eap->arg, &error, eap, skip, false);
} else { // ":for var in list-expr"
evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, skip);

View File

@ -1161,7 +1161,7 @@ int get_expr_indent(void)
// Need to make a copy, the 'indentexpr' option could be changed while
// evaluating it.
char *inde_copy = xstrdup(curbuf->b_p_inde);
int indent = (int)eval_to_number(inde_copy);
int indent = (int)eval_to_number(inde_copy, true);
xfree(inde_copy);
if (use_sandbox) {

View File

@ -1656,7 +1656,7 @@ char *eval_map_expr(mapblock_T *mp, int c)
api_clear_error(&err);
}
} else {
p = eval_to_string(expr, false);
p = eval_to_string(expr, false, false);
xfree(expr);
}
expr_map_lock--;

View File

@ -734,7 +734,7 @@ char *get_expr_line(void)
}
nested++;
char *rv = eval_to_string(expr_copy, true);
char *rv = eval_to_string(expr_copy, true, false);
nested--;
xfree(expr_copy);
return rv;

View File

@ -1059,6 +1059,9 @@ return {
v:fname_out name of the output file
Note that v:fname_in and v:fname_out will never be the same.
The advantage of using a function call without arguments is that it is
faster, see |expr-option-function|.
If the 'charconvert' expression starts with s: or |<SID>|, then it is
replaced with the script ID (|local-function|). Example: >vim
set charconvert=s:MyConvert()
@ -3231,6 +3234,9 @@ return {
< This will invoke the mylang#Format() function in the
autoload/mylang.vim file in 'runtimepath'. |autoload|
The advantage of using a function call without arguments is that it is
faster, see |expr-option-function|.
The expression is also evaluated when 'textwidth' is set and adding
text beyond that limit. This happens under the same conditions as
when internal formatting is used. Make sure the cursor is kept in the
@ -4165,11 +4171,14 @@ return {
If the expression starts with s: or |<SID>|, then it is replaced with
the script ID (|local-function|). Example: >vim
setlocal includeexpr=s:MyIncludeExpr(v:fname)
setlocal includeexpr=<SID>SomeIncludeExpr(v:fname)
setlocal includeexpr=s:MyIncludeExpr()
setlocal includeexpr=<SID>SomeIncludeExpr()
< Otherwise, the expression is evaluated in the context of the script
where the option was set, thus script-local items are available.
It is more efficient if the value is just a function call without
arguments, see |expr-option-function|.
The expression will be evaluated in the |sandbox| when set from a
modeline, see |sandbox-option|.
This option cannot be set in a modeline when 'modelineexpr' is off.
@ -4249,6 +4258,9 @@ return {
< Otherwise, the expression is evaluated in the context of the script
where the option was set, thus script-local items are available.
The advantage of using a function call without arguments is that it is
faster, see |expr-option-function|.
The expression must return the number of spaces worth of indent. It
can return "-1" to keep the current indent (this means 'autoindent' is
used for the indent).
@ -7949,9 +7961,11 @@ return {
The file is used for all languages.
expr:{expr} Evaluate expression {expr}. Use a function to avoid
trouble with spaces. |v:val| holds the badly spelled
word. The expression must evaluate to a List of
Lists, each with a suggestion and a score.
trouble with spaces. Best is to call a function
without arguments, see |expr-option-function|.
|v:val| holds the badly spelled word. The expression
must evaluate to a List of Lists, each with a
suggestion and a score.
Example:
[['the', 33], ['that', 44]] ~
Set 'verbose' and use |z=| to see the scores that the

View File

@ -1398,7 +1398,7 @@ static int expand_backtick(garray_T *gap, char *pat, int flags)
char *cmd = xmemdupz(pat + 1, strlen(pat) - 2);
if (*cmd == '=') { // `={expr}`: Expand expression
buffer = eval_to_string(cmd + 1, true);
buffer = eval_to_string(cmd + 1, true, false);
} else {
buffer = get_cmd_output(cmd, NULL, (flags & EW_SILENT) ? kShellOptSilent : 0, NULL);
}
@ -1694,7 +1694,8 @@ static char *eval_includeexpr(const char *const ptr, const size_t len)
current_sctx = curbuf->b_p_script_ctx[BV_INEX].script_ctx;
char *res = eval_to_string_safe(curbuf->b_p_inex,
was_set_insecurely(curwin, kOptIncludeexpr, OPT_LOCAL));
was_set_insecurely(curwin, kOptIncludeexpr, OPT_LOCAL),
true);
set_vim_var_string(VV_FNAME, NULL, 0);
current_sctx = save_sctx;

View File

@ -2194,7 +2194,7 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen
}
tv_clear(&rettv);
} else {
eval_result[nested] = eval_to_string(source + 2, true);
eval_result[nested] = eval_to_string(source + 2, true, false);
}
nesting--;

View File

@ -1100,7 +1100,7 @@ static int load_pack_plugin(bool opt, char *fname)
// If runtime/filetype.lua wasn't loaded yet, the scripts will be
// found when it loads.
if (opt && eval_to_number(cmd) > 0) {
if (opt && eval_to_number(cmd, false) > 0) {
do_cmdline_cmd("augroup filetypedetect");
vim_snprintf(pat, len, ftpat, ffname);
gen_expand_wildcards_and_cb(1, &pat, EW_FILE, true, source_callback_vim_lua, NULL);

View File

@ -954,7 +954,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
};
set_var(S_LEN("g:statusline_winid"), &tv, false);
usefmt = eval_to_string_safe(fmt + 2, use_sandbox);
usefmt = eval_to_string_safe(fmt + 2, use_sandbox, false);
if (usefmt == NULL) {
usefmt = fmt;
}
@ -1429,7 +1429,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
}
// Note: The result stored in `t` is unused.
str = eval_to_string_safe(out_p, use_sandbox);
str = eval_to_string_safe(out_p, use_sandbox, false);
curwin = save_curwin;
curbuf = save_curbuf;

View File

@ -869,7 +869,7 @@ int fex_format(linenr_T lnum, long count, int c)
if (use_sandbox) {
sandbox++;
}
int r = (int)eval_to_number(fex);
int r = (int)eval_to_number(fex, true);
if (use_sandbox) {
sandbox--;
}