Browse Source

vim-patch:8.0.1505: debugger can't break on a condition

Problem:    Debugger can't break on a condition. (Charles Campbell)
Solution:   Add ":breakadd expr". (Christian Brabandt, closes vim/vim#859)
c6f9f739d3

Do not port "has_watchexpr()" to avoid dead code.
"has_watchexpr()" always returns 0 because "debug_expr" is always 0.

Restore "eval_expr()" as a wrapper to allocate "typval_T" for "eval0()".
Remove it in later patches.

Include "typval_compare()" changes from patch v8.1.0958,
partially ported in 8b60368c1b.

Close https://github.com/neovim/neovim/pull/12373

N/A patches for version.c:

vim-patch:8.2.2720: GTK menu tooltip moves the cursor

Problem:    GTK menu tooltip moves the cursor.
Solution:   Position the cursor after displaying the tooltip.  Do not show the
            tooltip when editing the command line.
01ac0a1f66
pull/14314/head
Jan Edmund Lazo 1 week ago
parent
commit
1a1fe58f7e
No known key found for this signature in database GPG Key ID: 64915E6E9F735B15
5 changed files with 362 additions and 238 deletions
  1. +13
    -0
      runtime/doc/repeat.txt
  2. +234
    -190
      src/nvim/eval.c
  3. +13
    -0
      src/nvim/eval.h
  4. +1
    -24
      src/nvim/eval/funcs.c
  5. +101
    -24
      src/nvim/ex_cmds2.c

+ 13
- 0
runtime/doc/repeat.txt View File

@ -804,6 +804,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.


+ 234
- 190
src/nvim/eval.c View File

@ -916,6 +916,17 @@ varnumber_T eval_to_number(char_u *expr)
return retval;
}
// Top level evaluation function.
// Returns an allocated typval_T with the result.
// Returns NULL when there is an error.
typval_T *eval_expr(char_u *arg)
{
typval_T *tv = xmalloc(sizeof(*tv));
if (eval0(arg, tv, NULL, true) == FAIL) {
XFREE_CLEAR(tv);
}
return tv;
}
/*
* Prepare v: variable "idx" to be used.
@ -3129,21 +3140,6 @@ static int pattern_match(char_u *pat, char_u *text, bool 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;
// TODO(ZyX-I): move to eval/expressions
/*
@ -3420,11 +3416,9 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
{
typval_T var2;
char_u *p;
int i;
exptype_T type = TYPE_UNKNOWN;
bool type_is = false; // true for "is" and "isnot"
int len = 2;
varnumber_T n1, n2;
bool ic;
/*
@ -3491,173 +3485,7 @@ static int eval4(char_u **arg, typval_T *rettv, int 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"));
}
tv_clear(rettv);
tv_clear(&var2);
return FAIL;
} else {
// Compare two Lists for being equal or unequal.
n1 = tv_list_equal(rettv->vval.v_list, var2.vval.v_list, ic, false);
if (type == TYPE_NEQUAL) {
n1 = !n1;
}
}
} else if (rettv->v_type == VAR_DICT || var2.v_type == VAR_DICT) {
if (type_is) {
n1 = (rettv->v_type == var2.v_type
&& rettv->vval.v_dict == var2.vval.v_dict);
if (type == TYPE_NEQUAL)
n1 = !n1;
} else if (rettv->v_type != var2.v_type
|| (type != TYPE_EQUAL && type != TYPE_NEQUAL)) {
if (rettv->v_type != var2.v_type)
EMSG(_("E735: Can only compare Dictionary with Dictionary"));
else
EMSG(_("E736: Invalid operation for Dictionary"));
tv_clear(rettv);
tv_clear(&var2);
return FAIL;
} else {
// Compare two Dictionaries for being equal or unequal.
n1 = tv_dict_equal(rettv->vval.v_dict, var2.vval.v_dict,
ic, false);
if (type == TYPE_NEQUAL) {
n1 = !n1;
}
}
} else if (tv_is_func(*rettv) || tv_is_func(var2)) {
if (type != TYPE_EQUAL && type != TYPE_NEQUAL) {
EMSG(_("E694: Invalid operation for Funcrefs"));
tv_clear(rettv);
tv_clear(&var2);
return FAIL;
}
if ((rettv->v_type == VAR_PARTIAL
&& rettv->vval.v_partial == NULL)
|| (var2.v_type == VAR_PARTIAL
&& var2.vval.v_partial == NULL)) {
// when a partial is NULL assume not equal
n1 = false;
} else if (type_is) {
if (rettv->v_type == VAR_FUNC && var2.v_type == VAR_FUNC) {
// strings are considered the same if their value is
// the same
n1 = tv_equal(rettv, &var2, ic, false);
} else if (rettv->v_type == VAR_PARTIAL
&& var2.v_type == VAR_PARTIAL) {
n1 = (rettv->vval.v_partial == var2.vval.v_partial);
} else {
n1 = false;
}
} else {
n1 = tv_equal(rettv, &var2, ic, false);
}
if (type == TYPE_NEQUAL) {
n1 = !n1;
}
}
/*
* If one of the two variables is a float, compare as a float.
* When using "=~" or "!~", always compare as string.
*/
else if ((rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT)
&& type != TYPE_MATCH && type != TYPE_NOMATCH) {
float_T f1, f2;
if (rettv->v_type == VAR_FLOAT) {
f1 = rettv->vval.v_float;
} else {
f1 = tv_get_number(rettv);
}
if (var2.v_type == VAR_FLOAT) {
f2 = var2.vval.v_float;
} else {
f2 = tv_get_number(&var2);
}
n1 = false;
switch (type) {
case TYPE_EQUAL: n1 = (f1 == f2); break;
case TYPE_NEQUAL: n1 = (f1 != f2); break;
case TYPE_GREATER: n1 = (f1 > f2); break;
case TYPE_GEQUAL: n1 = (f1 >= f2); break;
case TYPE_SMALLER: n1 = (f1 < f2); break;
case TYPE_SEQUAL: n1 = (f1 <= f2); break;
case TYPE_UNKNOWN:
case TYPE_MATCH:
case TYPE_NOMATCH: break;
}
}
/*
* If one of the two variables is a number, compare as a number.
* When using "=~" or "!~", always compare as string.
*/
else if ((rettv->v_type == VAR_NUMBER || var2.v_type == VAR_NUMBER)
&& type != TYPE_MATCH && type != TYPE_NOMATCH) {
n1 = tv_get_number(rettv);
n2 = tv_get_number(&var2);
switch (type) {
case TYPE_EQUAL: n1 = (n1 == n2); break;
case TYPE_NEQUAL: n1 = (n1 != n2); break;
case TYPE_GREATER: n1 = (n1 > n2); break;
case TYPE_GEQUAL: n1 = (n1 >= n2); break;
case TYPE_SMALLER: n1 = (n1 < n2); break;
case TYPE_SEQUAL: n1 = (n1 <= n2); break;
case TYPE_UNKNOWN:
case TYPE_MATCH:
case TYPE_NOMATCH: break;
}
} else {
char buf1[NUMBUFLEN];
char buf2[NUMBUFLEN];
const char *const s1 = tv_get_string_buf(rettv, buf1);
const char *const s2 = tv_get_string_buf(&var2, buf2);
if (type != TYPE_MATCH && type != TYPE_NOMATCH) {
i = mb_strcmp_ic(ic, s1, s2);
} else {
i = 0;
}
n1 = false;
switch (type) {
case TYPE_EQUAL: n1 = (i == 0); break;
case TYPE_NEQUAL: n1 = (i != 0); break;
case TYPE_GREATER: n1 = (i > 0); break;
case TYPE_GEQUAL: n1 = (i >= 0); break;
case TYPE_SMALLER: n1 = (i < 0); break;
case TYPE_SEQUAL: n1 = (i <= 0); break;
case TYPE_MATCH:
case TYPE_NOMATCH: {
n1 = pattern_match((char_u *)s2, (char_u *)s1, ic);
if (type == TYPE_NOMATCH) {
n1 = !n1;
}
break;
}
case TYPE_UNKNOWN: break; // Avoid gcc warning.
}
}
tv_clear(rettv);
tv_clear(&var2);
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = n1;
}
return typval_compare(rettv, &var2, type, type_is, ic, evaluate);
}
return OK;
@ -8000,8 +7828,8 @@ int get_id_len(const char **const arg)
*/
int get_name_len(const char **const arg,
char **alias,
int evaluate,
int verbose)
bool evaluate,
bool verbose)
{
int len;
@ -8486,10 +8314,8 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg)
return oldval;
}
/*
* Get the value of internal variable "name".
* Return OK or FAIL.
*/
// Get the value of internal variable "name".
// Return OK or FAIL. If OK is returned "rettv" must be cleared.
int get_var_tv(
const char *name,
int len, // length of "name"
@ -10746,3 +10572,221 @@ bool invoke_prompt_interrupt(void)
tv_clear(&rettv);
return true;
}
int typval_compare(
typval_T *typ1, // first operand
typval_T *typ2, // second operand
exptype_T type, // operator
bool type_is, // true for "is" and "isnot"
bool ic, // ignore case
bool evaluate
)
FUNC_ATTR_NONNULL_ALL
{
varnumber_T n1, n2;
if (evaluate) {
if (type_is && typ1->v_type != typ2->v_type) {
// For "is" a different type always means false, for "notis"
// it means true.
n1 = type == TYPE_NEQUAL;
} else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST) {
if (type_is) {
n1 = typ1->v_type == typ2->v_type
&& typ1->vval.v_list == typ2->vval.v_list;
if (type == TYPE_NEQUAL) {
n1 = !n1;
}
} else if (typ1->v_type != typ2->v_type
|| (type != TYPE_EQUAL && type != TYPE_NEQUAL)) {
if (typ1->v_type != typ2->v_type) {
EMSG(_("E691: Can only compare List with List"));
} else {
EMSG(_("E692: Invalid operation for List"));
}
tv_clear(typ1);
tv_clear(typ2);
return FAIL;
} else {
// Compare two Lists for being equal or unequal.
n1 = tv_list_equal(typ1->vval.v_list, typ2->vval.v_list, ic, false);
if (type == TYPE_NEQUAL) {
n1 = !n1;
}
}
} else if (typ1->v_type == VAR_DICT || typ2->v_type == VAR_DICT) {
if (type_is) {
n1 = typ1->v_type == typ2->v_type
&& typ1->vval.v_dict == typ2->vval.v_dict;
if (type == TYPE_NEQUAL) {
n1 = !n1;
}
} else if (typ1->v_type != typ2->v_type
|| (type != TYPE_EQUAL && type != TYPE_NEQUAL)) {
if (typ1->v_type != typ2->v_type) {
EMSG(_("E735: Can only compare Dictionary with Dictionary"));
} else {
EMSG(_("E736: Invalid operation for Dictionary"));
}
tv_clear(typ1);
tv_clear(typ2);
return FAIL;
} else {
// Compare two Dictionaries for being equal or unequal.
n1 = tv_dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, ic, false);
if (type == TYPE_NEQUAL) {
n1 = !n1;
}
}
} else if (tv_is_func(*typ1) || tv_is_func(*typ2)) {
if (type != TYPE_EQUAL && type != TYPE_NEQUAL) {
EMSG(_("E694: Invalid operation for Funcrefs"));
tv_clear(typ1);
tv_clear(typ2);
return FAIL;
}
if ((typ1->v_type == VAR_PARTIAL && typ1->vval.v_partial == NULL)
|| (typ2->v_type == VAR_PARTIAL && typ2->vval.v_partial == NULL)) {
// when a partial is NULL assume not equal
n1 = false;
} else if (type_is) {
if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC) {
// strings are considered the same if their value is
// the same
n1 = tv_equal(typ1, typ2, ic, false);
} else if (typ1->v_type == VAR_PARTIAL
&& typ2->v_type == VAR_PARTIAL) {
n1 = typ1->vval.v_partial == typ2->vval.v_partial;
} else {
n1 = false;
}
} else {
n1 = tv_equal(typ1, typ2, ic, false);
}
if (type == TYPE_NEQUAL) {
n1 = !n1;
}
} else if ((typ1->v_type == VAR_FLOAT || typ2->v_type == VAR_FLOAT)
&& type != TYPE_MATCH && type != TYPE_NOMATCH) {
// If one of the two variables is a float, compare as a float.
// When using "=~" or "!~", always compare as string.
const float_T f1 = tv_get_float(typ1);
const float_T f2 = tv_get_float(typ2);
n1 = false;
switch (type) {
case TYPE_EQUAL: n1 = f1 == f2; break;
case TYPE_NEQUAL: n1 = f1 != f2; break;
case TYPE_GREATER: n1 = f1 > f2; break;
case TYPE_GEQUAL: n1 = f1 >= f2; break;
case TYPE_SMALLER: n1 = f1 < f2; break;
case TYPE_SEQUAL: n1 = f1 <= f2; break;
case TYPE_UNKNOWN:
case TYPE_MATCH:
case TYPE_NOMATCH: break;
}
} else if ((typ1->v_type == VAR_NUMBER || typ2->v_type == VAR_NUMBER)
&& type != TYPE_MATCH && type != TYPE_NOMATCH) {
// If one of the two variables is a number, compare as a number.
// When using "=~" or "!~", always compare as string.
n1 = tv_get_number(typ1);
n2 = tv_get_number(typ2);
switch (type) {
case TYPE_EQUAL: n1 = n1 == n2; break;
case TYPE_NEQUAL: n1 = n1 != n2; break;
case TYPE_GREATER: n1 = n1 > n2; break;
case TYPE_GEQUAL: n1 = n1 >= n2; break;
case TYPE_SMALLER: n1 = n1 < n2; break;
case TYPE_SEQUAL: n1 = n1 <= n2; break;
case TYPE_UNKNOWN:
case TYPE_MATCH:
case TYPE_NOMATCH: break;
}
} else {
char buf1[NUMBUFLEN];
char buf2[NUMBUFLEN];
const char *const s1 = tv_get_string_buf(typ1, buf1);
const char *const s2 = tv_get_string_buf(typ2, buf2);
int i;
if (type != TYPE_MATCH && type != TYPE_NOMATCH) {
i = mb_strcmp_ic(ic, s1, s2);
} else {
i = 0;
}
n1 = false;
switch (type) {
case TYPE_EQUAL: n1 = i == 0; break;
case TYPE_NEQUAL: n1 = i != 0; break;
case TYPE_GREATER: n1 = i > 0; break;
case TYPE_GEQUAL: n1 = i >= 0; break;
case TYPE_SMALLER: n1 = i < 0; break;
case TYPE_SEQUAL: n1 = i <= 0; break;
case TYPE_MATCH:
case TYPE_NOMATCH:
n1 = pattern_match((char_u *)s2, (char_u *)s1, ic);
if (type == TYPE_NOMATCH) {
n1 = !n1;
}
break;
case TYPE_UNKNOWN: break; // Avoid gcc warning.
}
}
tv_clear(typ1);
tv_clear(typ2);
typ1->v_type = VAR_NUMBER;
typ1->vval.v_number = n1;
}
return OK;
}
int typval_copy(typval_T *typ1, typval_T *typ2)
{
if (typ2 == NULL) {
tv_list_alloc_ret(typ2, kListLenUnknown);
}
if (typ1 != NULL && typ2 != NULL) {
return var_item_copy(NULL, typ1, typ2, true, 0);
}
return FAIL;
}
char *typval_tostring(typval_T *arg)
{
if (arg == NULL) {
return xstrdup("(does not exist)");
}
return encode_tv2string(arg, NULL);
}
bool var_exists(const char *var)
FUNC_ATTR_NONNULL_ALL
{
char *tofree;
bool n = false;
// get_name_len() takes care of expanding curly braces
const char *name = var;
const int len = get_name_len((const char **)&var, &tofree, true, false);
if (len > 0) {
typval_T tv;
if (tofree != NULL) {
name = tofree;
}
n = get_var_tv(name, len, &tv, NULL, false, true) == OK;
if (n) {
// Handle d.key, l[idx], f(expr).
n = handle_subscript(&var, &tv, true, false) == OK;
if (n) {
tv_clear(&tv);
}
}
}
if (*var != NUL) {
n = false;
}
xfree(tofree);
return n;
}

