0
0
mirror of https://github.com/vim/vim.git synced 2025-09-23 03:43:49 -04:00

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 #859)
This commit is contained in:
Bram Moolenaar
2018-02-11 19:06:26 +01:00
parent c33ecb2915
commit c6f9f739d3
10 changed files with 460 additions and 261 deletions

View File

@@ -806,6 +806,19 @@ DEFINING BREAKPOINTS
< Note that this only works for commands that are executed when
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
this line. When omitted line 1 is used.

View File

@@ -3236,22 +3236,6 @@ pattern_match(char_u *pat, char_u *text, int ic)
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;
/*
* 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
@@ -3531,9 +3515,6 @@ eval4(char_u **arg, typval_T *rettv, int evaluate)
exptype_T type = TYPE_UNKNOWN;
int type_is = FALSE; /* TRUE for "is" and "isnot" */
int len = 2;
varnumber_T n1, n2;
char_u *s1, *s2;
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
int ic;
/*
@@ -3615,198 +3596,7 @@ eval4(char_u **arg, typval_T *rettv, int evaluate)
clear_tv(rettv);
return FAIL;
}
if (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"));
clear_tv(rettv);
clear_tv(&var2);
return FAIL;
}
else
{
/* Compare two Lists for being equal or unequal. */
n1 = 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"));
clear_tv(rettv);
clear_tv(&var2);
return FAIL;
}
else
{
/* Compare two Dictionaries for being equal or unequal. */
n1 = dict_equal(rettv->vval.v_dict, var2.vval.v_dict,
ic, FALSE);
if (type == TYPE_NEQUAL)
n1 = !n1;
}
}
else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC
|| rettv->v_type == VAR_PARTIAL || var2.v_type == VAR_PARTIAL)
{
if (type != TYPE_EQUAL && type != TYPE_NEQUAL)
{
EMSG(_("E694: Invalid operation for Funcrefs"));
clear_tv(rettv);
clear_tv(&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;
}
#ifdef FEAT_FLOAT
/*
* 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 = get_tv_number(rettv);
if (var2.v_type == VAR_FLOAT)
f2 = var2.vval.v_float;
else
f2 = get_tv_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; /* avoid gcc warning */
}
}
#endif
/*
* 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 = get_tv_number(rettv);
n2 = get_tv_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; /* avoid gcc warning */
}
}
else
{
s1 = get_tv_string_buf(rettv, buf1);
s2 = get_tv_string_buf(&var2, buf2);
if (type != TYPE_MATCH && type != TYPE_NOMATCH)
i = ic ? MB_STRICMP(s1, s2) : STRCMP(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(s2, s1, ic);
if (type == TYPE_NOMATCH)
n1 = !n1;
break;
case TYPE_UNKNOWN: break; /* avoid gcc warning */
}
}
clear_tv(rettv);
clear_tv(&var2);
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = n1;
}
return typval_compare(rettv, &var2, type, type_is, ic, evaluate);
}
return OK;
@@ -6840,7 +6630,7 @@ set_cmdarg(exarg_T *eap, char_u *oldarg)
/*
* Get the value of internal variable "name".
* Return OK or FAIL.
* Return OK or FAIL. If OK is returned "rettv" must be cleared.
*/
int
get_var_tv(
@@ -8419,7 +8209,7 @@ ex_execute(exarg_T *eap)
win_T *
find_win_by_nr(
typval_T *vp,
tabpage_T *tp UNUSED) /* NULL for current tab page */
tabpage_T *tp) /* NULL for current tab page */
{
win_T *wp;
int nr;
@@ -9279,6 +9069,277 @@ fill_assert_error(
}
int
typval_compare(
typval_T *typ1, /* first operand */
typval_T *typ2, /* second operand */
exptype_T type, /* operator */
int type_is, /* TRUE for "is" and "isnot" */
int ic, /* ignore case */
int evaluate)
{
int i;
varnumber_T n1, n2;
char_u *s1, *s2;
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
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"));
clear_tv(typ1);
clear_tv(typ2);
return FAIL;
}
else
{
/* Compare two Lists for being equal or unequal. */
n1 = 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"));
clear_tv(typ1);
clear_tv(typ2);
return FAIL;
}
else
{
/* Compare two Dictionaries for being equal or unequal. */
n1 = dict_equal(typ1->vval.v_dict, typ2->vval.v_dict,
ic, FALSE);
if (type == TYPE_NEQUAL)
n1 = !n1;
}
}
else if (typ1->v_type == VAR_FUNC || typ2->v_type == VAR_FUNC
|| typ1->v_type == VAR_PARTIAL || typ2->v_type == VAR_PARTIAL)
{
if (type != TYPE_EQUAL && type != TYPE_NEQUAL)
{
EMSG(_("E694: Invalid operation for Funcrefs"));
clear_tv(typ1);
clear_tv(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;
}
#ifdef FEAT_FLOAT
/*
* If one of the two variables is a float, compare as a float.
* When using "=~" or "!~", always compare as string.
*/
else if ((typ1->v_type == VAR_FLOAT || typ2->v_type == VAR_FLOAT)
&& type != TYPE_MATCH && type != TYPE_NOMATCH)
{
float_T f1, f2;
if (typ1->v_type == VAR_FLOAT)
f1 = typ1->vval.v_float;
else
f1 = get_tv_number(typ1);
if (typ2->v_type == VAR_FLOAT)
f2 = typ2->vval.v_float;
else
f2 = get_tv_number(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; /* avoid gcc warning */
}
}
#endif
/*
* If one of the two variables is a number, compare as a number.
* When using "=~" or "!~", always compare as string.
*/
else if ((typ1->v_type == VAR_NUMBER || typ2->v_type == VAR_NUMBER)
&& type != TYPE_MATCH && type != TYPE_NOMATCH)
{
n1 = get_tv_number(typ1);
n2 = get_tv_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; /* avoid gcc warning */
}
}
else
{
s1 = get_tv_string_buf(typ1, buf1);
s2 = get_tv_string_buf(typ2, buf2);
if (type != TYPE_MATCH && type != TYPE_NOMATCH)
i = ic ? MB_STRICMP(s1, s2) : STRCMP(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(s2, s1, ic);
if (type == TYPE_NOMATCH)
n1 = !n1;
break;
case TYPE_UNKNOWN: break; /* avoid gcc warning */
}
}
clear_tv(typ1);
clear_tv(typ2);
typ1->v_type = VAR_NUMBER;
typ1->vval.v_number = n1;
}
return OK;
}
int
typval_copy(typ1, typ2)
typval_T *typ1;
typval_T *typ2;
{
if (typ2 == NULL)
rettv_list_alloc(typ2);
if (typ1 != NULL && typ2 != NULL)
return item_copy(typ1, typ2, TRUE, 0);
return FAIL;
}
char_u *
typval_tostring(arg)
typval_T *arg;
{
char_u *tofree;
char_u numbuf[NUMBUFLEN];
char_u *ret = NULL;
if (arg == NULL)
return vim_strsave((char_u *)"(does not exist)");
ret = tv2string(arg, &tofree, numbuf, 0);
/* Make a copy if we have a value but it's not in allocated memory. */
if (ret != NULL && tofree == NULL)
ret = vim_strsave(ret);
return ret;
}
int
var_exists(char_u *var)
{
char_u *name;
char_u *tofree;
typval_T tv;
int len = 0;
int n = FALSE;
/* get_name_len() takes care of expanding curly braces */
name = var;
len = get_name_len(&var, &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(&var, &tv, TRUE, FALSE) == OK);
if (n)
clear_tv(&tv);
}
}
if (*var != NUL)
n = FALSE;
vim_free(tofree);
return n;
}
#endif /* FEAT_EVAL */

