# HG changeset patch # User Christian Brabandt # Date 1518372905 -3600 # Node ID 69278c25429dfac419d81896e57141833609c113 # Parent fa53b212be266dd897d16d5da64fdc0edac8cee0 patch 8.0.1505: debugger can't break on a condition commit https://github.com/vim/vim/commit/c6f9f739d32084923c3031cbf6f581f8c8bf7fd2 Author: Bram Moolenaar Date: Sun Feb 11 19:06:26 2018 +0100 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 https://github.com/vim/vim/issues/859) diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -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. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -3237,22 +3237,6 @@ pattern_match(char_u *pat, char_u *text, } /* - * 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 * VAR_UNKNOWN. The function still returns FAIL for a syntax error. @@ -3531,9 +3515,6 @@ eval4(char_u **arg, typval_T *rettv, int 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 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 */ diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -2991,9 +2991,7 @@ f_exepath(typval_T *argvars, typval_T *r 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 *re } 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; diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -73,6 +73,16 @@ static void do_setdebugtracelevel(char_u 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( #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); diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -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. */ diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -64,7 +64,7 @@ void set_vim_var_nr(int idx, varnumber_T 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); diff --git a/src/proto/ex_cmds2.pro b/src/proto/ex_cmds2.pro --- a/src/proto/ex_cmds2.pro +++ b/src/proto/ex_cmds2.pro @@ -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); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -3263,6 +3263,22 @@ typedef struct { } 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(). */ struct js_reader diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -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) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -772,6 +772,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1505, +/**/ 1504, /**/ 1503,