+ 13
- 0
src/nvim/eval.h View File

@ -227,6 +227,19 @@ typedef enum
ASSERT_OTHER,
} assert_type_T;
/// types for expressions.
typedef enum {
TYPE_UNKNOWN = 0,
TYPE_EQUAL, ///< ==
TYPE_NEQUAL, ///< !=
TYPE_GREATER, ///< >
TYPE_GEQUAL, ///< >=
TYPE_SMALLER, ///< <
TYPE_SEQUAL, ///< <=
TYPE_MATCH, ///< =~
TYPE_NOMATCH, ///< !~
} exptype_T;
/// Type for dict_list function
typedef enum {
kDictListKeys, ///< List dictionary keys.


+ 1
- 24
src/nvim/eval/funcs.c View File

@ -2051,7 +2051,6 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int n = false;
int len = 0;
const char *p = tv_get_string(&argvars[0]);
if (*p == '$') { // Environment variable.
@ -2082,29 +2081,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
n = au_exists(p + 1);
}
} else { // Internal variable.
typval_T tv;
// get_name_len() takes care of expanding curly braces
const char *name = p;
char *tofree;
len = get_name_len((const char **)&p, &tofree, true, false);
if (len > 0) {
if (tofree != NULL) {
name = tofree;
}
n = (get_var_tv(name, len, &tv, NULL, false, true) == OK);
if (n) {
// Handle d.key, l[idx], f(expr).
n = (handle_subscript(&p, &tv, true, false) == OK);
if (n) {
tv_clear(&tv);
}
}
}
if (*p != NUL)
n = FALSE;
xfree(tofree);
n = var_exists(p);
}
rettv->vval.v_number = n;


