mirror of
https://github.com/neovim/neovim.git
synced 2025-09-19 09:48:19 +00:00
vim-patch:8.0.1505: debugger can't break on a condition
Problem: Debugger can't break on a condition. (Charles Campbell) Solution: Add ":breakadd expr". (Christian Brabandt, closes vim/vim#859)c6f9f739d3
Do not port "has_watchexpr()" to avoid dead code. "has_watchexpr()" always returns 0 because "debug_expr" is always 0. Restore "eval_expr()" as a wrapper to allocate "typval_T" for "eval0()". Remove it in later patches. Include "typval_compare()" changes from patch v8.1.0958, partially ported in8b60368c1b
. Close https://github.com/neovim/neovim/pull/12373 N/A patches for version.c: vim-patch:8.2.2720: GTK menu tooltip moves the cursor Problem: GTK menu tooltip moves the cursor. Solution: Position the cursor after displaying the tooltip. Do not show the tooltip when editing the command line.01ac0a1f66
This commit is contained in:
@@ -804,6 +804,19 @@ DEFINING BREAKPOINTS
|
|||||||
< Note that this only works for commands that are executed when
|
< Note that this only works for commands that are executed when
|
||||||
sourcing the file, not for a function defined in that file.
|
sourcing the file, not for a function defined in that file.
|
||||||
|
|
||||||
|
:breaka[dd] expr {expression}
|
||||||
|
Sets a breakpoint, that will break whenever the {expression}
|
||||||
|
evaluates to a different value. Example: >
|
||||||
|
:breakadd expr g:lnum
|
||||||
|
|
||||||
|
< Will break, whenever the global variable lnum changes.
|
||||||
|
Note if you watch a |script-variable| this will break
|
||||||
|
when switching scripts, since the script variable is only
|
||||||
|
valid in the script where it has been defined and if that
|
||||||
|
script is called from several other scripts, this will stop
|
||||||
|
whenever that particular variable will become visible or
|
||||||
|
unaccessible again.
|
||||||
|
|
||||||
The [lnum] is the line number of the breakpoint. Vim will stop at or after
|
The [lnum] is the line number of the breakpoint. Vim will stop at or after
|
||||||
this line. When omitted line 1 is used.
|
this line. When omitted line 1 is used.
|
||||||
|
|
||||||
|
424
src/nvim/eval.c
424
src/nvim/eval.c
@@ -916,6 +916,17 @@ varnumber_T eval_to_number(char_u *expr)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Top level evaluation function.
|
||||||
|
// Returns an allocated typval_T with the result.
|
||||||
|
// Returns NULL when there is an error.
|
||||||
|
typval_T *eval_expr(char_u *arg)
|
||||||
|
{
|
||||||
|
typval_T *tv = xmalloc(sizeof(*tv));
|
||||||
|
if (eval0(arg, tv, NULL, true) == FAIL) {
|
||||||
|
XFREE_CLEAR(tv);
|
||||||
|
}
|
||||||
|
return tv;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prepare v: variable "idx" to be used.
|
* Prepare v: variable "idx" to be used.
|
||||||
@@ -3129,21 +3140,6 @@ static int pattern_match(char_u *pat, char_u *text, bool ic)
|
|||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* types for expressions.
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
TYPE_UNKNOWN = 0,
|
|
||||||
TYPE_EQUAL, // ==
|
|
||||||
TYPE_NEQUAL, // !=
|
|
||||||
TYPE_GREATER, // >
|
|
||||||
TYPE_GEQUAL, // >=
|
|
||||||
TYPE_SMALLER, // <
|
|
||||||
TYPE_SEQUAL, // <=
|
|
||||||
TYPE_MATCH, // =~
|
|
||||||
TYPE_NOMATCH, // !~
|
|
||||||
} exptype_T;
|
|
||||||
|
|
||||||
// TODO(ZyX-I): move to eval/expressions
|
// TODO(ZyX-I): move to eval/expressions
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -3420,11 +3416,9 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
{
|
{
|
||||||
typval_T var2;
|
typval_T var2;
|
||||||
char_u *p;
|
char_u *p;
|
||||||
int i;
|
|
||||||
exptype_T type = TYPE_UNKNOWN;
|
exptype_T type = TYPE_UNKNOWN;
|
||||||
bool type_is = false; // true for "is" and "isnot"
|
bool type_is = false; // true for "is" and "isnot"
|
||||||
int len = 2;
|
int len = 2;
|
||||||
varnumber_T n1, n2;
|
|
||||||
bool ic;
|
bool ic;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -3491,173 +3485,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evaluate) {
|
return typval_compare(rettv, &var2, type, type_is, ic, evaluate);
|
||||||
if (type_is && rettv->v_type != var2.v_type) {
|
|
||||||
/* For "is" a different type always means FALSE, for "notis"
|
|
||||||
* it means TRUE. */
|
|
||||||
n1 = (type == TYPE_NEQUAL);
|
|
||||||
} else if (rettv->v_type == VAR_LIST || var2.v_type == VAR_LIST) {
|
|
||||||
if (type_is) {
|
|
||||||
n1 = (rettv->v_type == var2.v_type
|
|
||||||
&& rettv->vval.v_list == var2.vval.v_list);
|
|
||||||
if (type == TYPE_NEQUAL)
|
|
||||||
n1 = !n1;
|
|
||||||
} else if (rettv->v_type != var2.v_type
|
|
||||||
|| (type != TYPE_EQUAL && type != TYPE_NEQUAL)) {
|
|
||||||
if (rettv->v_type != var2.v_type) {
|
|
||||||
EMSG(_("E691: Can only compare List with List"));
|
|
||||||
} else {
|
|
||||||
EMSG(_("E692: Invalid operation for List"));
|
|
||||||
}
|
|
||||||
tv_clear(rettv);
|
|
||||||
tv_clear(&var2);
|
|
||||||
return FAIL;
|
|
||||||
} else {
|
|
||||||
// Compare two Lists for being equal or unequal.
|
|
||||||
n1 = tv_list_equal(rettv->vval.v_list, var2.vval.v_list, ic, false);
|
|
||||||
if (type == TYPE_NEQUAL) {
|
|
||||||
n1 = !n1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (rettv->v_type == VAR_DICT || var2.v_type == VAR_DICT) {
|
|
||||||
if (type_is) {
|
|
||||||
n1 = (rettv->v_type == var2.v_type
|
|
||||||
&& rettv->vval.v_dict == var2.vval.v_dict);
|
|
||||||
if (type == TYPE_NEQUAL)
|
|
||||||
n1 = !n1;
|
|
||||||
} else if (rettv->v_type != var2.v_type
|
|
||||||
|| (type != TYPE_EQUAL && type != TYPE_NEQUAL)) {
|
|
||||||
if (rettv->v_type != var2.v_type)
|
|
||||||
EMSG(_("E735: Can only compare Dictionary with Dictionary"));
|
|
||||||
else
|
|
||||||
EMSG(_("E736: Invalid operation for Dictionary"));
|
|
||||||
tv_clear(rettv);
|
|
||||||
tv_clear(&var2);
|
|
||||||
return FAIL;
|
|
||||||
} else {
|
|
||||||
// Compare two Dictionaries for being equal or unequal.
|
|
||||||
n1 = tv_dict_equal(rettv->vval.v_dict, var2.vval.v_dict,
|
|
||||||
ic, false);
|
|
||||||
if (type == TYPE_NEQUAL) {
|
|
||||||
n1 = !n1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (tv_is_func(*rettv) || tv_is_func(var2)) {
|
|
||||||
if (type != TYPE_EQUAL && type != TYPE_NEQUAL) {
|
|
||||||
EMSG(_("E694: Invalid operation for Funcrefs"));
|
|
||||||
tv_clear(rettv);
|
|
||||||
tv_clear(&var2);
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
if ((rettv->v_type == VAR_PARTIAL
|
|
||||||
&& rettv->vval.v_partial == NULL)
|
|
||||||
|| (var2.v_type == VAR_PARTIAL
|
|
||||||
&& var2.vval.v_partial == NULL)) {
|
|
||||||
// when a partial is NULL assume not equal
|
|
||||||
n1 = false;
|
|
||||||
} else if (type_is) {
|
|
||||||
if (rettv->v_type == VAR_FUNC && var2.v_type == VAR_FUNC) {
|
|
||||||
// strings are considered the same if their value is
|
|
||||||
// the same
|
|
||||||
n1 = tv_equal(rettv, &var2, ic, false);
|
|
||||||
} else if (rettv->v_type == VAR_PARTIAL
|
|
||||||
&& var2.v_type == VAR_PARTIAL) {
|
|
||||||
n1 = (rettv->vval.v_partial == var2.vval.v_partial);
|
|
||||||
} else {
|
|
||||||
n1 = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n1 = tv_equal(rettv, &var2, ic, false);
|
|
||||||
}
|
|
||||||
if (type == TYPE_NEQUAL) {
|
|
||||||
n1 = !n1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* If one of the two variables is a float, compare as a float.
|
|
||||||
* When using "=~" or "!~", always compare as string.
|
|
||||||
*/
|
|
||||||
else if ((rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT)
|
|
||||||
&& type != TYPE_MATCH && type != TYPE_NOMATCH) {
|
|
||||||
float_T f1, f2;
|
|
||||||
|
|
||||||
if (rettv->v_type == VAR_FLOAT) {
|
|
||||||
f1 = rettv->vval.v_float;
|
|
||||||
} else {
|
|
||||||
f1 = tv_get_number(rettv);
|
|
||||||
}
|
|
||||||
if (var2.v_type == VAR_FLOAT) {
|
|
||||||
f2 = var2.vval.v_float;
|
|
||||||
} else {
|
|
||||||
f2 = tv_get_number(&var2);
|
|
||||||
}
|
|
||||||
n1 = false;
|
|
||||||
switch (type) {
|
|
||||||
case TYPE_EQUAL: n1 = (f1 == f2); break;
|
|
||||||
case TYPE_NEQUAL: n1 = (f1 != f2); break;
|
|
||||||
case TYPE_GREATER: n1 = (f1 > f2); break;
|
|
||||||
case TYPE_GEQUAL: n1 = (f1 >= f2); break;
|
|
||||||
case TYPE_SMALLER: n1 = (f1 < f2); break;
|
|
||||||
case TYPE_SEQUAL: n1 = (f1 <= f2); break;
|
|
||||||
case TYPE_UNKNOWN:
|
|
||||||
case TYPE_MATCH:
|
|
||||||
case TYPE_NOMATCH: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* If one of the two variables is a number, compare as a number.
|
|
||||||
* When using "=~" or "!~", always compare as string.
|
|
||||||
*/
|
|
||||||
else if ((rettv->v_type == VAR_NUMBER || var2.v_type == VAR_NUMBER)
|
|
||||||
&& type != TYPE_MATCH && type != TYPE_NOMATCH) {
|
|
||||||
n1 = tv_get_number(rettv);
|
|
||||||
n2 = tv_get_number(&var2);
|
|
||||||
switch (type) {
|
|
||||||
case TYPE_EQUAL: n1 = (n1 == n2); break;
|
|
||||||
case TYPE_NEQUAL: n1 = (n1 != n2); break;
|
|
||||||
case TYPE_GREATER: n1 = (n1 > n2); break;
|
|
||||||
case TYPE_GEQUAL: n1 = (n1 >= n2); break;
|
|
||||||
case TYPE_SMALLER: n1 = (n1 < n2); break;
|
|
||||||
case TYPE_SEQUAL: n1 = (n1 <= n2); break;
|
|
||||||
case TYPE_UNKNOWN:
|
|
||||||
case TYPE_MATCH:
|
|
||||||
case TYPE_NOMATCH: break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
char buf1[NUMBUFLEN];
|
|
||||||
char buf2[NUMBUFLEN];
|
|
||||||
const char *const s1 = tv_get_string_buf(rettv, buf1);
|
|
||||||
const char *const s2 = tv_get_string_buf(&var2, buf2);
|
|
||||||
if (type != TYPE_MATCH && type != TYPE_NOMATCH) {
|
|
||||||
i = mb_strcmp_ic(ic, s1, s2);
|
|
||||||
} else {
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
n1 = false;
|
|
||||||
switch (type) {
|
|
||||||
case TYPE_EQUAL: n1 = (i == 0); break;
|
|
||||||
case TYPE_NEQUAL: n1 = (i != 0); break;
|
|
||||||
case TYPE_GREATER: n1 = (i > 0); break;
|
|
||||||
case TYPE_GEQUAL: n1 = (i >= 0); break;
|
|
||||||
case TYPE_SMALLER: n1 = (i < 0); break;
|
|
||||||
case TYPE_SEQUAL: n1 = (i <= 0); break;
|
|
||||||
|
|
||||||
case TYPE_MATCH:
|
|
||||||
case TYPE_NOMATCH: {
|
|
||||||
n1 = pattern_match((char_u *)s2, (char_u *)s1, ic);
|
|
||||||
if (type == TYPE_NOMATCH) {
|
|
||||||
n1 = !n1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TYPE_UNKNOWN: break; // Avoid gcc warning.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tv_clear(rettv);
|
|
||||||
tv_clear(&var2);
|
|
||||||
rettv->v_type = VAR_NUMBER;
|
|
||||||
rettv->vval.v_number = n1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
@@ -8000,8 +7828,8 @@ int get_id_len(const char **const arg)
|
|||||||
*/
|
*/
|
||||||
int get_name_len(const char **const arg,
|
int get_name_len(const char **const arg,
|
||||||
char **alias,
|
char **alias,
|
||||||
int evaluate,
|
bool evaluate,
|
||||||
int verbose)
|
bool verbose)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
@@ -8486,10 +8314,8 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg)
|
|||||||
return oldval;
|
return oldval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Get the value of internal variable "name".
|
||||||
* Get the value of internal variable "name".
|
// Return OK or FAIL. If OK is returned "rettv" must be cleared.
|
||||||
* Return OK or FAIL.
|
|
||||||
*/
|
|
||||||
int get_var_tv(
|
int get_var_tv(
|
||||||
const char *name,
|
const char *name,
|
||||||
int len, // length of "name"
|
int len, // length of "name"
|
||||||
@@ -10746,3 +10572,221 @@ bool invoke_prompt_interrupt(void)
|
|||||||
tv_clear(&rettv);
|
tv_clear(&rettv);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int typval_compare(
|
||||||
|
typval_T *typ1, // first operand
|
||||||
|
typval_T *typ2, // second operand
|
||||||
|
exptype_T type, // operator
|
||||||
|
bool type_is, // true for "is" and "isnot"
|
||||||
|
bool ic, // ignore case
|
||||||
|
bool evaluate
|
||||||
|
)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
varnumber_T n1, n2;
|
||||||
|
|
||||||
|
if (evaluate) {
|
||||||
|
if (type_is && typ1->v_type != typ2->v_type) {
|
||||||
|
// For "is" a different type always means false, for "notis"
|
||||||
|
// it means true.
|
||||||
|
n1 = type == TYPE_NEQUAL;
|
||||||
|
} else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST) {
|
||||||
|
if (type_is) {
|
||||||
|
n1 = typ1->v_type == typ2->v_type
|
||||||
|
&& typ1->vval.v_list == typ2->vval.v_list;
|
||||||
|
if (type == TYPE_NEQUAL) {
|
||||||
|
n1 = !n1;
|
||||||
|
}
|
||||||
|
} else if (typ1->v_type != typ2->v_type
|
||||||
|
|| (type != TYPE_EQUAL && type != TYPE_NEQUAL)) {
|
||||||
|
if (typ1->v_type != typ2->v_type) {
|
||||||
|
EMSG(_("E691: Can only compare List with List"));
|
||||||
|
} else {
|
||||||
|
EMSG(_("E692: Invalid operation for List"));
|
||||||
|
}
|
||||||
|
tv_clear(typ1);
|
||||||
|
tv_clear(typ2);
|
||||||
|
return FAIL;
|
||||||
|
} else {
|
||||||
|
// Compare two Lists for being equal or unequal.
|
||||||
|
n1 = tv_list_equal(typ1->vval.v_list, typ2->vval.v_list, ic, false);
|
||||||
|
if (type == TYPE_NEQUAL) {
|
||||||
|
n1 = !n1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (typ1->v_type == VAR_DICT || typ2->v_type == VAR_DICT) {
|
||||||
|
if (type_is) {
|
||||||
|
n1 = typ1->v_type == typ2->v_type
|
||||||
|
&& typ1->vval.v_dict == typ2->vval.v_dict;
|
||||||
|
if (type == TYPE_NEQUAL) {
|
||||||
|
n1 = !n1;
|
||||||
|
}
|
||||||
|
} else if (typ1->v_type != typ2->v_type
|
||||||
|
|| (type != TYPE_EQUAL && type != TYPE_NEQUAL)) {
|
||||||
|
if (typ1->v_type != typ2->v_type) {
|
||||||
|
EMSG(_("E735: Can only compare Dictionary with Dictionary"));
|
||||||
|
} else {
|
||||||
|
EMSG(_("E736: Invalid operation for Dictionary"));
|
||||||
|
}
|
||||||
|
tv_clear(typ1);
|
||||||
|
tv_clear(typ2);
|
||||||
|
return FAIL;
|
||||||
|
} else {
|
||||||
|
// Compare two Dictionaries for being equal or unequal.
|
||||||
|
n1 = tv_dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, ic, false);
|
||||||
|
if (type == TYPE_NEQUAL) {
|
||||||
|
n1 = !n1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (tv_is_func(*typ1) || tv_is_func(*typ2)) {
|
||||||
|
if (type != TYPE_EQUAL && type != TYPE_NEQUAL) {
|
||||||
|
EMSG(_("E694: Invalid operation for Funcrefs"));
|
||||||
|
tv_clear(typ1);
|
||||||
|
tv_clear(typ2);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
if ((typ1->v_type == VAR_PARTIAL && typ1->vval.v_partial == NULL)
|
||||||
|
|| (typ2->v_type == VAR_PARTIAL && typ2->vval.v_partial == NULL)) {
|
||||||
|
// when a partial is NULL assume not equal
|
||||||
|
n1 = false;
|
||||||
|
} else if (type_is) {
|
||||||
|
if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC) {
|
||||||
|
// strings are considered the same if their value is
|
||||||
|
// the same
|
||||||
|
n1 = tv_equal(typ1, typ2, ic, false);
|
||||||
|
} else if (typ1->v_type == VAR_PARTIAL
|
||||||
|
&& typ2->v_type == VAR_PARTIAL) {
|
||||||
|
n1 = typ1->vval.v_partial == typ2->vval.v_partial;
|
||||||
|
} else {
|
||||||
|
n1 = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n1 = tv_equal(typ1, typ2, ic, false);
|
||||||
|
}
|
||||||
|
if (type == TYPE_NEQUAL) {
|
||||||
|
n1 = !n1;
|
||||||
|
}
|
||||||
|
} else if ((typ1->v_type == VAR_FLOAT || typ2->v_type == VAR_FLOAT)
|
||||||
|
&& type != TYPE_MATCH && type != TYPE_NOMATCH) {
|
||||||
|
// If one of the two variables is a float, compare as a float.
|
||||||
|
// When using "=~" or "!~", always compare as string.
|
||||||
|
const float_T f1 = tv_get_float(typ1);
|
||||||
|
const float_T f2 = tv_get_float(typ2);
|
||||||
|
n1 = false;
|
||||||
|
switch (type) {
|
||||||
|
case TYPE_EQUAL: n1 = f1 == f2; break;
|
||||||
|
case TYPE_NEQUAL: n1 = f1 != f2; break;
|
||||||
|
case TYPE_GREATER: n1 = f1 > f2; break;
|
||||||
|
case TYPE_GEQUAL: n1 = f1 >= f2; break;
|
||||||
|
case TYPE_SMALLER: n1 = f1 < f2; break;
|
||||||
|
case TYPE_SEQUAL: n1 = f1 <= f2; break;
|
||||||
|
case TYPE_UNKNOWN:
|
||||||
|
case TYPE_MATCH:
|
||||||
|
case TYPE_NOMATCH: break;
|
||||||
|
}
|
||||||
|
} else if ((typ1->v_type == VAR_NUMBER || typ2->v_type == VAR_NUMBER)
|
||||||
|
&& type != TYPE_MATCH && type != TYPE_NOMATCH) {
|
||||||
|
// If one of the two variables is a number, compare as a number.
|
||||||
|
// When using "=~" or "!~", always compare as string.
|
||||||
|
n1 = tv_get_number(typ1);
|
||||||
|
n2 = tv_get_number(typ2);
|
||||||
|
switch (type) {
|
||||||
|
case TYPE_EQUAL: n1 = n1 == n2; break;
|
||||||
|
case TYPE_NEQUAL: n1 = n1 != n2; break;
|
||||||
|
case TYPE_GREATER: n1 = n1 > n2; break;
|
||||||
|
case TYPE_GEQUAL: n1 = n1 >= n2; break;
|
||||||
|
case TYPE_SMALLER: n1 = n1 < n2; break;
|
||||||
|
case TYPE_SEQUAL: n1 = n1 <= n2; break;
|
||||||
|
case TYPE_UNKNOWN:
|
||||||
|
case TYPE_MATCH:
|
||||||
|
case TYPE_NOMATCH: break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
char buf1[NUMBUFLEN];
|
||||||
|
char buf2[NUMBUFLEN];
|
||||||
|
const char *const s1 = tv_get_string_buf(typ1, buf1);
|
||||||
|
const char *const s2 = tv_get_string_buf(typ2, buf2);
|
||||||
|
int i;
|
||||||
|
if (type != TYPE_MATCH && type != TYPE_NOMATCH) {
|
||||||
|
i = mb_strcmp_ic(ic, s1, s2);
|
||||||
|
} else {
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
n1 = false;
|
||||||
|
switch (type) {
|
||||||
|
case TYPE_EQUAL: n1 = i == 0; break;
|
||||||
|
case TYPE_NEQUAL: n1 = i != 0; break;
|
||||||
|
case TYPE_GREATER: n1 = i > 0; break;
|
||||||
|
case TYPE_GEQUAL: n1 = i >= 0; break;
|
||||||
|
case TYPE_SMALLER: n1 = i < 0; break;
|
||||||
|
case TYPE_SEQUAL: n1 = i <= 0; break;
|
||||||
|
|
||||||
|
case TYPE_MATCH:
|
||||||
|
case TYPE_NOMATCH:
|
||||||
|
n1 = pattern_match((char_u *)s2, (char_u *)s1, ic);
|
||||||
|
if (type == TYPE_NOMATCH) {
|
||||||
|
n1 = !n1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TYPE_UNKNOWN: break; // Avoid gcc warning.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tv_clear(typ1);
|
||||||
|
tv_clear(typ2);
|
||||||
|
typ1->v_type = VAR_NUMBER;
|
||||||
|
typ1->vval.v_number = n1;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int typval_copy(typval_T *typ1, typval_T *typ2)
|
||||||
|
{
|
||||||
|
if (typ2 == NULL) {
|
||||||
|
tv_list_alloc_ret(typ2, kListLenUnknown);
|
||||||
|
}
|
||||||
|
if (typ1 != NULL && typ2 != NULL) {
|
||||||
|
return var_item_copy(NULL, typ1, typ2, true, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *typval_tostring(typval_T *arg)
|
||||||
|
{
|
||||||
|
if (arg == NULL) {
|
||||||
|
return xstrdup("(does not exist)");
|
||||||
|
}
|
||||||
|
return encode_tv2string(arg, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool var_exists(const char *var)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
char *tofree;
|
||||||
|
bool n = false;
|
||||||
|
|
||||||
|
// get_name_len() takes care of expanding curly braces
|
||||||
|
const char *name = var;
|
||||||
|
const int len = get_name_len((const char **)&var, &tofree, true, false);
|
||||||
|
if (len > 0) {
|
||||||
|
typval_T tv;
|
||||||
|
|
||||||
|
if (tofree != NULL) {
|
||||||
|
name = tofree;
|
||||||
|
}
|
||||||
|
n = get_var_tv(name, len, &tv, NULL, false, true) == OK;
|
||||||
|
if (n) {
|
||||||
|
// Handle d.key, l[idx], f(expr).
|
||||||
|
n = handle_subscript(&var, &tv, true, false) == OK;
|
||||||
|
if (n) {
|
||||||
|
tv_clear(&tv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*var != NUL) {
|
||||||
|
n = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xfree(tofree);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
@@ -227,6 +227,19 @@ typedef enum
|
|||||||
ASSERT_OTHER,
|
ASSERT_OTHER,
|
||||||
} assert_type_T;
|
} assert_type_T;
|
||||||
|
|
||||||
|
/// types for expressions.
|
||||||
|
typedef enum {
|
||||||
|
TYPE_UNKNOWN = 0,
|
||||||
|
TYPE_EQUAL, ///< ==
|
||||||
|
TYPE_NEQUAL, ///< !=
|
||||||
|
TYPE_GREATER, ///< >
|
||||||
|
TYPE_GEQUAL, ///< >=
|
||||||
|
TYPE_SMALLER, ///< <
|
||||||
|
TYPE_SEQUAL, ///< <=
|
||||||
|
TYPE_MATCH, ///< =~
|
||||||
|
TYPE_NOMATCH, ///< !~
|
||||||
|
} exptype_T;
|
||||||
|
|
||||||
/// Type for dict_list function
|
/// Type for dict_list function
|
||||||
typedef enum {
|
typedef enum {
|
||||||
kDictListKeys, ///< List dictionary keys.
|
kDictListKeys, ///< List dictionary keys.
|
||||||
|
@@ -2051,7 +2051,6 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
{
|
{
|
||||||
int n = false;
|
int n = false;
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
const char *p = tv_get_string(&argvars[0]);
|
const char *p = tv_get_string(&argvars[0]);
|
||||||
if (*p == '$') { // Environment variable.
|
if (*p == '$') { // Environment variable.
|
||||||
@@ -2082,29 +2081,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
n = au_exists(p + 1);
|
n = au_exists(p + 1);
|
||||||
}
|
}
|
||||||
} else { // Internal variable.
|
} else { // Internal variable.
|
||||||
typval_T tv;
|
n = var_exists(p);
|
||||||
|
|
||||||
// get_name_len() takes care of expanding curly braces
|
|
||||||
const char *name = p;
|
|
||||||
char *tofree;
|
|
||||||
len = get_name_len((const char **)&p, &tofree, true, false);
|
|
||||||
if (len > 0) {
|
|
||||||
if (tofree != NULL) {
|
|
||||||
name = tofree;
|
|
||||||
}
|
|
||||||
n = (get_var_tv(name, len, &tv, NULL, false, true) == OK);
|
|
||||||
if (n) {
|
|
||||||
// Handle d.key, l[idx], f(expr).
|
|
||||||
n = (handle_subscript(&p, &tv, true, false) == OK);
|
|
||||||
if (n) {
|
|
||||||
tv_clear(&tv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (*p != NUL)
|
|
||||||
n = FALSE;
|
|
||||||
|
|
||||||
xfree(tofree);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rettv->vval.v_number = n;
|
rettv->vval.v_number = n;
|
||||||
|
@@ -120,6 +120,9 @@ struct source_cookie {
|
|||||||
/// batch mode debugging: don't save and restore typeahead.
|
/// batch mode debugging: don't save and restore typeahead.
|
||||||
static bool debug_greedy = false;
|
static bool debug_greedy = false;
|
||||||
|
|
||||||
|
static char *debug_oldval = NULL; // old and newval for debug expressions
|
||||||
|
static char *debug_newval = NULL;
|
||||||
|
|
||||||
/// Debug mode. Repeatedly get Ex commands, until told to continue normal
|
/// Debug mode. Repeatedly get Ex commands, until told to continue normal
|
||||||
/// execution.
|
/// execution.
|
||||||
void do_debug(char_u *cmd)
|
void do_debug(char_u *cmd)
|
||||||
@@ -166,6 +169,16 @@ void do_debug(char_u *cmd)
|
|||||||
if (!debug_did_msg) {
|
if (!debug_did_msg) {
|
||||||
MSG(_("Entering Debug mode. Type \"cont\" to continue."));
|
MSG(_("Entering Debug mode. Type \"cont\" to continue."));
|
||||||
}
|
}
|
||||||
|
if (debug_oldval != NULL) {
|
||||||
|
smsg(_("Oldval = \"%s\""), debug_oldval);
|
||||||
|
xfree(debug_oldval);
|
||||||
|
debug_oldval = NULL;
|
||||||
|
}
|
||||||
|
if (debug_newval != NULL) {
|
||||||
|
smsg(_("Newval = \"%s\""), debug_newval);
|
||||||
|
xfree(debug_newval);
|
||||||
|
debug_newval = NULL;
|
||||||
|
}
|
||||||
if (sourcing_name != NULL) {
|
if (sourcing_name != NULL) {
|
||||||
msg(sourcing_name);
|
msg(sourcing_name);
|
||||||
}
|
}
|
||||||
@@ -174,7 +187,6 @@ void do_debug(char_u *cmd)
|
|||||||
} else {
|
} else {
|
||||||
smsg(_("cmd: %s"), cmd);
|
smsg(_("cmd: %s"), cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeat getting a command and executing it.
|
// Repeat getting a command and executing it.
|
||||||
for (;; ) {
|
for (;; ) {
|
||||||
msg_scroll = true;
|
msg_scroll = true;
|
||||||
@@ -514,11 +526,13 @@ bool dbg_check_skipped(exarg_T *eap)
|
|||||||
/// This is a grow-array of structs.
|
/// This is a grow-array of structs.
|
||||||
struct debuggy {
|
struct debuggy {
|
||||||
int dbg_nr; ///< breakpoint number
|
int dbg_nr; ///< breakpoint number
|
||||||
int dbg_type; ///< DBG_FUNC or DBG_FILE
|
int dbg_type; ///< DBG_FUNC or DBG_FILE or DBG_EXPR
|
||||||
char_u *dbg_name; ///< function or file name
|
char_u *dbg_name; ///< function, expression or file name
|
||||||
regprog_T *dbg_prog; ///< regexp program
|
regprog_T *dbg_prog; ///< regexp program
|
||||||
linenr_T dbg_lnum; ///< line number in function or file
|
linenr_T dbg_lnum; ///< line number in function or file
|
||||||
int dbg_forceit; ///< ! used
|
int dbg_forceit; ///< ! used
|
||||||
|
typval_T *dbg_val; ///< last result of watchexpression
|
||||||
|
int dbg_level; ///< stored nested level for expr
|
||||||
};
|
};
|
||||||
|
|
||||||
static garray_T dbg_breakp = { 0, 0, sizeof(struct debuggy), 4, NULL };
|
static garray_T dbg_breakp = { 0, 0, sizeof(struct debuggy), 4, NULL };
|
||||||
@@ -530,6 +544,7 @@ static int last_breakp = 0; // nr of last defined breakpoint
|
|||||||
static garray_T prof_ga = { 0, 0, sizeof(struct debuggy), 4, NULL };
|
static garray_T prof_ga = { 0, 0, sizeof(struct debuggy), 4, NULL };
|
||||||
#define DBG_FUNC 1
|
#define DBG_FUNC 1
|
||||||
#define DBG_FILE 2
|
#define DBG_FILE 2
|
||||||
|
#define DBG_EXPR 3
|
||||||
|
|
||||||
|
|
||||||
/// Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them
|
/// Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them
|
||||||
@@ -562,6 +577,8 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
|
|||||||
}
|
}
|
||||||
bp->dbg_type = DBG_FILE;
|
bp->dbg_type = DBG_FILE;
|
||||||
here = true;
|
here = true;
|
||||||
|
} else if (gap != &prof_ga && STRNCMP(p, "expr", 4) == 0) {
|
||||||
|
bp->dbg_type = DBG_EXPR;
|
||||||
} else {
|
} else {
|
||||||
EMSG2(_(e_invarg2), p);
|
EMSG2(_(e_invarg2), p);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -590,6 +607,9 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
|
|||||||
bp->dbg_name = vim_strsave(p);
|
bp->dbg_name = vim_strsave(p);
|
||||||
} else if (here) {
|
} else if (here) {
|
||||||
bp->dbg_name = vim_strsave(curbuf->b_ffname);
|
bp->dbg_name = vim_strsave(curbuf->b_ffname);
|
||||||
|
} else if (bp->dbg_type == DBG_EXPR) {
|
||||||
|
bp->dbg_name = vim_strsave(p);
|
||||||
|
bp->dbg_val = eval_expr(bp->dbg_name);
|
||||||
} else {
|
} else {
|
||||||
// Expand the file name in the same way as do_source(). This means
|
// Expand the file name in the same way as do_source(). This means
|
||||||
// doing it twice, so that $DIR/file gets expanded when $DIR is
|
// doing it twice, so that $DIR/file gets expanded when $DIR is
|
||||||
@@ -621,7 +641,6 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
|
|||||||
void ex_breakadd(exarg_T *eap)
|
void ex_breakadd(exarg_T *eap)
|
||||||
{
|
{
|
||||||
struct debuggy *bp;
|
struct debuggy *bp;
|
||||||
char_u *pat;
|
|
||||||
garray_T *gap;
|
garray_T *gap;
|
||||||
|
|
||||||
gap = &dbg_breakp;
|
gap = &dbg_breakp;
|
||||||
@@ -633,22 +652,28 @@ void ex_breakadd(exarg_T *eap)
|
|||||||
bp = &DEBUGGY(gap, gap->ga_len);
|
bp = &DEBUGGY(gap, gap->ga_len);
|
||||||
bp->dbg_forceit = eap->forceit;
|
bp->dbg_forceit = eap->forceit;
|
||||||
|
|
||||||
pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, false);
|
if (bp->dbg_type != DBG_EXPR) {
|
||||||
if (pat != NULL) {
|
char_u *pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, false);
|
||||||
bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
|
if (pat != NULL) {
|
||||||
xfree(pat);
|
bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
|
||||||
}
|
xfree(pat);
|
||||||
if (pat == NULL || bp->dbg_prog == NULL) {
|
}
|
||||||
xfree(bp->dbg_name);
|
if (pat == NULL || bp->dbg_prog == NULL) {
|
||||||
|
xfree(bp->dbg_name);
|
||||||
|
} else {
|
||||||
|
if (bp->dbg_lnum == 0) { // default line number is 1
|
||||||
|
bp->dbg_lnum = 1;
|
||||||
|
}
|
||||||
|
if (eap->cmdidx != CMD_profile) {
|
||||||
|
DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
|
||||||
|
debug_tick++;
|
||||||
|
}
|
||||||
|
gap->ga_len++;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (bp->dbg_lnum == 0) { // default line number is 1
|
// DBG_EXPR
|
||||||
bp->dbg_lnum = 1;
|
DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp;
|
||||||
}
|
debug_tick++;
|
||||||
if (eap->cmdidx != CMD_profile) {
|
|
||||||
DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
|
|
||||||
debug_tick++;
|
|
||||||
}
|
|
||||||
gap->ga_len++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -691,7 +716,7 @@ void ex_breakdel(exarg_T *eap)
|
|||||||
todel = 0;
|
todel = 0;
|
||||||
del_all = true;
|
del_all = true;
|
||||||
} else {
|
} else {
|
||||||
// ":breakdel {func|file} [lnum] {name}"
|
// ":breakdel {func|file|expr} [lnum] {name}"
|
||||||
if (dbg_parsearg(eap->arg, gap) == FAIL) {
|
if (dbg_parsearg(eap->arg, gap) == FAIL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -716,6 +741,10 @@ void ex_breakdel(exarg_T *eap)
|
|||||||
} else {
|
} else {
|
||||||
while (!GA_EMPTY(gap)) {
|
while (!GA_EMPTY(gap)) {
|
||||||
xfree(DEBUGGY(gap, todel).dbg_name);
|
xfree(DEBUGGY(gap, todel).dbg_name);
|
||||||
|
if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR
|
||||||
|
&& DEBUGGY(gap, todel).dbg_val != NULL) {
|
||||||
|
tv_free(DEBUGGY(gap, todel).dbg_val);
|
||||||
|
}
|
||||||
vim_regfree(DEBUGGY(gap, todel).dbg_prog);
|
vim_regfree(DEBUGGY(gap, todel).dbg_prog);
|
||||||
gap->ga_len--;
|
gap->ga_len--;
|
||||||
if (todel < gap->ga_len) {
|
if (todel < gap->ga_len) {
|
||||||
@@ -750,11 +779,15 @@ void ex_breaklist(exarg_T *eap)
|
|||||||
if (bp->dbg_type == DBG_FILE) {
|
if (bp->dbg_type == DBG_FILE) {
|
||||||
home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, true);
|
home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, true);
|
||||||
}
|
}
|
||||||
smsg(_("%3d %s %s line %" PRId64),
|
if (bp->dbg_type != DBG_EXPR) {
|
||||||
bp->dbg_nr,
|
smsg(_("%3d %s %s line %" PRId64),
|
||||||
bp->dbg_type == DBG_FUNC ? "func" : "file",
|
bp->dbg_nr,
|
||||||
bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
|
bp->dbg_type == DBG_FUNC ? "func" : "file",
|
||||||
(int64_t)bp->dbg_lnum);
|
bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
|
||||||
|
(int64_t)bp->dbg_lnum);
|
||||||
|
} else {
|
||||||
|
smsg(_("%3d expr %s"), bp->dbg_nr, bp->dbg_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -814,6 +847,7 @@ debuggy_find(
|
|||||||
// an already found breakpoint.
|
// an already found breakpoint.
|
||||||
bp = &DEBUGGY(gap, i);
|
bp = &DEBUGGY(gap, i);
|
||||||
if ((bp->dbg_type == DBG_FILE) == file
|
if ((bp->dbg_type == DBG_FILE) == file
|
||||||
|
&& bp->dbg_type != DBG_EXPR
|
||||||
&& (gap == &prof_ga
|
&& (gap == &prof_ga
|
||||||
|| (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum)))) {
|
|| (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum)))) {
|
||||||
// Save the value of got_int and reset it. We don't want a
|
// Save the value of got_int and reset it. We don't want a
|
||||||
@@ -827,6 +861,49 @@ debuggy_find(
|
|||||||
*fp = bp->dbg_forceit;
|
*fp = bp->dbg_forceit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
got_int |= prev_got_int;
|
||||||
|
} else if (bp->dbg_type == DBG_EXPR) {
|
||||||
|
bool line = false;
|
||||||
|
|
||||||
|
prev_got_int = got_int;
|
||||||
|
got_int = false;
|
||||||
|
|
||||||
|
typval_T *tv = eval_expr(bp->dbg_name);
|
||||||
|
if (tv != NULL) {
|
||||||
|
if (bp->dbg_val == NULL) {
|
||||||
|
debug_oldval = typval_tostring(NULL);
|
||||||
|
bp->dbg_val = tv;
|
||||||
|
debug_newval = typval_tostring(bp->dbg_val);
|
||||||
|
line = true;
|
||||||
|
} else {
|
||||||
|
typval_T val3;
|
||||||
|
|
||||||
|
if (typval_copy(bp->dbg_val, &val3) == OK) {
|
||||||
|
if (typval_compare(tv, &val3, TYPE_EQUAL, true, false, true) == OK
|
||||||
|
&& tv->vval.v_number == false) {
|
||||||
|
line = true;
|
||||||
|
debug_oldval = typval_tostring(bp->dbg_val);
|
||||||
|
typval_T *v = eval_expr(bp->dbg_name);
|
||||||
|
debug_newval = typval_tostring(v);
|
||||||
|
tv_free(bp->dbg_val);
|
||||||
|
bp->dbg_val = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tv_free(tv);
|
||||||
|
}
|
||||||
|
} else if (bp->dbg_val != NULL) {
|
||||||
|
debug_oldval = typval_tostring(bp->dbg_val);
|
||||||
|
debug_newval = typval_tostring(NULL);
|
||||||
|
tv_free(bp->dbg_val);
|
||||||
|
bp->dbg_val = NULL;
|
||||||
|
line = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line) {
|
||||||
|
lnum = after > 0 ? after : 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
got_int |= prev_got_int;
|
got_int |= prev_got_int;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user