diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 9dffc84c0c..2386b149a9 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2017,6 +2017,7 @@ argidx() Number current index in the argument list arglistid([{winnr} [, {tabnr}]]) Number argument list id argv({nr}) String {nr} entry of the argument list argv() List the argument list +assert_beeps({cmd}) none assert {cmd} causes a beep assert_equal({exp}, {act} [, {msg}]) none assert {exp} is equal to {act} assert_exception({error} [, {msg}]) @@ -2151,6 +2152,7 @@ getbufline({expr}, {lnum} [, {end}]) List lines {lnum} to {end} of buffer {expr} getbufvar({expr}, {varname} [, {def}]) any variable {varname} in buffer {expr} +getchangelist({expr}) List list of change list items getchar([expr]) Number get one character from the user getcharmod() Number modifiers for the last typed character getcharsearch() Dict last character search @@ -2568,6 +2570,11 @@ argv([{nr}]) The result is the {nr}th file in the argument list of the < Without the {nr} argument a |List| with the whole |arglist| is returned. +assert_beeps({cmd}) *assert_beeps()* + Run {cmd} and add an error message to |v:errors| if it does + NOT produce a beep or visual bell. + Also see |assert_fails()|. + *assert_equal()* assert_equal({expected}, {actual} [, {msg}]) When {expected} and {actual} are not equal an error message is @@ -2600,6 +2607,8 @@ assert_fails({cmd} [, {error}]) *assert_fails()* Run {cmd} and add an error message to |v:errors| if it does NOT produce an error. When {error} is given it must match in |v:errmsg|. + Note that beeping is not considered an error, and some failing + commands only beep. Use |assert_beeps()| for those. assert_false({actual} [, {msg}]) *assert_false()* When {actual} is not false an error message is added to @@ -4270,6 +4279,22 @@ getbufvar({expr}, {varname} [, {def}]) *getbufvar()* :let bufmodified = getbufvar(1, "&mod") :echo "todo myvar = " . getbufvar("todo", "myvar") < +getchangelist({expr}) *getchangelist()* + Returns the |changelist| for the buffer {expr}. For the use + of {expr}, see |bufname()| above. If buffer {expr} doesn't + exist, an empty list is returned. + + The returned list contains two entries: a list with the change + locations and the current position in the list. Each + entry in the change list is a dictionary with the following + entries: + col column number + coladd column offset for 'virtualedit' + lnum line number + If buffer {expr} is the current buffer, then the current + position refers to the position in the list. For other + buffers, it is set to the length of the list. + getchar([expr]) *getchar()* Get a single character from the user or input stream. If [expr] is omitted, wait until a character is available. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 590fde4827..ccf59d6176 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -807,6 +807,7 @@ Buffers, windows and the argument list: getbufinfo() get a list with buffer information gettabinfo() get a list with tab page information getwininfo() get a list with window information + getchangelist() get a list of change list entries getjumplist() get a list of jump list entries Command line: *command-line-functions* diff --git a/src/Makefile b/src/Makefile index 95e00df169..35768639ba 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2144,6 +2144,7 @@ test_arglist \ test_cd \ test_cdo \ test_changedtick \ + test_changelist \ test_channel \ test_charsearch \ test_charsearch_utf8 \ diff --git a/src/channel.c b/src/channel.c index 51b357129e..404219bddf 100644 --- a/src/channel.c +++ b/src/channel.c @@ -4493,7 +4493,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) return OK; if (tv->v_type != VAR_DICT) { - EMSG(_(e_invarg)); + EMSG(_(e_dictreq)); return FAIL; } dict = tv->vval.v_dict; @@ -4576,7 +4576,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) opt->jo_io_buf[part] = get_tv_number(item); if (opt->jo_io_buf[part] <= 0) { - EMSG2(_(e_invarg2), get_tv_string(item)); + EMSG3(_(e_invargNval), hi->hi_key, get_tv_string(item)); return FAIL; } if (buflist_findnr(opt->jo_io_buf[part]) == NULL) @@ -4625,7 +4625,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) *lp = get_tv_number(item); if (*lp < 0) { - EMSG2(_(e_invarg2), get_tv_string(item)); + EMSG3(_(e_invargNval), hi->hi_key, get_tv_string(item)); return FAIL; } } @@ -4636,7 +4636,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) opt->jo_set |= JO_CHANNEL; if (item->v_type != VAR_CHANNEL) { - EMSG2(_(e_invarg2), "channel"); + EMSG2(_(e_invargval), "channel"); return FAIL; } opt->jo_channel = item->vval.v_channel; @@ -4649,7 +4649,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) opt->jo_callback = get_callback(item, &opt->jo_partial); if (opt->jo_callback == NULL) { - EMSG2(_(e_invarg2), "callback"); + EMSG2(_(e_invargval), "callback"); return FAIL; } } @@ -4661,7 +4661,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) opt->jo_out_cb = get_callback(item, &opt->jo_out_partial); if (opt->jo_out_cb == NULL) { - EMSG2(_(e_invarg2), "out_cb"); + EMSG2(_(e_invargval), "out_cb"); return FAIL; } } @@ -4673,7 +4673,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) opt->jo_err_cb = get_callback(item, &opt->jo_err_partial); if (opt->jo_err_cb == NULL) { - EMSG2(_(e_invarg2), "err_cb"); + EMSG2(_(e_invargval), "err_cb"); return FAIL; } } @@ -4685,7 +4685,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) opt->jo_close_cb = get_callback(item, &opt->jo_close_partial); if (opt->jo_close_cb == NULL) { - EMSG2(_(e_invarg2), "close_cb"); + EMSG2(_(e_invargval), "close_cb"); return FAIL; } } @@ -4698,7 +4698,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) never = TRUE; else if (STRCMP(val, "auto") != 0) { - EMSG2(_(e_invarg2), "drop"); + EMSG3(_(e_invargNval), "drop", val); return FAIL; } opt->jo_drop_never = never; @@ -4711,7 +4711,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) opt->jo_exit_cb = get_callback(item, &opt->jo_exit_partial); if (opt->jo_exit_cb == NULL) { - EMSG2(_(e_invarg2), "exit_cb"); + EMSG2(_(e_invargval), "exit_cb"); return FAIL; } } @@ -4724,7 +4724,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) opt->jo_term_name = get_tv_string_chk(item); if (opt->jo_term_name == NULL) { - EMSG2(_(e_invarg2), "term_name"); + EMSG2(_(e_invargval), "term_name"); return FAIL; } } @@ -4735,7 +4735,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) val = get_tv_string(item); if (STRCMP(val, "open") != 0 && STRCMP(val, "close") != 0) { - EMSG2(_(e_invarg2), val); + EMSG3(_(e_invargNval), "term_finish", val); return FAIL; } opt->jo_set2 |= JO2_TERM_FINISH; @@ -4759,7 +4759,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) } if (p == NULL) { - EMSG2(_(e_invarg2), "term_opencmd"); + EMSG2(_(e_invargval), "term_opencmd"); return FAIL; } } @@ -4773,7 +4773,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) p = opt->jo_eof_chars = get_tv_string_chk(item); if (p == NULL) { - EMSG2(_(e_invarg2), "term_opencmd"); + EMSG2(_(e_invargval), "eof_chars"); return FAIL; } } @@ -4828,7 +4828,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) opt->jo_cwd = get_tv_string_buf_chk(item, opt->jo_cwd_buf); if (opt->jo_cwd == NULL || !mch_isdir(opt->jo_cwd)) { - EMSG2(_(e_invarg2), "cwd"); + EMSG2(_(e_invargval), "cwd"); return FAIL; } opt->jo_set |= JO2_CWD; @@ -4873,7 +4873,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) opt->jo_part = PART_OUT; else { - EMSG2(_(e_invarg2), val); + EMSG3(_(e_invargNval), "part", val); return FAIL; } } @@ -4893,7 +4893,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2) opt->jo_soe_buf); if (opt->jo_stoponexit == NULL) { - EMSG2(_(e_invarg2), "stoponexit"); + EMSG2(_(e_invargval), "stoponexit"); return FAIL; } } diff --git a/src/eval.c b/src/eval.c index 2c93e4ec4b..e5c1dbf88c 100644 --- a/src/eval.c +++ b/src/eval.c @@ -3596,7 +3596,13 @@ eval4(char_u **arg, typval_T *rettv, int evaluate) clear_tv(rettv); return FAIL; } - return typval_compare(rettv, &var2, type, type_is, ic, evaluate); + if (evaluate) + { + int ret = typval_compare(rettv, &var2, type, type_is, ic); + + clear_tv(&var2); + return ret; + } } return OK; @@ -8941,6 +8947,29 @@ assert_exception(typval_T *argvars) } } + void +assert_beeps(typval_T *argvars) +{ + char_u *cmd = get_tv_string_chk(&argvars[0]); + garray_T ga; + + called_vim_beep = FALSE; + suppress_errthrow = TRUE; + emsg_silent = FALSE; + do_cmdline_cmd(cmd); + if (!called_vim_beep) + { + prepare_assert_error(&ga); + ga_concat(&ga, (char_u *)"command did not beep: "); + ga_concat(&ga, cmd); + assert_error(&ga); + ga_clear(&ga); + } + + suppress_errthrow = FALSE; + emsg_on_display = FALSE; +} + void assert_fails(typval_T *argvars) { @@ -9068,229 +9097,210 @@ fill_assert_error( } } - +/* + * 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 */ int type_is, /* TRUE for "is" and "isnot" */ - int ic, /* ignore case */ - int evaluate) + int ic) /* ignore case */ { int i; varnumber_T n1, n2; char_u *s1, *s2; char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; - if (evaluate) + if (type_is && typ1->v_type != typ2->v_type) { - 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) { - /* For "is" a different type always means FALSE, for "notis" - * it means TRUE. */ - n1 = (type == TYPE_NEQUAL); - } - else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST) - { - if (type_is) - { - n1 = (typ1->v_type == typ2->v_type - && typ1->vval.v_list == typ2->vval.v_list); - if (type == TYPE_NEQUAL) - n1 = !n1; - } - else if (typ1->v_type != typ2->v_type - || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) - { - if (typ1->v_type != typ2->v_type) - EMSG(_("E691: Can only compare List with List")); - else - EMSG(_("E692: Invalid operation for List")); - clear_tv(typ1); - clear_tv(typ2); - return FAIL; - } - else - { - /* Compare two Lists for being equal or unequal. */ - n1 = list_equal(typ1->vval.v_list, typ2->vval.v_list, - ic, FALSE); - if (type == TYPE_NEQUAL) - n1 = !n1; - } - } - - else if (typ1->v_type == VAR_DICT || typ2->v_type == VAR_DICT) - { - if (type_is) - { - n1 = (typ1->v_type == typ2->v_type - && typ1->vval.v_dict == typ2->vval.v_dict); - if (type == TYPE_NEQUAL) - n1 = !n1; - } - else if (typ1->v_type != typ2->v_type - || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) - { - if (typ1->v_type != typ2->v_type) - EMSG(_("E735: Can only compare Dictionary with Dictionary")); - else - EMSG(_("E736: Invalid operation for Dictionary")); - clear_tv(typ1); - clear_tv(typ2); - return FAIL; - } - else - { - /* Compare two Dictionaries for being equal or unequal. */ - n1 = dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, - ic, FALSE); - if (type == TYPE_NEQUAL) - n1 = !n1; - } - } - - else if (typ1->v_type == VAR_FUNC || typ2->v_type == VAR_FUNC - || typ1->v_type == VAR_PARTIAL || typ2->v_type == VAR_PARTIAL) - { - if (type != TYPE_EQUAL && type != TYPE_NEQUAL) - { - EMSG(_("E694: Invalid operation for Funcrefs")); - clear_tv(typ1); - clear_tv(typ2); - return FAIL; - } - if ((typ1->v_type == VAR_PARTIAL - && typ1->vval.v_partial == NULL) - || (typ2->v_type == VAR_PARTIAL - && typ2->vval.v_partial == NULL)) - /* when a partial is NULL assume not equal */ - n1 = FALSE; - else if (type_is) - { - if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC) - /* strings are considered the same if their value is - * the same */ - n1 = tv_equal(typ1, typ2, ic, FALSE); - else if (typ1->v_type == VAR_PARTIAL - && typ2->v_type == VAR_PARTIAL) - n1 = (typ1->vval.v_partial == typ2->vval.v_partial); - else - n1 = FALSE; - } - else - n1 = tv_equal(typ1, typ2, ic, FALSE); + n1 = (typ1->v_type == typ2->v_type + && typ1->vval.v_list == typ2->vval.v_list); if (type == TYPE_NEQUAL) n1 = !n1; } - -#ifdef FEAT_FLOAT - /* - * If one of the two variables is a float, compare as a float. - * When using "=~" or "!~", always compare as string. - */ - else if ((typ1->v_type == VAR_FLOAT || typ2->v_type == VAR_FLOAT) - && type != TYPE_MATCH && type != TYPE_NOMATCH) + else if (typ1->v_type != typ2->v_type + || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) { - float_T f1, f2; - - if (typ1->v_type == VAR_FLOAT) - f1 = typ1->vval.v_float; + if (typ1->v_type != typ2->v_type) + EMSG(_("E691: Can only compare List with List")); else - f1 = get_tv_number(typ1); - if (typ2->v_type == VAR_FLOAT) - f2 = typ2->vval.v_float; - else - f2 = get_tv_number(typ2); - n1 = FALSE; - switch (type) - { - case TYPE_EQUAL: n1 = (f1 == f2); break; - case TYPE_NEQUAL: n1 = (f1 != f2); break; - case TYPE_GREATER: n1 = (f1 > f2); break; - case TYPE_GEQUAL: n1 = (f1 >= f2); break; - case TYPE_SMALLER: n1 = (f1 < f2); break; - case TYPE_SEQUAL: n1 = (f1 <= f2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; /* avoid gcc warning */ - } - } -#endif - - /* - * If one of the two variables is a number, compare as a number. - * When using "=~" or "!~", always compare as string. - */ - else if ((typ1->v_type == VAR_NUMBER || typ2->v_type == VAR_NUMBER) - && type != TYPE_MATCH && type != TYPE_NOMATCH) - { - n1 = get_tv_number(typ1); - n2 = get_tv_number(typ2); - switch (type) - { - case TYPE_EQUAL: n1 = (n1 == n2); break; - case TYPE_NEQUAL: n1 = (n1 != n2); break; - case TYPE_GREATER: n1 = (n1 > n2); break; - case TYPE_GEQUAL: n1 = (n1 >= n2); break; - case TYPE_SMALLER: n1 = (n1 < n2); break; - case TYPE_SEQUAL: n1 = (n1 <= n2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; /* avoid gcc warning */ - } + EMSG(_("E692: Invalid operation for List")); + clear_tv(typ1); + return FAIL; } else { - s1 = get_tv_string_buf(typ1, buf1); - s2 = get_tv_string_buf(typ2, buf2); - if (type != TYPE_MATCH && type != TYPE_NOMATCH) - i = ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2); - else - i = 0; - n1 = FALSE; - switch (type) - { - case TYPE_EQUAL: n1 = (i == 0); break; - case TYPE_NEQUAL: n1 = (i != 0); break; - case TYPE_GREATER: n1 = (i > 0); break; - case TYPE_GEQUAL: n1 = (i >= 0); break; - case TYPE_SMALLER: n1 = (i < 0); break; - case TYPE_SEQUAL: n1 = (i <= 0); break; - - case TYPE_MATCH: - case TYPE_NOMATCH: - n1 = pattern_match(s2, s1, ic); - if (type == TYPE_NOMATCH) - n1 = !n1; - break; - - case TYPE_UNKNOWN: break; /* avoid gcc warning */ - } + /* Compare two Lists for being equal or unequal. */ + n1 = list_equal(typ1->vval.v_list, typ2->vval.v_list, + ic, FALSE); + if (type == TYPE_NEQUAL) + n1 = !n1; } - clear_tv(typ1); - clear_tv(typ2); - typ1->v_type = VAR_NUMBER; - typ1->vval.v_number = n1; } + + else if (typ1->v_type == VAR_DICT || typ2->v_type == VAR_DICT) + { + if (type_is) + { + n1 = (typ1->v_type == typ2->v_type + && typ1->vval.v_dict == typ2->vval.v_dict); + if (type == TYPE_NEQUAL) + n1 = !n1; + } + else if (typ1->v_type != typ2->v_type + || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) + { + if (typ1->v_type != typ2->v_type) + EMSG(_("E735: Can only compare Dictionary with Dictionary")); + else + EMSG(_("E736: Invalid operation for Dictionary")); + clear_tv(typ1); + return FAIL; + } + else + { + /* Compare two Dictionaries for being equal or unequal. */ + n1 = dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, + ic, FALSE); + if (type == TYPE_NEQUAL) + n1 = !n1; + } + } + + else if (typ1->v_type == VAR_FUNC || typ2->v_type == VAR_FUNC + || typ1->v_type == VAR_PARTIAL || typ2->v_type == VAR_PARTIAL) + { + if (type != TYPE_EQUAL && type != TYPE_NEQUAL) + { + EMSG(_("E694: Invalid operation for Funcrefs")); + clear_tv(typ1); + return FAIL; + } + if ((typ1->v_type == VAR_PARTIAL + && typ1->vval.v_partial == NULL) + || (typ2->v_type == VAR_PARTIAL + && typ2->vval.v_partial == NULL)) + /* when a partial is NULL assume not equal */ + n1 = FALSE; + else if (type_is) + { + if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC) + /* strings are considered the same if their value is + * the same */ + n1 = tv_equal(typ1, typ2, ic, FALSE); + else if (typ1->v_type == VAR_PARTIAL + && typ2->v_type == VAR_PARTIAL) + n1 = (typ1->vval.v_partial == typ2->vval.v_partial); + else + n1 = FALSE; + } + else + n1 = tv_equal(typ1, typ2, ic, FALSE); + if (type == TYPE_NEQUAL) + n1 = !n1; + } + +#ifdef FEAT_FLOAT + /* + * If one of the two variables is a float, compare as a float. + * When using "=~" or "!~", always compare as string. + */ + else if ((typ1->v_type == VAR_FLOAT || typ2->v_type == VAR_FLOAT) + && type != TYPE_MATCH && type != TYPE_NOMATCH) + { + float_T f1, f2; + + if (typ1->v_type == VAR_FLOAT) + f1 = typ1->vval.v_float; + else + f1 = get_tv_number(typ1); + if (typ2->v_type == VAR_FLOAT) + f2 = typ2->vval.v_float; + else + f2 = get_tv_number(typ2); + n1 = FALSE; + switch (type) + { + case TYPE_EQUAL: n1 = (f1 == f2); break; + case TYPE_NEQUAL: n1 = (f1 != f2); break; + case TYPE_GREATER: n1 = (f1 > f2); break; + case TYPE_GEQUAL: n1 = (f1 >= f2); break; + case TYPE_SMALLER: n1 = (f1 < f2); break; + case TYPE_SEQUAL: n1 = (f1 <= f2); break; + case TYPE_UNKNOWN: + case TYPE_MATCH: + case TYPE_NOMATCH: break; /* avoid gcc warning */ + } + } +#endif + + /* + * If one of the two variables is a number, compare as a number. + * When using "=~" or "!~", always compare as string. + */ + else if ((typ1->v_type == VAR_NUMBER || typ2->v_type == VAR_NUMBER) + && type != TYPE_MATCH && type != TYPE_NOMATCH) + { + n1 = get_tv_number(typ1); + n2 = get_tv_number(typ2); + switch (type) + { + case TYPE_EQUAL: n1 = (n1 == n2); break; + case TYPE_NEQUAL: n1 = (n1 != n2); break; + case TYPE_GREATER: n1 = (n1 > n2); break; + case TYPE_GEQUAL: n1 = (n1 >= n2); break; + case TYPE_SMALLER: n1 = (n1 < n2); break; + case TYPE_SEQUAL: n1 = (n1 <= n2); break; + case TYPE_UNKNOWN: + case TYPE_MATCH: + case TYPE_NOMATCH: break; /* avoid gcc warning */ + } + } + else + { + s1 = get_tv_string_buf(typ1, buf1); + s2 = get_tv_string_buf(typ2, buf2); + if (type != TYPE_MATCH && type != TYPE_NOMATCH) + i = ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2); + else + i = 0; + n1 = FALSE; + switch (type) + { + case TYPE_EQUAL: n1 = (i == 0); break; + case TYPE_NEQUAL: n1 = (i != 0); break; + case TYPE_GREATER: n1 = (i > 0); break; + case TYPE_GEQUAL: n1 = (i >= 0); break; + case TYPE_SMALLER: n1 = (i < 0); break; + case TYPE_SEQUAL: n1 = (i <= 0); break; + + case TYPE_MATCH: + case TYPE_NOMATCH: + n1 = pattern_match(s2, s1, ic); + if (type == TYPE_NOMATCH) + n1 = !n1; + break; + + case TYPE_UNKNOWN: break; /* avoid gcc warning */ + } + } + clear_tv(typ1); + typ1->v_type = VAR_NUMBER; + typ1->vval.v_number = n1; + return OK; } - int -typval_copy(typ1, typ2) - typval_T *typ1; - typval_T *typ2; -{ - if (typ2 == NULL) - rettv_list_alloc(typ2); - - if (typ1 != NULL && typ2 != NULL) - return item_copy(typ1, typ2, TRUE, 0); - - return FAIL; -} - char_u * typval_tostring(arg) typval_T *arg; diff --git a/src/evalfunc.c b/src/evalfunc.c index 074ca719c8..9ff0cd24db 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -44,6 +44,7 @@ static void f_argc(typval_T *argvars, typval_T *rettv); static void f_argidx(typval_T *argvars, typval_T *rettv); static void f_arglistid(typval_T *argvars, typval_T *rettv); static void f_argv(typval_T *argvars, typval_T *rettv); +static void f_assert_beeps(typval_T *argvars, typval_T *rettv); static void f_assert_equal(typval_T *argvars, typval_T *rettv); static void f_assert_exception(typval_T *argvars, typval_T *rettv); static void f_assert_fails(typval_T *argvars, typval_T *rettv); @@ -164,6 +165,7 @@ static void f_get(typval_T *argvars, typval_T *rettv); static void f_getbufinfo(typval_T *argvars, typval_T *rettv); static void f_getbufline(typval_T *argvars, typval_T *rettv); static void f_getbufvar(typval_T *argvars, typval_T *rettv); +static void f_getchangelist(typval_T *argvars, typval_T *rettv); static void f_getchar(typval_T *argvars, typval_T *rettv); static void f_getcharmod(typval_T *argvars, typval_T *rettv); static void f_getcharsearch(typval_T *argvars, typval_T *rettv); @@ -483,6 +485,7 @@ static struct fst #ifdef FEAT_FLOAT {"asin", 1, 1, f_asin}, /* WJMc */ #endif + {"assert_beeps", 1, 2, f_assert_beeps}, {"assert_equal", 2, 3, f_assert_equal}, {"assert_exception", 1, 2, f_assert_exception}, {"assert_fails", 1, 2, f_assert_fails}, @@ -605,6 +608,7 @@ static struct fst {"getbufinfo", 0, 1, f_getbufinfo}, {"getbufline", 2, 3, f_getbufline}, {"getbufvar", 2, 3, f_getbufvar}, + {"getchangelist", 1, 1, f_getchangelist}, {"getchar", 0, 1, f_getchar}, {"getcharmod", 0, 0, f_getcharmod}, {"getcharsearch", 0, 0, f_getcharsearch}, @@ -1274,6 +1278,15 @@ f_argv(typval_T *argvars, typval_T *rettv) alist_name(&ARGLIST[idx]), -1); } +/* + * "assert_beeps(cmd [, error])" function + */ + static void +f_assert_beeps(typval_T *argvars, typval_T *rettv UNUSED) +{ + assert_beeps(argvars); +} + /* * "assert_equal(expected, actual[, msg])" function */ @@ -4335,6 +4348,61 @@ f_getbufvar(typval_T *argvars, typval_T *rettv) --emsg_off; } +/* + * "getchangelist()" function + */ + static void +f_getchangelist(typval_T *argvars, typval_T *rettv) +{ +#ifdef FEAT_JUMPLIST + buf_T *buf; + int i; + list_T *l; + dict_T *d; +#endif + + if (rettv_list_alloc(rettv) != OK) + return; + +#ifdef FEAT_JUMPLIST + (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ + ++emsg_off; + buf = get_buf_tv(&argvars[0], FALSE); + --emsg_off; + if (buf == NULL) + return; + + l = list_alloc(); + if (l == NULL) + return; + + if (list_append_list(rettv->vval.v_list, l) == FAIL) + return; + /* + * The current window change list index tracks only the position in the + * current buffer change list. For other buffers, use the change list + * length as the current index. + */ + list_append_number(rettv->vval.v_list, + (varnumber_T)((buf == curwin->w_buffer) + ? curwin->w_changelistidx : buf->b_changelistlen)); + + for (i = 0; i < buf->b_changelistlen; ++i) + { + if (buf->b_changelist[i].lnum == 0) + continue; + if ((d = dict_alloc()) == NULL) + return; + if (list_append_dict(l, d) == FAIL) + return; + dict_add_nr_str(d, "lnum", (long)buf->b_changelist[i].lnum, NULL); + dict_add_nr_str(d, "col", (long)buf->b_changelist[i].col, NULL); +# ifdef FEAT_VIRTUALEDIT + dict_add_nr_str(d, "coladd", (long)buf->b_changelist[i].coladd, NULL); +# endif + } +#endif +} /* * "getchar()" function */ @@ -4854,13 +4922,12 @@ f_getjumplist(typval_T *argvars, typval_T *rettv) return; list_append_number(rettv->vval.v_list, (varnumber_T)wp->w_jumplistidx); - cleanup_jumplist(wp); + cleanup_jumplist(wp, TRUE); + for (i = 0; i < wp->w_jumplistlen; ++i) { if (wp->w_jumplist[i].fmark.mark.lnum == 0) continue; - if (wp->w_jumplist[i].fmark.fnum == 0) - fname2fnum(&wp->w_jumplist[i]); if ((d = dict_alloc()) == NULL) return; if (list_append_dict(l, d) == FAIL) diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 788e9eb828..a4d6221b01 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -6918,7 +6918,7 @@ fix_help_buffer(void) copy_option_part(&p, NameBuff, MAXPATHL, ","); mustfree = FALSE; rt = vim_getenv((char_u *)"VIMRUNTIME", &mustfree); - if (fullpathcmp(rt, NameBuff, FALSE) != FPC_SAME) + if (rt != NULL && fullpathcmp(rt, NameBuff, FALSE) != FPC_SAME) { int fcount; char_u **fnames; diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c index 8c28a50832..64e482411b 100644 --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -988,23 +988,20 @@ debuggy_find( } else { - typval_T val3; - - if (typval_copy(bp->dbg_val, &val3) == OK) + if (typval_compare(tv, bp->dbg_val, TYPE_EQUAL, + TRUE, FALSE) == OK + && tv->vval.v_number == FALSE) { - if (typval_compare(tv, &val3, TYPE_EQUAL, - TRUE, FALSE, TRUE) == OK - && tv->vval.v_number == FALSE) - { - typval_T *v; + typval_T *v; - line = TRUE; - debug_oldval = typval_tostring(bp->dbg_val); - v = eval_expr(bp->dbg_name, NULL); - debug_newval = typval_tostring(v); - free_tv(bp->dbg_val); - bp->dbg_val = v; - } + line = TRUE; + debug_oldval = typval_tostring(bp->dbg_val); + /* Need to evaluate again, typval_compare() overwrites + * "tv". */ + v = eval_expr(bp->dbg_name, NULL); + debug_newval = typval_tostring(v); + free_tv(bp->dbg_val); + bp->dbg_val = v; } free_tv(tv); } diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 59058e7dd2..663e1b137c 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -2887,8 +2887,18 @@ do_one_cmd( } #endif + /* The :try command saves the emsg_silent flag, reset it here when + * ":silent! try" was used, it should only apply to :try itself. */ + if (ea.cmdidx == CMD_try && did_esilent > 0) + { + emsg_silent -= did_esilent; + if (emsg_silent < 0) + emsg_silent = 0; + did_esilent = 0; + } + /* - * 7. Switch on command name. + * 7. Execute the command. * * The "ea" structure holds the arguments that can be used. */ diff --git a/src/globals.h b/src/globals.h index 6288cb32e4..23bfb41d53 100644 --- a/src/globals.h +++ b/src/globals.h @@ -181,6 +181,7 @@ EXTERN dict_T globvardict; /* Dictionary with g: variables */ EXTERN int did_emsg; /* set by emsg() when the message is displayed or thrown */ #ifdef FEAT_EVAL +EXTERN int called_vim_beep; /* set if vim_beep() is called */ EXTERN int did_uncaught_emsg; /* emsg() was called and did not cause an exception */ #endif @@ -1441,6 +1442,8 @@ EXTERN char_u e_interr[] INIT(= N_("Interrupted")); EXTERN char_u e_invaddr[] INIT(= N_("E14: Invalid address")); EXTERN char_u e_invarg[] INIT(= N_("E474: Invalid argument")); EXTERN char_u e_invarg2[] INIT(= N_("E475: Invalid argument: %s")); +EXTERN char_u e_invargval[] INIT(= N_("E475: Invalid value for argument %s")); +EXTERN char_u e_invargNval[] INIT(= N_("E475: Invalid value for argument %s: %s")); #ifdef FEAT_EVAL EXTERN char_u e_invexpr2[] INIT(= N_("E15: Invalid expression: %s")); #endif diff --git a/src/mark.c b/src/mark.c index 9ebc9c24c4..dd714d800d 100644 --- a/src/mark.c +++ b/src/mark.c @@ -221,7 +221,7 @@ movemark(int count) pos_T *pos; xfmark_T *jmp; - cleanup_jumplist(curwin); + cleanup_jumplist(curwin, TRUE); if (curwin->w_jumplistlen == 0) /* nothing to jump to */ return (pos_T *)NULL; @@ -891,7 +891,7 @@ ex_jumps(exarg_T *eap UNUSED) int i; char_u *name; - cleanup_jumplist(curwin); + cleanup_jumplist(curwin, TRUE); /* Highlight title */ MSG_PUTS_TITLE(_("\n jump line col file/text")); @@ -899,8 +899,6 @@ ex_jumps(exarg_T *eap UNUSED) { if (curwin->w_jumplist[i].fmark.mark.lnum != 0) { - if (curwin->w_jumplist[i].fmark.fnum == 0) - fname2fnum(&curwin->w_jumplist[i]); name = fm_getname(&curwin->w_jumplist[i].fmark, 16); if (name == NULL) /* file name not available */ continue; @@ -1303,13 +1301,28 @@ mark_col_adjust( /* * When deleting lines, this may create duplicate marks in the * jumplist. They will be removed here for the specified window. + * When "loadfiles" is TRUE first ensure entries have the "fnum" field set + * (this may be a bit slow). */ void -cleanup_jumplist(win_T *wp) +cleanup_jumplist(win_T *wp, int loadfiles) { int i; int from, to; + if (loadfiles) + { + /* If specified, load all the files from the jump list. This is + * needed to properly clean up duplicate entries, but will take some + * time. */ + for (i = 0; i < wp->w_jumplistlen; ++i) + { + if ((wp->w_jumplist[i].fmark.fnum == 0) && + (wp->w_jumplist[i].fmark.mark.lnum != 0)) + fname2fnum(&wp->w_jumplist[i]); + } + } + to = 0; for (from = 0; from < wp->w_jumplistlen; ++from) { @@ -1738,7 +1751,7 @@ write_viminfo_filemarks(FILE *fp) /* Write the jumplist with -' */ fputs(_("\n# Jumplist (newest first):\n"), fp); setpcmark(); /* add current cursor position */ - cleanup_jumplist(curwin); + cleanup_jumplist(curwin, FALSE); vi_idx = 0; idx = curwin->w_jumplistlen - 1; for (i = 0; i < JUMPLISTSIZE; ++i) diff --git a/src/misc1.c b/src/misc1.c index cd8dedd568..c2cdadd252 100644 --- a/src/misc1.c +++ b/src/misc1.c @@ -3694,6 +3694,10 @@ beep_flush(void) vim_beep( unsigned val) /* one of the BO_ values, e.g., BO_OPER */ { +#ifdef FEAT_EVAL + called_vim_beep = TRUE; +#endif + if (emsg_silent == 0) { if (!((bo_flags & val) || (bo_flags & BO_ALL))) @@ -3724,8 +3728,9 @@ vim_beep( #endif } - /* When 'verbose' is set and we are sourcing a script or executing a - * function give the user a hint where the beep comes from. */ + /* When 'debug' contains "beep" produce a message. If we are sourcing + * a script or executing a function give the user a hint where the beep + * comes from. */ if (vim_strchr(p_debug, 'e') != NULL) { msg_source(HL_ATTR(HLF_W)); diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 855946ce29..ba7dcec59c 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -127,10 +127,10 @@ void assert_inrange(typval_T *argvars); void assert_bool(typval_T *argvars, int isTrue); void assert_report(typval_T *argvars); void assert_exception(typval_T *argvars); +void assert_beeps(typval_T *argvars); void assert_fails(typval_T *argvars); void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, typval_T *exp_tv, typval_T *got_tv, assert_type_T atype); -int typval_compare(typval_T *typ1, typval_T *typ2, exptype_T type, int type_is, int ic, int evaluate); -int typval_copy(typval_T *typ1, typval_T *typ2); +int typval_compare(typval_T *typ1, typval_T *typ2, exptype_T type, int type_is, int ic); char_u *typval_tostring(typval_T *arg); int var_exists(char_u *var); int modify_fname(char_u *src, int *usedlen, char_u **fnamep, char_u **bufp, int *fnamelen); diff --git a/src/proto/mark.pro b/src/proto/mark.pro index aa6f44efcc..448b452739 100644 --- a/src/proto/mark.pro +++ b/src/proto/mark.pro @@ -24,7 +24,7 @@ void mark_adjust_nofold(linenr_T line1, linenr_T line2, long amount, long amount void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount); void copy_jumplist(win_T *from, win_T *to); void free_jumplist(win_T *wp); -void cleanup_jumplist(win_T *wp); +void cleanup_jumplist(win_T *wp, int loadfiles); void set_last_cursor(win_T *win); void free_all_marks(void); int read_viminfo_filemark(vir_T *virp, int force); diff --git a/src/regexp.c b/src/regexp.c index d07391eefb..b1cad99c33 100644 --- a/src/regexp.c +++ b/src/regexp.c @@ -5585,9 +5585,14 @@ regmatch( { #ifdef FEAT_MBYTE if (has_mbyte) + { + char_u *line = + reg_getline(behind_pos.rs_u.pos.lnum); + rp->rs_un.regsave.rs_u.pos.col -= - (*mb_head_off)(regline, regline + (*mb_head_off)(line, line + rp->rs_un.regsave.rs_u.pos.col - 1) + 1; + } else #endif --rp->rs_un.regsave.rs_u.pos.col; diff --git a/src/screen.c b/src/screen.c index c9e3bcafc8..6b3e96ffcd 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1176,6 +1176,11 @@ win_update(win_T *wp) */ if (term_update_window(wp) == OK) { +# ifdef FEAT_MENU + /* Draw the window toolbar, if there is one. */ + if (winbar_height(wp) > 0) + redraw_win_toolbar(wp); +# endif wp->w_redr_type = 0; return; } diff --git a/src/terminal.c b/src/terminal.c index f1f288a17b..fce4ee75eb 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -475,9 +475,12 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit) channel_set_nonblock(term->tl_job->jv_channel, PART_IN); #ifdef FEAT_AUTOCMD - ++curbuf->b_locked; - apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf); - --curbuf->b_locked; + if (!opt->jo_hidden) + { + ++curbuf->b_locked; + apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf); + --curbuf->b_locked; + } #endif if (old_curbuf != NULL) @@ -2400,8 +2403,8 @@ term_update_window(win_T *wp) else pos.col = 0; - screen_line(wp->w_winrow + pos.row, wp->w_wincol, - pos.col, wp->w_width, FALSE); + screen_line(wp->w_winrow + pos.row + winbar_height(wp), + wp->w_wincol, pos.col, wp->w_width, FALSE); } term->tl_dirty_row_start = MAX_ROW; term->tl_dirty_row_end = 0; diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index 2d98fa6a11..e3230c5540 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -75,6 +75,7 @@ NEW_TESTS = test_arabic.res \ test_breakindent.res \ test_bufwintabinfo.res \ test_cdo.res \ + test_changelist.res \ test_channel.res \ test_charsearch.res \ test_cindent.res \ diff --git a/src/testdir/test_assert.vim b/src/testdir/test_assert.vim index 8359eb11c8..29c853409e 100644 --- a/src/testdir/test_assert.vim +++ b/src/testdir/test_assert.vim @@ -111,6 +111,16 @@ func Test_assert_fail_fails() call remove(v:errors, 0) endfunc +func Test_assert_beeps() + new + call assert_beeps('normal h') + + call assert_beeps('normal 0') + call assert_match("command did not beep: normal 0", v:errors[0]) + call remove(v:errors, 0) + bwipe +endfunc + func Test_assert_inrange() call assert_inrange(7, 7, 7) call assert_inrange(5, 7, 5) diff --git a/src/testdir/test_changelist.vim b/src/testdir/test_changelist.vim new file mode 100644 index 0000000000..dd6ea9600c --- /dev/null +++ b/src/testdir/test_changelist.vim @@ -0,0 +1,48 @@ +" Tests for the changelist functionality + +" Tests for the getchangelist() function +func Test_getchangelist() + if !has("jumplist") + return + endif + + bwipe! + enew + call assert_equal([], getchangelist(10)) + call assert_equal([[], 0], getchangelist('%')) + + call writefile(['line1', 'line2', 'line3'], 'Xfile1.txt') + call writefile(['line1', 'line2', 'line3'], 'Xfile2.txt') + + edit Xfile1.txt + exe "normal 1Goline\u1.1" + exe "normal 3Goline\u2.1" + exe "normal 5Goline\u3.1" + normal g; + call assert_equal([[ + \ {'lnum' : 2, 'col' : 4, 'coladd' : 0}, + \ {'lnum' : 4, 'col' : 4, 'coladd' : 0}, + \ {'lnum' : 6, 'col' : 4, 'coladd' : 0}], 2], + \ getchangelist('%')) + + hide edit Xfile2.txt + exe "normal 1GOline\u1.0" + exe "normal 2Goline\u2.0" + call assert_equal([[ + \ {'lnum' : 1, 'col' : 6, 'coladd' : 0}, + \ {'lnum' : 3, 'col' : 6, 'coladd' : 0}], 2], + \ getchangelist('%')) + hide enew + + call assert_equal([[ + \ {'lnum' : 2, 'col' : 4, 'coladd' : 0}, + \ {'lnum' : 4, 'col' : 4, 'coladd' : 0}, + \ {'lnum' : 6, 'col' : 4, 'coladd' : 0}], 3], getchangelist(2)) + call assert_equal([[ + \ {'lnum' : 1, 'col' : 6, 'coladd' : 0}, + \ {'lnum' : 3, 'col' : 6, 'coladd' : 0}], 2], getchangelist(3)) + + bwipe! + call delete('Xfile1.txt') + call delete('Xfile2.txt') +endfunc diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim index 92e1ec5335..12222303d3 100644 --- a/src/testdir/test_eval_stuff.vim +++ b/src/testdir/test_eval_stuff.vim @@ -11,3 +11,17 @@ endfunction func Test_catch_return_with_error() call assert_equal(1, s:foo()) endfunc + +func Test_nocatch_restore_silent_emsg() + silent! try + throw 1 + catch + endtry + echoerr 'wrong' + let c1 = nr2char(screenchar(&lines, 1)) + let c2 = nr2char(screenchar(&lines, 2)) + let c3 = nr2char(screenchar(&lines, 3)) + let c4 = nr2char(screenchar(&lines, 4)) + let c5 = nr2char(screenchar(&lines, 5)) + call assert_equal('wrong', c1 . c2 . c3 . c4 . c5) +endfunc diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim index 27c589783a..2d1446bfd6 100644 --- a/src/testdir/test_normal.vim +++ b/src/testdir/test_normal.vim @@ -2177,6 +2177,8 @@ endfunc func! Test_normal45_drop() if !has('dnd') + " The ~ register does not exist + call assert_beeps('norm! "~') return endif diff --git a/src/version.c b/src/version.c index ed7c51c3cb..690510583c 100644 --- a/src/version.c +++ b/src/version.c @@ -786,6 +786,28 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1520, +/**/ + 1519, +/**/ + 1518, +/**/ + 1517, +/**/ + 1516, +/**/ + 1515, +/**/ + 1514, +/**/ + 1513, +/**/ + 1512, +/**/ + 1511, +/**/ + 1510, /**/ 1509, /**/