mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #14314 from janlazo/vim-8.2.2720
vim-patch:8.0.{1505,1511},8.2.{1304,1305,1393,1508,1791,2720}
This commit is contained in:
commit
ed3c0a27c7
@ -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.
|
||||||
|
|
||||||
|
403
src/nvim/eval.c
403
src/nvim/eval.c
@ -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;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3490,173 +3484,11 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
tv_clear(rettv);
|
tv_clear(rettv);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evaluate) {
|
if (evaluate) {
|
||||||
if (type_is && rettv->v_type != var2.v_type) {
|
const int ret = typval_compare(rettv, &var2, type, type_is, ic);
|
||||||
/* 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);
|
tv_clear(&var2);
|
||||||
rettv->v_type = VAR_NUMBER;
|
return ret;
|
||||||
rettv->vval.v_number = n1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8000,8 +7832,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 +8318,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 +10576,202 @@ bool invoke_prompt_interrupt(void)
|
|||||||
tv_clear(&rettv);
|
tv_clear(&rettv);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compare "typ1" and "typ2". Put the result in "typ1".
|
||||||
|
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
|
||||||
|
)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
varnumber_T n1, n2;
|
||||||
|
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
typ1->v_type = VAR_NUMBER;
|
||||||
|
typ1->vval.v_number = n1;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
@ -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,46 @@ 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 {
|
||||||
|
if (typval_compare(tv, bp->dbg_val, TYPE_EQUAL, true, false) == OK
|
||||||
|
&& tv->vval.v_number == false) {
|
||||||
|
line = true;
|
||||||
|
debug_oldval = typval_tostring(bp->dbg_val);
|
||||||
|
// Need to evaluate again, typval_compare() overwrites "tv".
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,9 @@
|
|||||||
" For csh:
|
" For csh:
|
||||||
" setenv TEST_FILTER Test_channel
|
" setenv TEST_FILTER Test_channel
|
||||||
"
|
"
|
||||||
|
" While working on a test you can make $TEST_NO_RETRY non-empty to not retry:
|
||||||
|
" export TEST_NO_RETRY=yes
|
||||||
|
"
|
||||||
" To ignore failure for tests that are known to fail in a certain environment,
|
" To ignore failure for tests that are known to fail in a certain environment,
|
||||||
" set $TEST_MAY_FAIL to a comma separated list of function names. E.g. for
|
" set $TEST_MAY_FAIL to a comma separated list of function names. E.g. for
|
||||||
" sh/bash:
|
" sh/bash:
|
||||||
@ -413,9 +416,11 @@ for s:test in sort(s:tests)
|
|||||||
call RunTheTest(s:test)
|
call RunTheTest(s:test)
|
||||||
|
|
||||||
" Repeat a flaky test. Give up when:
|
" Repeat a flaky test. Give up when:
|
||||||
|
" - $TEST_NO_RETRY is not empty
|
||||||
" - it fails again with the same message
|
" - it fails again with the same message
|
||||||
" - it fails five times (with a different message)
|
" - it fails five times (with a different message)
|
||||||
if len(v:errors) > 0
|
if len(v:errors) > 0
|
||||||
|
\ && $TEST_NO_RETRY == ''
|
||||||
\ && (index(s:flaky_tests, s:test) >= 0
|
\ && (index(s:flaky_tests, s:test) >= 0
|
||||||
\ || g:test_is_flaky)
|
\ || g:test_is_flaky)
|
||||||
while 1
|
while 1
|
||||||
|
@ -2,6 +2,31 @@
|
|||||||
|
|
||||||
source shared.vim
|
source shared.vim
|
||||||
source screendump.vim
|
source screendump.vim
|
||||||
|
source check.vim
|
||||||
|
|
||||||
|
func CheckCWD()
|
||||||
|
" Check that the longer lines don't wrap due to the length of the script name
|
||||||
|
" in cwd
|
||||||
|
let script_len = len( getcwd() .. '/Xtest1.vim' )
|
||||||
|
let longest_line = len( 'Breakpoint in "" line 1' )
|
||||||
|
if script_len > ( 75 - longest_line )
|
||||||
|
throw 'Skipped: Your CWD has too many characters'
|
||||||
|
endif
|
||||||
|
endfunc
|
||||||
|
command! -nargs=0 -bar CheckCWD call CheckCWD()
|
||||||
|
|
||||||
|
func CheckDbgOutput(buf, lines, options = {})
|
||||||
|
" Verify the expected output
|
||||||
|
let lnum = 20 - len(a:lines)
|
||||||
|
for l in a:lines
|
||||||
|
if get(a:options, 'match', 'equal') ==# 'pattern'
|
||||||
|
call WaitForAssert({-> assert_match(l, term_getline(a:buf, lnum))}, 200)
|
||||||
|
else
|
||||||
|
call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}, 200)
|
||||||
|
endif
|
||||||
|
let lnum += 1
|
||||||
|
endfor
|
||||||
|
endfunc
|
||||||
|
|
||||||
" Run a Vim debugger command
|
" Run a Vim debugger command
|
||||||
" If the expected output argument is supplied, then check for it.
|
" If the expected output argument is supplied, then check for it.
|
||||||
@ -10,20 +35,17 @@ func RunDbgCmd(buf, cmd, ...)
|
|||||||
call term_wait(a:buf)
|
call term_wait(a:buf)
|
||||||
|
|
||||||
if a:0 != 0
|
if a:0 != 0
|
||||||
" Verify the expected output
|
let options = #{match: 'equal'}
|
||||||
let lnum = 20 - len(a:1)
|
if a:0 > 1
|
||||||
for l in a:1
|
call extend(options, a:2)
|
||||||
call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))})
|
endif
|
||||||
let lnum += 1
|
call CheckDbgOutput(a:buf, a:1, options)
|
||||||
endfor
|
|
||||||
endif
|
endif
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" Debugger tests
|
" Debugger tests
|
||||||
func Test_Debugger()
|
func Test_Debugger()
|
||||||
if !CanRunVimInTerminal()
|
CheckRunVimInTerminal
|
||||||
throw 'Skipped: cannot run Vim in a terminal window'
|
|
||||||
endif
|
|
||||||
|
|
||||||
" Create a Vim script with some functions
|
" Create a Vim script with some functions
|
||||||
let lines =<< trim END
|
let lines =<< trim END
|
||||||
@ -317,6 +339,785 @@ func Test_Debugger()
|
|||||||
call delete('Xtest.vim')
|
call delete('Xtest.vim')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_Backtrace_Through_Source()
|
||||||
|
CheckRunVimInTerminal
|
||||||
|
CheckCWD
|
||||||
|
let file1 =<< trim END
|
||||||
|
func SourceAnotherFile()
|
||||||
|
source Xtest2.vim
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func CallAFunction()
|
||||||
|
call SourceAnotherFile()
|
||||||
|
call File2Function()
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func GlobalFunction()
|
||||||
|
call CallAFunction()
|
||||||
|
endfunc
|
||||||
|
END
|
||||||
|
call writefile(file1, 'Xtest1.vim')
|
||||||
|
|
||||||
|
let file2 =<< trim END
|
||||||
|
func DoAThing()
|
||||||
|
echo "DoAThing"
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func File2Function()
|
||||||
|
call DoAThing()
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
call File2Function()
|
||||||
|
END
|
||||||
|
call writefile(file2, 'Xtest2.vim')
|
||||||
|
|
||||||
|
let buf = RunVimInTerminal('-S Xtest1.vim', {})
|
||||||
|
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\ ':debug call GlobalFunction()',
|
||||||
|
\ ['cmd: call GlobalFunction()'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'backtrace', ['>backtrace',
|
||||||
|
\ '->0 function GlobalFunction',
|
||||||
|
\ 'line 1: call CallAFunction()'])
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'backtrace', ['>backtrace',
|
||||||
|
\ ' 2 function GlobalFunction[1]',
|
||||||
|
\ ' 1 CallAFunction[1]',
|
||||||
|
\ '->0 SourceAnotherFile',
|
||||||
|
\ 'line 1: source Xtest2.vim'])
|
||||||
|
|
||||||
|
" Step into the 'source' command. Note that we print the full trace all the
|
||||||
|
" way though the source command.
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: func DoAThing()'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ '->0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()'])
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'up' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ '->1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'up' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ '->2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'up' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ '->3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'up', [ 'frame at highest level: 3' ] )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ '->3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'down' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ '->2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'down' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ '->1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'down' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ '->0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'down', [ 'frame is zero' ] )
|
||||||
|
|
||||||
|
" step until we have another meaninfgul trace
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 5: func File2Function()'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 9: call File2Function()'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ '->0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 9: call File2Function()'])
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 5 function GlobalFunction[1]',
|
||||||
|
\ ' 4 CallAFunction[1]',
|
||||||
|
\ ' 3 SourceAnotherFile[1]',
|
||||||
|
\ ' 2 script ' .. getcwd() .. '/Xtest2.vim[9]',
|
||||||
|
\ ' 1 function File2Function[1]',
|
||||||
|
\ '->0 DoAThing',
|
||||||
|
\ 'line 1: echo "DoAThing"'])
|
||||||
|
|
||||||
|
" Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: End of function'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: End of function'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 10: End of sourced file'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: End of function'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 2: call File2Function()'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 1 function GlobalFunction[1]',
|
||||||
|
\ '->0 CallAFunction',
|
||||||
|
\ 'line 2: call File2Function()'])
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 2 function GlobalFunction[1]',
|
||||||
|
\ ' 1 CallAFunction[2]',
|
||||||
|
\ '->0 File2Function',
|
||||||
|
\ 'line 1: call DoAThing()'])
|
||||||
|
|
||||||
|
call StopVimInTerminal(buf)
|
||||||
|
call delete('Xtest1.vim')
|
||||||
|
call delete('Xtest2.vim')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_Backtrace_Autocmd()
|
||||||
|
CheckRunVimInTerminal
|
||||||
|
CheckCWD
|
||||||
|
let file1 =<< trim END
|
||||||
|
func SourceAnotherFile()
|
||||||
|
source Xtest2.vim
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func CallAFunction()
|
||||||
|
call SourceAnotherFile()
|
||||||
|
call File2Function()
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func GlobalFunction()
|
||||||
|
call CallAFunction()
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
au User TestGlobalFunction :call GlobalFunction() | echo "Done"
|
||||||
|
END
|
||||||
|
call writefile(file1, 'Xtest1.vim')
|
||||||
|
|
||||||
|
let file2 =<< trim END
|
||||||
|
func DoAThing()
|
||||||
|
echo "DoAThing"
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func File2Function()
|
||||||
|
call DoAThing()
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
call File2Function()
|
||||||
|
END
|
||||||
|
call writefile(file2, 'Xtest2.vim')
|
||||||
|
|
||||||
|
let buf = RunVimInTerminal('-S Xtest1.vim', {})
|
||||||
|
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\ ':debug doautocmd User TestGlobalFunction',
|
||||||
|
\ ['cmd: doautocmd User TestGlobalFunction'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['cmd: call GlobalFunction() | echo "Done"'])
|
||||||
|
|
||||||
|
" At this point the ontly thing in the stack is the autocommand
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ '->0 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ 'cmd: call GlobalFunction() | echo "Done"'])
|
||||||
|
|
||||||
|
" And now we're back into the call stack
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 1 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ '->0 function GlobalFunction',
|
||||||
|
\ 'line 1: call CallAFunction()'])
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 3 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 2 function GlobalFunction[1]',
|
||||||
|
\ ' 1 CallAFunction[1]',
|
||||||
|
\ '->0 SourceAnotherFile',
|
||||||
|
\ 'line 1: source Xtest2.vim'])
|
||||||
|
|
||||||
|
" Step into the 'source' command. Note that we print the full trace all the
|
||||||
|
" way though the source command.
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: func DoAThing()'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 4 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ '->0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()'])
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'up' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 4 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ '->1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'up' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 4 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ '->2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'up' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 4 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ '->3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'up' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ '->4 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'up', [ 'frame at highest level: 4' ] )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ '->4 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'down' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 4 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ '->3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'down' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 4 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ '->2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'down' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 4 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ '->1 SourceAnotherFile[1]',
|
||||||
|
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'down' )
|
||||||
|
call RunDbgCmd( buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 4 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ '->0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 1: func DoAThing()' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd( buf, 'down', [ 'frame is zero' ] )
|
||||||
|
|
||||||
|
" step until we have another meaninfgul trace
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 5: func File2Function()'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 9: call File2Function()'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 4 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 3 function GlobalFunction[1]',
|
||||||
|
\ ' 2 CallAFunction[1]',
|
||||||
|
\ ' 1 SourceAnotherFile[1]',
|
||||||
|
\ '->0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ 'line 9: call File2Function()'])
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 6 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 5 function GlobalFunction[1]',
|
||||||
|
\ ' 4 CallAFunction[1]',
|
||||||
|
\ ' 3 SourceAnotherFile[1]',
|
||||||
|
\ ' 2 script ' .. getcwd() .. '/Xtest2.vim[9]',
|
||||||
|
\ ' 1 function File2Function[1]',
|
||||||
|
\ '->0 DoAThing',
|
||||||
|
\ 'line 1: echo "DoAThing"'])
|
||||||
|
|
||||||
|
" Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: End of function'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: End of function'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 10: End of sourced file'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: End of function'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 2: call File2Function()'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 2 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 1 function GlobalFunction[1]',
|
||||||
|
\ '->0 CallAFunction',
|
||||||
|
\ 'line 2: call File2Function()'])
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 3 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 2 function GlobalFunction[1]',
|
||||||
|
\ ' 1 CallAFunction[2]',
|
||||||
|
\ '->0 File2Function',
|
||||||
|
\ 'line 1: call DoAThing()'])
|
||||||
|
|
||||||
|
|
||||||
|
" Now unwind so that we get back to the original autocommand (and the second
|
||||||
|
" cmd echo "Done")
|
||||||
|
call RunDbgCmd(buf, 'finish', ['line 1: End of function'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 3 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 2 function GlobalFunction[1]',
|
||||||
|
\ ' 1 CallAFunction[2]',
|
||||||
|
\ '->0 File2Function',
|
||||||
|
\ 'line 1: End of function'])
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'finish', ['line 2: End of function'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 2 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ ' 1 function GlobalFunction[1]',
|
||||||
|
\ '->0 CallAFunction',
|
||||||
|
\ 'line 2: End of function'])
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'finish', ['line 1: End of function'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 1 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ '->0 function GlobalFunction',
|
||||||
|
\ 'line 1: End of function'])
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'step', ['cmd: echo "Done"'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ '->0 User Autocommands for "TestGlobalFunction"',
|
||||||
|
\ 'cmd: echo "Done"'])
|
||||||
|
|
||||||
|
call StopVimInTerminal(buf)
|
||||||
|
call delete('Xtest1.vim')
|
||||||
|
call delete('Xtest2.vim')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_Backtrace_CmdLine()
|
||||||
|
CheckRunVimInTerminal
|
||||||
|
CheckCWD
|
||||||
|
let file1 =<< trim END
|
||||||
|
func SourceAnotherFile()
|
||||||
|
source Xtest2.vim
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func CallAFunction()
|
||||||
|
call SourceAnotherFile()
|
||||||
|
call File2Function()
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func GlobalFunction()
|
||||||
|
call CallAFunction()
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
au User TestGlobalFunction :call GlobalFunction() | echo "Done"
|
||||||
|
END
|
||||||
|
call writefile(file1, 'Xtest1.vim')
|
||||||
|
|
||||||
|
let file2 =<< trim END
|
||||||
|
func DoAThing()
|
||||||
|
echo "DoAThing"
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func File2Function()
|
||||||
|
call DoAThing()
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
call File2Function()
|
||||||
|
END
|
||||||
|
call writefile(file2, 'Xtest2.vim')
|
||||||
|
|
||||||
|
let buf = RunVimInTerminal(
|
||||||
|
\ '-S Xtest1.vim -c "debug call GlobalFunction()"',
|
||||||
|
\ {'wait_for_ruler': 0})
|
||||||
|
|
||||||
|
" Need to wait for the vim-in-terminal to be ready
|
||||||
|
call CheckDbgOutput(buf, ['command line',
|
||||||
|
\ 'cmd: call GlobalFunction()'])
|
||||||
|
|
||||||
|
" At this point the ontly thing in the stack is the cmdline
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ '->0 command line',
|
||||||
|
\ 'cmd: call GlobalFunction()'])
|
||||||
|
|
||||||
|
" And now we're back into the call stack
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '>backtrace',
|
||||||
|
\ ' 1 command line',
|
||||||
|
\ '->0 function GlobalFunction',
|
||||||
|
\ 'line 1: call CallAFunction()'])
|
||||||
|
|
||||||
|
call StopVimInTerminal(buf)
|
||||||
|
call delete('Xtest1.vim')
|
||||||
|
call delete('Xtest2.vim')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_Backtrace_DefFunction()
|
||||||
|
CheckRunVimInTerminal
|
||||||
|
CheckCWD
|
||||||
|
let file1 =<< trim END
|
||||||
|
vim9script
|
||||||
|
import File2Function from './Xtest2.vim'
|
||||||
|
|
||||||
|
def SourceAnotherFile()
|
||||||
|
source Xtest2.vim
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def CallAFunction()
|
||||||
|
SourceAnotherFile()
|
||||||
|
File2Function()
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def g:GlobalFunction()
|
||||||
|
CallAFunction()
|
||||||
|
enddef
|
||||||
|
|
||||||
|
defcompile
|
||||||
|
END
|
||||||
|
call writefile(file1, 'Xtest1.vim')
|
||||||
|
|
||||||
|
let file2 =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
def DoAThing(): number
|
||||||
|
var a = 100 * 2
|
||||||
|
a += 3
|
||||||
|
return a
|
||||||
|
enddef
|
||||||
|
|
||||||
|
export def File2Function()
|
||||||
|
DoAThing()
|
||||||
|
enddef
|
||||||
|
|
||||||
|
defcompile
|
||||||
|
File2Function()
|
||||||
|
END
|
||||||
|
call writefile(file2, 'Xtest2.vim')
|
||||||
|
|
||||||
|
let buf = RunVimInTerminal('-S Xtest1.vim', {})
|
||||||
|
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\ ':debug call GlobalFunction()',
|
||||||
|
\ ['cmd: call GlobalFunction()'])
|
||||||
|
|
||||||
|
" FIXME: Vim9 lines are not debugged!
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
|
||||||
|
|
||||||
|
" But they do appear in the backtrace
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '\V>backtrace',
|
||||||
|
\ '\V 2 function GlobalFunction[1]',
|
||||||
|
\ '\V 1 <SNR>\.\*_CallAFunction[1]',
|
||||||
|
\ '\V->0 <SNR>\.\*_SourceAnotherFile',
|
||||||
|
\ '\Vline 1: source Xtest2.vim'],
|
||||||
|
\ #{match: 'pattern'})
|
||||||
|
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 1: vim9script'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 3: def DoAThing(): number'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 9: export def File2Function()'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 9: def File2Function()'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 13: defcompile'])
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 14: File2Function()'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '\V>backtrace',
|
||||||
|
\ '\V 3 function GlobalFunction[1]',
|
||||||
|
\ '\V 2 <SNR>\.\*_CallAFunction[1]',
|
||||||
|
\ '\V 1 <SNR>\.\*_SourceAnotherFile[1]',
|
||||||
|
\ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ '\Vline 14: File2Function()'],
|
||||||
|
\ #{match: 'pattern'})
|
||||||
|
|
||||||
|
" Don't step into compiled functions...
|
||||||
|
call RunDbgCmd(buf, 'step', ['line 15: End of sourced file'])
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '\V>backtrace',
|
||||||
|
\ '\V 3 function GlobalFunction[1]',
|
||||||
|
\ '\V 2 <SNR>\.\*_CallAFunction[1]',
|
||||||
|
\ '\V 1 <SNR>\.\*_SourceAnotherFile[1]',
|
||||||
|
\ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
|
||||||
|
\ '\Vline 15: End of sourced file'],
|
||||||
|
\ #{match: 'pattern'})
|
||||||
|
|
||||||
|
|
||||||
|
call StopVimInTerminal(buf)
|
||||||
|
call delete('Xtest1.vim')
|
||||||
|
call delete('Xtest2.vim')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_debug_backtrace_level()
|
||||||
|
CheckRunVimInTerminal
|
||||||
|
CheckCWD
|
||||||
|
let lines =<< trim END
|
||||||
|
let s:file1_var = 'file1'
|
||||||
|
let g:global_var = 'global'
|
||||||
|
|
||||||
|
func s:File1Func( arg )
|
||||||
|
let s:file1_var .= a:arg
|
||||||
|
let local_var = s:file1_var .. ' test1'
|
||||||
|
let g:global_var .= local_var
|
||||||
|
source Xtest2.vim
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
call s:File1Func( 'arg1' )
|
||||||
|
END
|
||||||
|
call writefile(lines, 'Xtest1.vim')
|
||||||
|
|
||||||
|
let lines =<< trim END
|
||||||
|
let s:file2_var = 'file2'
|
||||||
|
|
||||||
|
func s:File2Func( arg )
|
||||||
|
let s:file2_var .= a:arg
|
||||||
|
let local_var = s:file2_var .. ' test2'
|
||||||
|
let g:global_var .= local_var
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
call s:File2Func( 'arg2' )
|
||||||
|
END
|
||||||
|
call writefile(lines, 'Xtest2.vim')
|
||||||
|
|
||||||
|
let file1 = getcwd() .. '/Xtest1.vim'
|
||||||
|
let file2 = getcwd() .. '/Xtest2.vim'
|
||||||
|
|
||||||
|
" set a breakpoint and source file1.vim
|
||||||
|
let buf = RunVimInTerminal(
|
||||||
|
\ '-c "breakadd file 1 Xtest1.vim" -S Xtest1.vim',
|
||||||
|
\ #{ wait_for_ruler: 0 } )
|
||||||
|
|
||||||
|
call CheckDbgOutput(buf, [
|
||||||
|
\ 'Breakpoint in "' .. file1 .. '" line 1',
|
||||||
|
\ 'Entering Debug mode. Type "cont" to continue.',
|
||||||
|
\ 'command line..script ' .. file1,
|
||||||
|
\ 'line 1: let s:file1_var = ''file1'''
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
" step throught the initial declarations
|
||||||
|
call RunDbgCmd(buf, 'step', [ 'line 2: let g:global_var = ''global''' ] )
|
||||||
|
call RunDbgCmd(buf, 'step', [ 'line 4: func s:File1Func( arg )' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo global_var', [ 'global' ] )
|
||||||
|
|
||||||
|
" step in to the first function
|
||||||
|
call RunDbgCmd(buf, 'step', [ 'line 11: call s:File1Func( ''arg1'' )' ] )
|
||||||
|
call RunDbgCmd(buf, 'step', [ 'line 1: let s:file1_var .= a:arg' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\'echo global_var',
|
||||||
|
\[ 'E121: Undefined variable: global_var' ] )
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\'echo local_var',
|
||||||
|
\[ 'E121: Undefined variable: local_var' ] )
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\'echo l:local_var',
|
||||||
|
\[ 'E121: Undefined variable: l:local_var' ] )
|
||||||
|
|
||||||
|
" backtrace up
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '\V>backtrace',
|
||||||
|
\ '\V 2 command line',
|
||||||
|
\ '\V 1 script ' .. file1 .. '[11]',
|
||||||
|
\ '\V->0 function <SNR>\.\*_File1Func',
|
||||||
|
\ '\Vline 1: let s:file1_var .= a:arg',
|
||||||
|
\ ],
|
||||||
|
\ #{ match: 'pattern' } )
|
||||||
|
call RunDbgCmd(buf, 'up', [ '>up' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '\V>backtrace',
|
||||||
|
\ '\V 2 command line',
|
||||||
|
\ '\V->1 script ' .. file1 .. '[11]',
|
||||||
|
\ '\V 0 function <SNR>\.\*_File1Func',
|
||||||
|
\ '\Vline 1: let s:file1_var .= a:arg',
|
||||||
|
\ ],
|
||||||
|
\ #{ match: 'pattern' } )
|
||||||
|
|
||||||
|
" Expression evaluation in the script frame (not the function frame)
|
||||||
|
" FIXME: Unexpected in this scope (a: should not be visibnle)
|
||||||
|
call RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
|
||||||
|
" FIXME: Unexpected in this scope (global should be found)
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\'echo global_var',
|
||||||
|
\[ 'E121: Undefined variable: global_var' ] )
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\'echo local_var',
|
||||||
|
\[ 'E121: Undefined variable: local_var' ] )
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\'echo l:local_var',
|
||||||
|
\[ 'E121: Undefined variable: l:local_var' ] )
|
||||||
|
|
||||||
|
|
||||||
|
" step while backtraced jumps to the latest frame
|
||||||
|
call RunDbgCmd(buf, 'step', [
|
||||||
|
\ 'line 2: let local_var = s:file1_var .. '' test1''' ] )
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '\V>backtrace',
|
||||||
|
\ '\V 2 command line',
|
||||||
|
\ '\V 1 script ' .. file1 .. '[11]',
|
||||||
|
\ '\V->0 function <SNR>\.\*_File1Func',
|
||||||
|
\ '\Vline 2: let local_var = s:file1_var .. '' test1''',
|
||||||
|
\ ],
|
||||||
|
\ #{ match: 'pattern' } )
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'step', [ 'line 3: let g:global_var .= local_var' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo local_var', [ 'file1arg1 test1' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo l:local_var', [ 'file1arg1 test1' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'step', [ 'line 4: source Xtest2.vim' ] )
|
||||||
|
call RunDbgCmd(buf, 'step', [ 'line 1: let s:file2_var = ''file2''' ] )
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '\V>backtrace',
|
||||||
|
\ '\V 3 command line',
|
||||||
|
\ '\V 2 script ' .. file1 .. '[11]',
|
||||||
|
\ '\V 1 function <SNR>\.\*_File1Func[4]',
|
||||||
|
\ '\V->0 script ' .. file2,
|
||||||
|
\ '\Vline 1: let s:file2_var = ''file2''',
|
||||||
|
\ ],
|
||||||
|
\ #{ match: 'pattern' } )
|
||||||
|
|
||||||
|
" Expression evaluation in the script frame file2 (not the function frame)
|
||||||
|
call RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] )
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\ 'echo s:file1_var',
|
||||||
|
\ [ 'E121: Undefined variable: s:file1_var' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo g:global_var', [ 'globalfile1arg1 test1' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo global_var', [ 'globalfile1arg1 test1' ] )
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\'echo local_var',
|
||||||
|
\[ 'E121: Undefined variable: local_var' ] )
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\'echo l:local_var',
|
||||||
|
\[ 'E121: Undefined variable: l:local_var' ] )
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\ 'echo s:file2_var',
|
||||||
|
\ [ 'E121: Undefined variable: s:file2_var' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'step', [ 'line 3: func s:File2Func( arg )' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] )
|
||||||
|
|
||||||
|
" Up the stack to the other script context
|
||||||
|
call RunDbgCmd(buf, 'up')
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '\V>backtrace',
|
||||||
|
\ '\V 3 command line',
|
||||||
|
\ '\V 2 script ' .. file1 .. '[11]',
|
||||||
|
\ '\V->1 function <SNR>\.\*_File1Func[4]',
|
||||||
|
\ '\V 0 script ' .. file2,
|
||||||
|
\ '\Vline 3: func s:File2Func( arg )',
|
||||||
|
\ ],
|
||||||
|
\ #{ match: 'pattern' } )
|
||||||
|
" FIXME: Unexpected. Should see the a: and l: dicts from File1Func
|
||||||
|
call RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] )
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\ 'echo l:local_var',
|
||||||
|
\ [ 'E121: Undefined variable: l:local_var' ] )
|
||||||
|
|
||||||
|
call RunDbgCmd(buf, 'up')
|
||||||
|
call RunDbgCmd(buf, 'backtrace', [
|
||||||
|
\ '\V>backtrace',
|
||||||
|
\ '\V 3 command line',
|
||||||
|
\ '\V->2 script ' .. file1 .. '[11]',
|
||||||
|
\ '\V 1 function <SNR>\.\*_File1Func[4]',
|
||||||
|
\ '\V 0 script ' .. file2,
|
||||||
|
\ '\Vline 3: func s:File2Func( arg )',
|
||||||
|
\ ],
|
||||||
|
\ #{ match: 'pattern' } )
|
||||||
|
|
||||||
|
" FIXME: Unexpected (wrong script vars are used)
|
||||||
|
call RunDbgCmd(buf,
|
||||||
|
\ 'echo s:file1_var',
|
||||||
|
\ [ 'E121: Undefined variable: s:file1_var' ] )
|
||||||
|
call RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] )
|
||||||
|
|
||||||
|
call StopVimInTerminal(buf)
|
||||||
|
call delete('Xtest1.vim')
|
||||||
|
call delete('Xtest2.vim')
|
||||||
|
endfunc
|
||||||
|
|
||||||
" Test for setting a breakpoint on a :endif where the :if condition is false
|
" Test for setting a breakpoint on a :endif where the :if condition is false
|
||||||
" and then quit the script. This should generate an interrupt.
|
" and then quit the script. This should generate an interrupt.
|
||||||
func Test_breakpt_endif_intr()
|
func Test_breakpt_endif_intr()
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
" Tests for the writefile() function and some :write commands.
|
" Tests for the writefile() function and some :write commands.
|
||||||
|
|
||||||
|
source check.vim
|
||||||
|
source term_util.vim
|
||||||
|
|
||||||
func Test_writefile()
|
func Test_writefile()
|
||||||
let f = tempname()
|
let f = tempname()
|
||||||
call writefile(["over","written"], f, "b")
|
call writefile(["over","written"], f, "b")
|
||||||
@ -179,3 +182,120 @@ func Test_writefile_sync_arg()
|
|||||||
call writefile(['two'], 'Xtest', 'S')
|
call writefile(['two'], 'Xtest', 'S')
|
||||||
call delete('Xtest')
|
call delete('Xtest')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" Tests for reading and writing files with conversion for Win32.
|
||||||
|
func Test_write_file_encoding()
|
||||||
|
CheckMSWindows
|
||||||
|
throw 'skipped: Nvim does not support :w ++enc=cp1251'
|
||||||
|
let save_encoding = &encoding
|
||||||
|
let save_fileencodings = &fileencodings
|
||||||
|
set encoding& fileencodings&
|
||||||
|
let text =<< trim END
|
||||||
|
1 utf-8 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01
|
||||||
|
2 cp1251 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
|
||||||
|
3 cp866 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01
|
||||||
|
END
|
||||||
|
call writefile(text, 'Xfile')
|
||||||
|
edit Xfile
|
||||||
|
|
||||||
|
" write tests:
|
||||||
|
" combine three values for 'encoding' with three values for 'fileencoding'
|
||||||
|
" also write files for read tests
|
||||||
|
call cursor(1, 1)
|
||||||
|
set encoding=utf-8
|
||||||
|
.w! ++enc=utf-8 Xtest
|
||||||
|
.w ++enc=cp1251 >> Xtest
|
||||||
|
.w ++enc=cp866 >> Xtest
|
||||||
|
.w! ++enc=utf-8 Xutf8
|
||||||
|
let expected =<< trim END
|
||||||
|
1 utf-8 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01
|
||||||
|
1 utf-8 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
|
||||||
|
1 utf-8 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01
|
||||||
|
END
|
||||||
|
call assert_equal(expected, readfile('Xtest'))
|
||||||
|
|
||||||
|
call cursor(2, 1)
|
||||||
|
set encoding=cp1251
|
||||||
|
.w! ++enc=utf-8 Xtest
|
||||||
|
.w ++enc=cp1251 >> Xtest
|
||||||
|
.w ++enc=cp866 >> Xtest
|
||||||
|
.w! ++enc=cp1251 Xcp1251
|
||||||
|
let expected =<< trim END
|
||||||
|
2 cp1251 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01
|
||||||
|
2 cp1251 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
|
||||||
|
2 cp1251 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01
|
||||||
|
END
|
||||||
|
call assert_equal(expected, readfile('Xtest'))
|
||||||
|
|
||||||
|
call cursor(3, 1)
|
||||||
|
set encoding=cp866
|
||||||
|
.w! ++enc=utf-8 Xtest
|
||||||
|
.w ++enc=cp1251 >> Xtest
|
||||||
|
.w ++enc=cp866 >> Xtest
|
||||||
|
.w! ++enc=cp866 Xcp866
|
||||||
|
let expected =<< trim END
|
||||||
|
3 cp866 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01
|
||||||
|
3 cp866 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
|
||||||
|
3 cp866 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01
|
||||||
|
END
|
||||||
|
call assert_equal(expected, readfile('Xtest'))
|
||||||
|
|
||||||
|
" read three 'fileencoding's with utf-8 'encoding'
|
||||||
|
set encoding=utf-8 fencs=utf-8,cp1251
|
||||||
|
e Xutf8
|
||||||
|
.w! ++enc=utf-8 Xtest
|
||||||
|
e Xcp1251
|
||||||
|
.w ++enc=utf-8 >> Xtest
|
||||||
|
set fencs=utf-8,cp866
|
||||||
|
e Xcp866
|
||||||
|
.w ++enc=utf-8 >> Xtest
|
||||||
|
let expected =<< trim END
|
||||||
|
1 utf-8 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01
|
||||||
|
2 cp1251 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01
|
||||||
|
3 cp866 text: ÐÐ»Ñ Vim version 6.2. ÐоÑледнее изменение: 1970 Jan 01
|
||||||
|
END
|
||||||
|
call assert_equal(expected, readfile('Xtest'))
|
||||||
|
|
||||||
|
" read three 'fileencoding's with cp1251 'encoding'
|
||||||
|
set encoding=utf-8 fencs=utf-8,cp1251
|
||||||
|
e Xutf8
|
||||||
|
.w! ++enc=cp1251 Xtest
|
||||||
|
e Xcp1251
|
||||||
|
.w ++enc=cp1251 >> Xtest
|
||||||
|
set fencs=utf-8,cp866
|
||||||
|
e Xcp866
|
||||||
|
.w ++enc=cp1251 >> Xtest
|
||||||
|
let expected =<< trim END
|
||||||
|
1 utf-8 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
|
||||||
|
2 cp1251 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
|
||||||
|
3 cp866 text: Äëÿ Vim version 6.2. Ïîñëåäíåå èçìåíåíèå: 1970 Jan 01
|
||||||
|
END
|
||||||
|
call assert_equal(expected, readfile('Xtest'))
|
||||||
|
|
||||||
|
" read three 'fileencoding's with cp866 'encoding'
|
||||||
|
set encoding=cp866 fencs=utf-8,cp1251
|
||||||
|
e Xutf8
|
||||||
|
.w! ++enc=cp866 Xtest
|
||||||
|
e Xcp1251
|
||||||
|
.w ++enc=cp866 >> Xtest
|
||||||
|
set fencs=utf-8,cp866
|
||||||
|
e Xcp866
|
||||||
|
.w ++enc=cp866 >> Xtest
|
||||||
|
let expected =<< trim END
|
||||||
|
1 utf-8 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01
|
||||||
|
2 cp1251 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01
|
||||||
|
3 cp866 text: «ï Vim version 6.2. ®á«¥¤¥¥ ¨§¬¥¥¨¥: 1970 Jan 01
|
||||||
|
END
|
||||||
|
call assert_equal(expected, readfile('Xtest'))
|
||||||
|
|
||||||
|
call delete('Xfile')
|
||||||
|
call delete('Xtest')
|
||||||
|
call delete('Xutf8')
|
||||||
|
call delete('Xcp1251')
|
||||||
|
call delete('Xcp866')
|
||||||
|
let &encoding = save_encoding
|
||||||
|
let &fileencodings = save_fileencodings
|
||||||
|
%bw!
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
Loading…
Reference in New Issue
Block a user