View File

@@ -2991,9 +2991,7 @@ f_exepath(typval_T *argvars, typval_T *rettv)
f_exists(typval_T *argvars, typval_T *rettv)
{
char_u *p;
char_u *name;
int n = FALSE;
int len = 0;
p = get_tv_string(&argvars[0]);
if (*p == '$') /* environment variable */
@@ -3035,29 +3033,7 @@ f_exists(typval_T *argvars, typval_T *rettv)
}
else /* internal variable */
{
char_u *tofree;
typval_T tv;
/* get_name_len() takes care of expanding curly braces */
name = p;
len = get_name_len(&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)
clear_tv(&tv);
}
}
if (*p != NUL)
n = FALSE;
vim_free(tofree);
n = var_exists(p);
}
rettv->vval.v_number = n;

View File

@@ -73,6 +73,16 @@ static void do_setdebugtracelevel(char_u *arg);
static void do_checkbacktracelevel(void);
static void do_showbacktrace(char_u *cmd);
static char_u *debug_oldval = NULL; /* old and newval for debug expressions */
static char_u *debug_newval = NULL;
static int debug_expr = 0; /* use debug_expr */
int
has_watchexpr(void)
{
return debug_expr;
}
/*
* do_debug(): Debug mode.
* Repeatedly get Ex commands, until told to continue normal execution.
@@ -135,13 +145,24 @@ do_debug(char_u *cmd)
if (!debug_did_msg)
MSG(_("Entering Debug mode. Type \"cont\" to continue."));
if (debug_oldval != NULL)
{
smsg((char_u *)_("Oldval = \"%s\""), debug_oldval);
vim_free(debug_oldval);
debug_oldval = NULL;
}
if (debug_newval != NULL)
{
smsg((char_u *)_("Newval = \"%s\""), debug_newval);
vim_free(debug_newval);
debug_newval = NULL;
}
if (sourcing_name != NULL)
msg(sourcing_name);
if (sourcing_lnum != 0)
smsg((char_u *)_("line %ld: %s"), (long)sourcing_lnum, cmd);
else
smsg((char_u *)_("cmd: %s"), cmd);
/*
* Repeat getting a command and executing it.
*/
@@ -528,11 +549,15 @@ dbg_check_skipped(exarg_T *eap)
struct debuggy
{
int dbg_nr; /* breakpoint number */
int dbg_type; /* DBG_FUNC or DBG_FILE */
char_u *dbg_name; /* function or file name */
int dbg_type; /* DBG_FUNC, DBG_FILE or DBG_EXPR */
char_u *dbg_name; /* function, expression or file name */
regprog_T *dbg_prog; /* regexp program */
linenr_T dbg_lnum; /* line number in function or file */
int dbg_forceit; /* ! used */
#ifdef FEAT_EVAL
typval_T *dbg_val; /* last result of watchexpression */
#endif
int dbg_level; /* stored nested level for expr */
};
static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL};
@@ -546,6 +571,7 @@ static garray_T prof_ga = {0, 0, sizeof(struct debuggy), 4, NULL};
#endif
#define DBG_FUNC 1
#define DBG_FILE 2
#define DBG_EXPR 3
static int dbg_parsearg(char_u *arg, garray_T *gap);
static linenr_T debuggy_find(int file,char_u *fname, linenr_T after, garray_T *gap, int *fp);
@@ -589,6 +615,12 @@ dbg_parsearg(
bp->dbg_type = DBG_FILE;
here = TRUE;
}
else if (
#ifdef FEAT_PROFILE
gap != &prof_ga &&
#endif
STRNCMP(p, "expr", 4) == 0)
bp->dbg_type = DBG_EXPR;
else
{
EMSG2(_(e_invarg2), p);
@@ -624,6 +656,12 @@ dbg_parsearg(
bp->dbg_name = vim_strsave(p);
else if (here)
bp->dbg_name = vim_strsave(curbuf->b_ffname);
else if (bp->dbg_type == DBG_EXPR)
{
bp->dbg_name = vim_strsave(p);
if (bp->dbg_name != NULL)
bp->dbg_val = eval_expr(bp->dbg_name, NULL);
}
else
{
/* Expand the file name in the same way as do_source(). This means
@@ -671,26 +709,35 @@ ex_breakadd(exarg_T *eap)
bp = &DEBUGGY(gap, gap->ga_len);
bp->dbg_forceit = eap->forceit;
pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE);
if (pat != NULL)
if (bp->dbg_type != DBG_EXPR)
{
bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
vim_free(pat);
pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE);
if (pat != NULL)
{
bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
vim_free(pat);
}
if (pat == NULL || bp->dbg_prog == NULL)
vim_free(bp->dbg_name);
else
{
if (bp->dbg_lnum == 0) /* default line number is 1 */
bp->dbg_lnum = 1;
#ifdef FEAT_PROFILE
if (eap->cmdidx != CMD_profile)
#endif
{
DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
++debug_tick;
}
++gap->ga_len;
}
}
if (pat == NULL || bp->dbg_prog == NULL)
vim_free(bp->dbg_name);
else
{
if (bp->dbg_lnum == 0) /* default line number is 1 */
bp->dbg_lnum = 1;
#ifdef FEAT_PROFILE
if (eap->cmdidx != CMD_profile)
#endif
{
DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
++debug_tick;
}
++gap->ga_len;
/* DBG_EXPR */
DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp;
++debug_tick;
}
}
}
@@ -750,7 +797,7 @@ ex_breakdel(exarg_T *eap)
}
else
{
/* ":breakdel {func|file} [lnum] {name}" */
/* ":breakdel {func|file|expr} [lnum] {name}" */
if (dbg_parsearg(eap->arg, gap) == FAIL)
return;
bp = &DEBUGGY(gap, gap->ga_len);
@@ -778,6 +825,11 @@ ex_breakdel(exarg_T *eap)
while (gap->ga_len > 0)
{
vim_free(DEBUGGY(gap, todel).dbg_name);
#ifdef FEAT_EVAL
if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR
&& DEBUGGY(gap, todel).dbg_val != NULL)
free_tv(DEBUGGY(gap, todel).dbg_val);
#endif
vim_regfree(DEBUGGY(gap, todel).dbg_prog);
--gap->ga_len;
if (todel < gap->ga_len)
@@ -814,11 +866,15 @@ ex_breaklist(exarg_T *eap UNUSED)
bp = &BREAKP(i);
if (bp->dbg_type == DBG_FILE)
home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, TRUE);
smsg((char_u *)_("%3d %s %s line %ld"),
if (bp->dbg_type != DBG_EXPR)
smsg((char_u *)_("%3d %s %s line %ld"),
bp->dbg_nr,
bp->dbg_type == DBG_FUNC ? "func" : "file",
bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
(long)bp->dbg_lnum);
else
smsg((char_u *)_("%3d expr %s"),
bp->dbg_nr, bp->dbg_name);
}
}
@@ -889,7 +945,8 @@ debuggy_find(
/* Skip entries that are not useful or are for a line that is beyond
* an already found breakpoint. */
bp = &DEBUGGY(gap, i);
if (((bp->dbg_type == DBG_FILE) == file && (
if (((bp->dbg_type == DBG_FILE) == file &&
bp->dbg_type != DBG_EXPR && (
#ifdef FEAT_PROFILE
gap == &prof_ga ||
#endif
@@ -910,6 +967,66 @@ debuggy_find(
}
got_int |= prev_got_int;
}
#ifdef FEAT_EVAL
else if (bp->dbg_type == DBG_EXPR)
{
typval_T *tv;
int line = FALSE;
prev_got_int = got_int;
got_int = FALSE;
tv = eval_expr(bp->dbg_name, NULL);
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)
{
typval_T *v;
line = TRUE;
debug_oldval = typval_tostring(bp->dbg_val);
v = eval_expr(bp->dbg_name, NULL);
debug_newval = typval_tostring(v);
free_tv(bp->dbg_val);
bp->dbg_val = v;
}
}
free_tv(tv);
}
}
else if (bp->dbg_val != NULL)
{
debug_oldval = typval_tostring(bp->dbg_val);
debug_newval = typval_tostring(NULL);
free_tv(bp->dbg_val);
bp->dbg_val = NULL;
line = TRUE;
}
if (line)
{
lnum = after > 0 ? after : 1;
break;
}
got_int |= prev_got_int;
}
#endif
}
if (name != fname)
vim_free(name);

View File

@@ -1174,6 +1174,13 @@ do_cmdline(
}
}
/* Check for the next breakpoint after a watchexpression */
if (breakpoint != NULL && has_watchexpr())
{
*breakpoint = dbg_find_breakpoint(FALSE, fname, sourcing_lnum);
*dbg_tick = debug_tick;
}
/*
* When not inside any ":while" loop, clear remembered lines.
*/

