To: vim_dev@googlegroups.com Subject: Patch 8.0.1505 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.0.1505 Problem: Debugger can't break on a condition. (Charles Campbell) Solution: Add ":breakadd expr". (Christian Brabandt, closes #859) Files: runtime/doc/repeat.txt, src/eval.c, src/evalfunc.c, src/userfunc.c, src/ex_cmds2.c, src/ex_docmd.c, src/proto/eval.pro, src/proto/ex_cmds2.pro, src/structs.h *** ../vim-8.0.1504/runtime/doc/repeat.txt 2017-06-10 14:29:26.766871128 +0200 --- runtime/doc/repeat.txt 2018-02-11 18:28:19.817645767 +0100 *************** *** 802,807 **** --- 806,824 ---- < 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. *** ../vim-8.0.1504/src/eval.c 2018-02-10 18:45:21.044822330 +0100 --- src/eval.c 2018-02-11 18:44:34.598688909 +0100 *************** *** 3237,3258 **** } /* - * 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. --- 3237,3242 ---- *************** *** 3531,3539 **** 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; /* --- 3515,3520 ---- *************** *** 3615,3812 **** 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 OK; --- 3596,3602 ---- clear_tv(rettv); return FAIL; } ! return typval_compare(rettv, &var2, type, type_is, ic, evaluate); } return OK; *************** *** 6840,6846 **** /* * Get the value of internal variable "name". ! * Return OK or FAIL. */ int get_var_tv( --- 6630,6636 ---- /* * Get the value of internal variable "name". ! * Return OK or FAIL. If OK is returned "rettv" must be cleared. */ int get_var_tv( *************** *** 8419,8425 **** win_T * find_win_by_nr( typval_T *vp, ! tabpage_T *tp UNUSED) /* NULL for current tab page */ { win_T *wp; int nr; --- 8209,8215 ---- win_T * find_win_by_nr( typval_T *vp, ! tabpage_T *tp) /* NULL for current tab page */ { win_T *wp; int nr; *************** *** 9279,9284 **** --- 9069,9345 ---- } + 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 */ *** ../vim-8.0.1504/src/evalfunc.c 2018-02-11 14:29:45.372349161 +0100 --- src/evalfunc.c 2018-02-11 18:39:17.376950755 +0100 *************** *** 2991,2999 **** 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 */ --- 2991,2997 ---- *************** *** 3035,3063 **** } 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); } rettv->vval.v_number = n; --- 3033,3039 ---- } else /* internal variable */ { ! n = var_exists(p); } rettv->vval.v_number = n; *** ../vim-8.0.1504/src/userfunc.c 2018-02-10 18:45:21.096821957 +0100 --- src/userfunc.c 2018-02-11 18:30:01.808923584 +0100 *************** *** 3085,3090 **** --- 3085,3092 ---- 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) *** ../vim-8.0.1504/src/ex_cmds2.c 2018-02-10 18:45:21.052822273 +0100 --- src/ex_cmds2.c 2018-02-11 19:01:13.319595875 +0100 *************** *** 73,78 **** --- 73,88 ---- 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,147 **** if (!debug_did_msg) MSG(_("Entering Debug mode. Type \"cont\" to continue.")); 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. */ --- 145,168 ---- 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,538 **** struct debuggy { int dbg_nr; /* breakpoint number */ ! int dbg_type; /* DBG_FUNC or DBG_FILE */ ! char_u *dbg_name; /* function or file name */ regprog_T *dbg_prog; /* regexp program */ linenr_T dbg_lnum; /* line number in function or file */ int dbg_forceit; /* ! used */ }; static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL}; --- 549,563 ---- struct debuggy { int dbg_nr; /* breakpoint number */ ! 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,551 **** --- 571,577 ---- #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,594 **** --- 615,626 ---- 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,629 **** --- 656,667 ---- 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,696 **** 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) ! { ! 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; } } } --- 709,743 ---- bp = &DEBUGGY(gap, gap->ga_len); bp->dbg_forceit = eap->forceit; ! if (bp->dbg_type != DBG_EXPR) { ! 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; } ! } ! else ! { ! /* DBG_EXPR */ ! DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp; ! ++debug_tick; } } } *************** *** 750,756 **** } else { ! /* ":breakdel {func|file} [lnum] {name}" */ if (dbg_parsearg(eap->arg, gap) == FAIL) return; bp = &DEBUGGY(gap, gap->ga_len); --- 797,803 ---- } else { ! /* ":breakdel {func|file|expr} [lnum] {name}" */ if (dbg_parsearg(eap->arg, gap) == FAIL) return; bp = &DEBUGGY(gap, gap->ga_len); *************** *** 778,783 **** --- 825,835 ---- 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,824 **** 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"), bp->dbg_nr, bp->dbg_type == DBG_FUNC ? "func" : "file", bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff, (long)bp->dbg_lnum); } } --- 866,880 ---- bp = &BREAKP(i); if (bp->dbg_type == DBG_FILE) home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, TRUE); ! 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,895 **** /* 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 && ( #ifdef FEAT_PROFILE gap == &prof_ga || #endif --- 945,952 ---- /* 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 && ! bp->dbg_type != DBG_EXPR && ( #ifdef FEAT_PROFILE gap == &prof_ga || #endif *************** *** 910,915 **** --- 967,1032 ---- } 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); *** ../vim-8.0.1504/src/ex_docmd.c 2018-02-10 18:45:21.056822244 +0100 --- src/ex_docmd.c 2018-02-11 18:16:14.746787486 +0100 *************** *** 1174,1179 **** --- 1174,1186 ---- } } + /* 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. */ *** ../vim-8.0.1504/src/proto/eval.pro 2017-12-16 18:26:56.626992497 +0100 --- src/proto/eval.pro 2018-02-11 18:45:02.854487787 +0100 *************** *** 64,70 **** 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); 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); --- 64,70 ---- 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); 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,134 **** --- 129,138 ---- 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); *** ../vim-8.0.1504/src/proto/ex_cmds2.pro 2017-08-03 14:29:09.895896191 +0200 --- src/proto/ex_cmds2.pro 2018-02-11 18:16:14.746787486 +0100 *************** *** 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); *** ../vim-8.0.1504/src/structs.h 2018-02-10 18:15:00.754098808 +0100 --- src/structs.h 2018-02-11 18:16:14.746787486 +0100 *************** *** 3263,3268 **** --- 3263,3284 ---- } 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 *** ../vim-8.0.1504/src/version.c 2018-02-11 16:40:13.439759211 +0100 --- src/version.c 2018-02-11 19:05:22.425829110 +0100 *************** *** 773,774 **** --- 773,776 ---- { /* Add new patch number below this line */ + /**/ + 1505, /**/ -- ARTHUR: Be quiet! DENNIS: --but by a two-thirds majority in the case of more-- ARTHUR: Be quiet! I order you to be quiet! WOMAN: Order, eh -- who does he think he is? ARTHUR: I am your king! The Quest for the Holy Grail (Monty Python) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///