+ 101
- 24
src/nvim/ex_cmds2.c View File

@ -120,6 +120,9 @@ struct source_cookie {
/// batch mode debugging: don't save and restore typeahead.
static bool debug_greedy = false;
static char *debug_oldval = NULL; // old and newval for debug expressions
static char *debug_newval = NULL;
/// Debug mode. Repeatedly get Ex commands, until told to continue normal
/// execution.
void do_debug(char_u *cmd)
@ -166,6 +169,16 @@ void do_debug(char_u *cmd)
if (!debug_did_msg) {
MSG(_("Entering Debug mode. Type \"cont\" to continue."));
}
if (debug_oldval != NULL) {
smsg(_("Oldval = \"%s\""), debug_oldval);
xfree(debug_oldval);
debug_oldval = NULL;
}
if (debug_newval != NULL) {
smsg(_("Newval = \"%s\""), debug_newval);
xfree(debug_newval);
debug_newval = NULL;
}
if (sourcing_name != NULL) {
msg(sourcing_name);
}
@ -174,7 +187,6 @@ void do_debug(char_u *cmd)
} else {
smsg(_("cmd: %s"), cmd);
}
// Repeat getting a command and executing it.
for (;; ) {
msg_scroll = true;
@ -514,11 +526,13 @@ bool dbg_check_skipped(exarg_T *eap)
/// This is a grow-array of structs.
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 or 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
typval_T *dbg_val; ///< last result of watchexpression
int dbg_level; ///< stored nested level for expr
};
static garray_T dbg_breakp = { 0, 0, sizeof(struct debuggy), 4, NULL };
@ -530,6 +544,7 @@ static int last_breakp = 0; // nr of last defined breakpoint
static garray_T prof_ga = { 0, 0, sizeof(struct debuggy), 4, NULL };
#define DBG_FUNC 1
#define DBG_FILE 2
#define DBG_EXPR 3
/// Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them
@ -562,6 +577,8 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
}
bp->dbg_type = DBG_FILE;
here = true;
} else if (gap != &prof_ga && STRNCMP(p, "expr", 4) == 0) {
bp->dbg_type = DBG_EXPR;
} else {
EMSG2(_(e_invarg2), p);
return FAIL;
@ -590,6 +607,9 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
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);
bp->dbg_val = eval_expr(bp->dbg_name);
} else {
// Expand the file name in the same way as do_source(). This means
// doing it twice, so that $DIR/file gets expanded when $DIR is
@ -621,7 +641,6 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
void ex_breakadd(exarg_T *eap)
{
struct debuggy *bp;
char_u *pat;
garray_T *gap;
gap = &dbg_breakp;
@ -633,22 +652,28 @@ void 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) {
bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
xfree(pat);
}
if (pat == NULL || bp->dbg_prog == NULL) {
xfree(bp->dbg_name);
} else {
if (bp->dbg_lnum == 0) { // default line number is 1
bp->dbg_lnum = 1;
if (bp->dbg_type != DBG_EXPR) {
char_u *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);
xfree(pat);
}
if (eap->cmdidx != CMD_profile) {
DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
debug_tick++;
if (pat == NULL || bp->dbg_prog == NULL) {
xfree(bp->dbg_name);
} else {
if (bp->dbg_lnum == 0) { // default line number is 1
bp->dbg_lnum = 1;
}
if (eap->cmdidx != CMD_profile) {
DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
debug_tick++;
}
gap->ga_len++;
}
gap->ga_len++;
} else {
// DBG_EXPR
DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp;
debug_tick++;
}
}
}
@ -691,7 +716,7 @@ void ex_breakdel(exarg_T *eap)
todel = 0;
del_all = true;
} else {
// ":breakdel {func|file} [lnum] {name}"
// ":breakdel {func|file|expr} [lnum] {name}"
if (dbg_parsearg(eap->arg, gap) == FAIL) {
return;
}
@ -716,6 +741,10 @@ void ex_breakdel(exarg_T *eap)
} else {
while (!GA_EMPTY(gap)) {
xfree(DEBUGGY(gap, todel).dbg_name);
if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR
&& DEBUGGY(gap, todel).dbg_val != NULL) {
tv_free(DEBUGGY(gap, todel).dbg_val);
}
vim_regfree(DEBUGGY(gap, todel).dbg_prog);
gap->ga_len--;
if (todel < gap->ga_len) {
@ -750,11 +779,15 @@ void ex_breaklist(exarg_T *eap)
if (bp->dbg_type == DBG_FILE) {
home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, true);
}
smsg(_("%3d %s %s line %" PRId64),
bp->dbg_nr,
bp->dbg_type == DBG_FUNC ? "func" : "file",
bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
(int64_t)bp->dbg_lnum);
if (bp->dbg_type != DBG_EXPR) {
smsg(_("%3d %s %s line %" PRId64),
bp->dbg_nr,
bp->dbg_type == DBG_FUNC ? "func" : "file",
bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
(int64_t)bp->dbg_lnum);
} else {
smsg(_("%3d expr %s"), bp->dbg_nr, bp->dbg_name);
}
}
}
}
@ -814,6 +847,7 @@ debuggy_find(
// an already found breakpoint.
bp = &DEBUGGY(gap, i);
if ((bp->dbg_type == DBG_FILE) == file
&& bp->dbg_type != DBG_EXPR
&& (gap == &prof_ga
|| (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum)))) {
// Save the value of got_int and reset it. We don't want a
@ -827,6 +861,49 @@ debuggy_find(
*fp = bp->dbg_forceit;
}
}
got_int |= prev_got_int;
} else if (bp->dbg_type == DBG_EXPR) {
bool line = false;
prev_got_int = got_int;
got_int = false;
typval_T *tv = eval_expr(bp->dbg_name);
if (tv != NULL) {
if (bp->dbg_val == NULL) {
debug_oldval = typval_tostring(NULL);
bp->dbg_val = tv;
debug_newval = typval_tostring(bp->dbg_val);
line = true;
} else {
typval_T val3;
if (typval_copy(bp->dbg_val, &val3) == OK) {
if (typval_compare(tv, &val3, TYPE_EQUAL, true, false, true) == OK
&& tv->vval.v_number == false) {
line = true;
debug_oldval = typval_tostring(bp->dbg_val);
typval_T *v = eval_expr(bp->dbg_name);
debug_newval = typval_tostring(v);
tv_free(bp->dbg_val);
bp->dbg_val = v;
}
}
tv_free(tv);
}
} else if (bp->dbg_val != NULL) {
debug_oldval = typval_tostring(bp->dbg_val);
debug_newval = typval_tostring(NULL);
tv_free(bp->dbg_val);
bp->dbg_val = NULL;
line = true;
}
if (line) {
lnum = after > 0 ? after : 1;
break;
}
got_int |= prev_got_int;
}
}


Loading…
Cancel
Save