View File

@@ -64,7 +64,7 @@ void set_vim_var_nr(int idx, varnumber_T val);
varnumber_T get_vim_var_nr(int idx);
char_u *get_vim_var_str(int idx);
list_T *get_vim_var_list(int idx);
dict_T * get_vim_var_dict(int idx);
dict_T *get_vim_var_dict(int idx);
void set_vim_var_char(int c);
void set_vcount(long count, long count1, int set_prevcount);
void set_vim_var_string(int idx, char_u *val, int len);
@@ -129,6 +129,10 @@ void assert_report(typval_T *argvars);
void assert_exception(typval_T *argvars);
void assert_fails(typval_T *argvars);
void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, typval_T *exp_tv, typval_T *got_tv, assert_type_T atype);
int typval_compare(typval_T *typ1, typval_T *typ2, exptype_T type, int type_is, int ic, int evaluate);
int typval_copy(typval_T *typ1, typval_T *typ2);
char_u *typval_tostring(typval_T *arg);
int var_exists(char_u *var);
int modify_fname(char_u *src, int *usedlen, char_u **fnamep, char_u **bufp, int *fnamelen);
char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, typval_T *expr, char_u *flags);
void filter_map(typval_T *argvars, typval_T *rettv, int map);

View File

@@ -1,4 +1,5 @@
/* ex_cmds2.c */
int has_watchexpr (void);
void do_debug(char_u *cmd);
void ex_debug(exarg_T *eap);
void dbg_check_breakpoint(exarg_T *eap);

View File

@@ -3262,6 +3262,22 @@ typedef struct {
char_u buffer[64];
} context_sha256_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;
/*
* Structure used for reading in json_decode().
*/

View File

@@ -3085,6 +3085,8 @@ ex_call(exarg_T *eap)
failed = TRUE;
break;
}
if (has_watchexpr())
dbg_check_breakpoint(eap);
/* Handle a function returning a Funcref, Dictionary or List. */
if (handle_subscript(&arg, &rettv, !eap->skip, TRUE) == FAIL)

View File

@@ -771,6 +771,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1505,
/**/
1504,
/**/