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
This commit is contained in:
Jan Edmund Lazo
2021-04-06 18:56:57 -04:00
parent 48e8057285
commit 1a1fe58f7e
5 changed files with 363 additions and 239 deletions

View File

@@ -804,6 +804,19 @@ DEFINING BREAKPOINTS
< Note that this only works for commands that are executed when < Note that this only works for commands that are executed when
sourcing the file, not for a function defined in that file. 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 The [lnum] is the line number of the breakpoint. Vim will stop at or after
this line. When omitted line 1 is used. this line. When omitted line 1 is used.

View File

@@ -916,6 +916,17 @@ varnumber_T eval_to_number(char_u *expr)
return retval; 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. * 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; 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 // 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; typval_T var2;
char_u *p; char_u *p;
int i;
exptype_T type = TYPE_UNKNOWN; exptype_T type = TYPE_UNKNOWN;
bool type_is = false; // true for "is" and "isnot" bool type_is = false; // true for "is" and "isnot"
int len = 2; int len = 2;
varnumber_T n1, n2;
bool ic; bool ic;
/* /*
@@ -3491,173 +3485,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
return FAIL; return FAIL;
} }
if (evaluate) { return typval_compare(rettv, &var2, type, type_is, ic, 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 OK; return OK;
@@ -8000,8 +7828,8 @@ int get_id_len(const char **const arg)
*/ */
int get_name_len(const char **const arg, int get_name_len(const char **const arg,
char **alias, char **alias,
int evaluate, bool evaluate,
int verbose) bool verbose)
{ {
int len; int len;
@@ -8486,10 +8314,8 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg)
return oldval; return oldval;
} }
/* // Get the value of internal variable "name".
* Get the value of internal variable "name". // Return OK or FAIL. If OK is returned "rettv" must be cleared.
* Return OK or FAIL.
*/
int get_var_tv( int get_var_tv(
const char *name, const char *name,
int len, // length of "name" int len, // length of "name"
@@ -10746,3 +10572,221 @@ bool invoke_prompt_interrupt(void)
tv_clear(&rettv); tv_clear(&rettv);
return true; 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;
}

View File

@@ -227,6 +227,19 @@ typedef enum
ASSERT_OTHER, ASSERT_OTHER,
} assert_type_T; } 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 /// Type for dict_list function
typedef enum { typedef enum {
kDictListKeys, ///< List dictionary keys. kDictListKeys, ///< List dictionary keys.

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) static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
int n = false; int n = false;
int len = 0;
const char *p = tv_get_string(&argvars[0]); const char *p = tv_get_string(&argvars[0]);
if (*p == '$') { // Environment variable. 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); n = au_exists(p + 1);
} }
} else { // Internal variable. } else { // Internal variable.
typval_T tv; n = var_exists(p);
// 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);
} }
rettv->vval.v_number = n; rettv->vval.v_number = n;

View File

