Okay, I have updated the patch. It was harder than expected, since several of the last patches change something, this patch also touches. (debugger enhancement, type comparisons, __ARGS removal).
Changes include: * fix rejects caused by previous patches * fix an error, that would cause to not break on expressions, although it should Hopefully this is useful enough, that it can be merged for the upcoming 7.5 release. I know Charles has used this patch before successfully. Best, Christian -- Eigentümlichkeit ruft Eigentümlichkeit hervor. -- Goethe, Maximen und Reflektionen, Nr. 59 -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_dev+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -541,6 +541,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 {string} + Sets a breakpoint, that will break whenever the {string} + evaluates to true. 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 @@ -3540,6 +3540,8 @@ ex_call(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) @@ -4105,22 +4107,6 @@ get_user_var_name(xp, idx) #endif /* FEAT_CMDL_COMPL */ /* - * 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. @@ -4411,12 +4397,7 @@ eval4(arg, rettv, evaluate) exptype_T type = TYPE_UNKNOWN; int type_is = FALSE; /* TRUE for "is" and "isnot" */ int len = 2; - long n1, n2; - char_u *s1, *s2; - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; - regmatch_T regmatch; int ic; - char_u *save_cpo; /* * Get the first variable. @@ -4498,203 +4479,8 @@ eval4(arg, rettv, evaluate) 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) - { - if (rettv->v_type != var2.v_type - || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) - { - if (rettv->v_type != var2.v_type) - EMSG(_("E693: Can only compare Funcref with Funcref")); - else - EMSG(_("E694: Invalid operation for Funcrefs")); - clear_tv(rettv); - clear_tv(&var2); - return FAIL; - } - else - { - /* Compare two Funcrefs for being equal or unequal. */ - if (rettv->vval.v_string == NULL - || var2.vval.v_string == NULL) - n1 = FALSE; - else - n1 = STRCMP(rettv->vval.v_string, - var2.vval.v_string) == 0; - 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: - /* avoid 'l' flag in 'cpoptions' */ - save_cpo = p_cpo; - p_cpo = (char_u *)""; - regmatch.regprog = vim_regcomp(s2, - RE_MAGIC + RE_STRING); - regmatch.rm_ic = ic; - if (regmatch.regprog != NULL) - { - n1 = vim_regexec_nl(®match, s1, (colnr_T)0); - vim_regfree(regmatch.regprog); - if (type == TYPE_NOMATCH) - n1 = !n1; - } - p_cpo = save_cpo; - 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; } @@ -10682,9 +10468,7 @@ f_exists(argvars, rettv) 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 */ @@ -10725,13 +10509,23 @@ f_exists(argvars, rettv) #endif } else /* internal variable */ - { - char_u *tofree; + n = var_exists(p); + + rettv->vval.v_number = n; +} + +int +var_exists(var) + char_u *var; +{ + char_u *tofree, *name; typval_T tv; + int n = FALSE; + int len = 0; /* get_name_len() takes care of expanding curly braces */ - name = p; - len = get_name_len(&p, &tofree, TRUE, FALSE); + name = var; + len = get_name_len(&var, &tofree, TRUE, FALSE); if (len > 0) { if (tofree != NULL) @@ -10740,18 +10534,17 @@ f_exists(argvars, rettv) if (n) { /* handle d.key, l[idx], f(expr) */ - n = (handle_subscript(&p, &tv, TRUE, FALSE) == OK); + n = (handle_subscript(&var, &tv, TRUE, FALSE) == OK); if (n) clear_tv(&tv); } } - if (*p != NUL) + if (*var != NUL) n = FALSE; vim_free(tofree); - } - - rettv->vval.v_number = n; + + return n; } #ifdef FEAT_FLOAT @@ -25489,6 +25282,252 @@ reset_v_option_vars() } +int +typval_compare(typ1, typ2, type, type_is, ic, evaluate) + 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; + long n1, n2; + char_u *s1, *s2; + char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; + regmatch_T regmatch; + char_u *save_cpo; + + 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) + { + if (typ1->v_type != typ2->v_type + || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) + { + if (typ1->v_type != typ2->v_type) + EMSG(_("E693: Can only compare Funcref with Funcref")); + else + EMSG(_("E694: Invalid operation for Funcrefs")); + clear_tv(typ1); + clear_tv(typ2); + return FAIL; + } + else + { + /* Compare two Funcrefs for being equal or unequal. */ + if (typ1->vval.v_string == NULL + || typ2->vval.v_string == NULL) + n1 = FALSE; + else + n1 = STRCMP(typ1->vval.v_string, + typ2->vval.v_string) == 0; + 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: + /* avoid 'l' flag in 'cpoptions' */ + save_cpo = p_cpo; + p_cpo = (char_u *)""; + regmatch.regprog = vim_regcomp(s2, + RE_MAGIC + RE_STRING); + regmatch.rm_ic = ic; + if (regmatch.regprog != NULL) + { + n1 = vim_regexec_nl(®match, s1, (colnr_T)0); + vim_regfree(regmatch.regprog); + if (type == TYPE_NOMATCH) + n1 = !n1; + } + p_cpo = save_cpo; + 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; +} + + #endif /* FEAT_EVAL */ 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() +{ + return debug_expr; +} + /* * do_debug(): Debug mode. * Repeatedly get Ex commands, until told to continue normal execution. @@ -140,13 +150,24 @@ do_debug(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. */ @@ -543,11 +564,15 @@ dbg_check_skipped(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; /* watchexpression used */ +#endif + int dbg_level; /* stored nested level for expr */ }; static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL}; @@ -561,6 +586,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 __ARGS((char_u *arg, garray_T *gap)); static linenr_T debuggy_find __ARGS((int file,char_u *fname, linenr_T after, garray_T *gap, int *fp)); @@ -604,6 +630,12 @@ dbg_parsearg(arg, gap) 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); @@ -639,6 +671,17 @@ dbg_parsearg(arg, gap) bp->dbg_name = vim_strsave(p); else if (here) bp->dbg_name = vim_strsave(curbuf->b_ffname); +#ifdef FEAT_EVAL + else if (bp->dbg_type == DBG_EXPR) + { + bp->dbg_name = vim_strsave(p); + debug_expr++; + if (var_exists(bp->dbg_name) == OK) + bp->dbg_val = eval_expr(p, NULL); + else + bp->dbg_val = NULL; + } +#endif else { /* Expand the file name in the same way as do_source(). This means @@ -687,26 +730,35 @@ ex_breakadd(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; } } } @@ -768,7 +820,7 @@ ex_breakdel(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); @@ -796,6 +848,11 @@ ex_breakdel(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) @@ -833,11 +890,15 @@ ex_breaklist(eap) 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); } } @@ -908,7 +969,8 @@ debuggy_find(file, fname, after, gap, fp /* 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 @@ -929,6 +991,62 @@ debuggy_find(file, fname, after, gap, fp } got_int |= prev_got_int; } +#ifdef FEAT_EVAL + else if (bp->dbg_type == DBG_EXPR) + { + typval_T val3; + typval_T *tv; + int line = FALSE; + + prev_got_int = got_int; + got_int = FALSE; + + tv = eval_expr(bp->dbg_name, NULL); + + if (var_exists(bp->dbg_name) == OK || 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 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 @@ -1238,6 +1238,13 @@ do_cmdline(cmdline, fgetline, cookie, fl } } + /* 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 @@ -141,4 +141,8 @@ void ex_oldfiles(exarg_T *eap); void reset_v_option_vars(void); 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, char_u *flags); +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); +int var_exists(char_u *var); +char_u * typval_tostring __ARGS((typval_T *arg)); /* vim: set ft=c : */ 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 @@ -2685,6 +2685,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(). */ typedef struct