@@ -120,6 +120,9 @@ struct source_cookie {
/// batch mode debugging: don't save and restore typeahead. /// batch mode debugging: don't save and restore typeahead.
static bool debug_greedy = false; 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 /// Debug mode. Repeatedly get Ex commands, until told to continue normal
/// execution. /// execution.
void do_debug(char_u *cmd) void do_debug(char_u *cmd)
@@ -166,6 +169,16 @@ void do_debug(char_u *cmd)
if (!debug_did_msg) { if (!debug_did_msg) {
MSG(_("Entering Debug mode. Type \"cont\" to continue.")); 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) { if (sourcing_name != NULL) {
msg(sourcing_name); msg(sourcing_name);
} }
@@ -174,7 +187,6 @@ void do_debug(char_u *cmd)
} else { } else {
smsg(_("cmd: %s"), cmd); smsg(_("cmd: %s"), cmd);
} }
// Repeat getting a command and executing it. // Repeat getting a command and executing it.
for (;; ) { for (;; ) {
msg_scroll = true; msg_scroll = true;
@@ -514,11 +526,13 @@ bool dbg_check_skipped(exarg_T *eap)
/// This is a grow-array of structs. /// This is a grow-array of structs.
struct debuggy { struct debuggy {
int dbg_nr; ///< breakpoint number int dbg_nr; ///< breakpoint number
int dbg_type; ///< DBG_FUNC or DBG_FILE int dbg_type; ///< DBG_FUNC or DBG_FILE or DBG_EXPR
char_u *dbg_name; ///< function or file name char_u *dbg_name; ///< function, expression or file name
regprog_T *dbg_prog; ///< regexp program regprog_T *dbg_prog; ///< regexp program
linenr_T dbg_lnum; ///< line number in function or file linenr_T dbg_lnum; ///< line number in function or file
int dbg_forceit; ///< ! used 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 }; 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 }; static garray_T prof_ga = { 0, 0, sizeof(struct debuggy), 4, NULL };
#define DBG_FUNC 1 #define DBG_FUNC 1
#define DBG_FILE 2 #define DBG_FILE 2
#define DBG_EXPR 3
/// Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them /// 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; bp->dbg_type = DBG_FILE;
here = true; here = true;
} else if (gap != &prof_ga && STRNCMP(p, "expr", 4) == 0) {
bp->dbg_type = DBG_EXPR;
} else { } else {
EMSG2(_(e_invarg2), p); EMSG2(_(e_invarg2), p);
return FAIL; return FAIL;
@@ -590,6 +607,9 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
bp->dbg_name = vim_strsave(p); bp->dbg_name = vim_strsave(p);
} else if (here) { } else if (here) {
bp->dbg_name = vim_strsave(curbuf->b_ffname); 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 { } else {
// Expand the file name in the same way as do_source(). This means // 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 // 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) void ex_breakadd(exarg_T *eap)
{ {
struct debuggy *bp; struct debuggy *bp;
char_u *pat;
garray_T *gap; garray_T *gap;
gap = &dbg_breakp; gap = &dbg_breakp;
@@ -633,22 +652,28 @@ void ex_breakadd(exarg_T *eap)
bp = &DEBUGGY(gap, gap->ga_len); bp = &DEBUGGY(gap, gap->ga_len);
bp->dbg_forceit = eap->forceit; bp->dbg_forceit = eap->forceit;
pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, false); if (bp->dbg_type != DBG_EXPR) {
if (pat != NULL) { char_u *pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, false);
bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING); if (pat != NULL) {
xfree(pat); bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
} xfree(pat);
if (pat == NULL || bp->dbg_prog == NULL) { }
xfree(bp->dbg_name); 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++;
}
} else { } else {
if (bp->dbg_lnum == 0) { // default line number is 1 // DBG_EXPR
bp->dbg_lnum = 1; DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp;
} debug_tick++;
if (eap->cmdidx != CMD_profile) {
DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
debug_tick++;
}
gap->ga_len++;
} }
} }
} }
@@ -691,7 +716,7 @@ void ex_breakdel(exarg_T *eap)
todel = 0; todel = 0;
del_all = true; del_all = true;
} else { } else {
// ":breakdel {func|file} [lnum] {name}" // ":breakdel {func|file|expr} [lnum] {name}"
if (dbg_parsearg(eap->arg, gap) == FAIL) { if (dbg_parsearg(eap->arg, gap) == FAIL) {
return; return;
} }
@@ -716,6 +741,10 @@ void ex_breakdel(exarg_T *eap)
} else { } else {
while (!GA_EMPTY(gap)) { while (!GA_EMPTY(gap)) {
xfree(DEBUGGY(gap, todel).dbg_name); 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); vim_regfree(DEBUGGY(gap, todel).dbg_prog);
gap->ga_len--; gap->ga_len--;
if (todel < gap->ga_len) { if (todel < gap->ga_len) {
@@ -750,11 +779,15 @@ void ex_breaklist(exarg_T *eap)
if (bp->dbg_type == DBG_FILE) { if (bp->dbg_type == DBG_FILE) {
home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, true); home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, true);
} }
smsg(_("%3d %s %s line %" PRId64), if (bp->dbg_type != DBG_EXPR) {
bp->dbg_nr, smsg(_("%3d %s %s line %" PRId64),
bp->dbg_type == DBG_FUNC ? "func" : "file", bp->dbg_nr,
bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff, bp->dbg_type == DBG_FUNC ? "func" : "file",
(int64_t)bp->dbg_lnum); 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. // an already found breakpoint.
bp = &DEBUGGY(gap, i); bp = &DEBUGGY(gap, i);
if ((bp->dbg_type == DBG_FILE) == file if ((bp->dbg_type == DBG_FILE) == file
&& bp->dbg_type != DBG_EXPR
&& (gap == &prof_ga && (gap == &prof_ga
|| (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum)))) { || (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum)))) {
// Save the value of got_int and reset it. We don't want a // Save the value of got_int and reset it. We don't want a
@@ -827,6 +861,49 @@ debuggy_find(
*fp = bp->dbg_forceit; *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; got_int |= prev_got_int;
} }
} }