aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/README.md4
-rw-r--r--src/nvim/buffer_defs.h6
-rw-r--r--src/nvim/cmdexpand.c4
-rw-r--r--src/nvim/diff.c431
-rw-r--r--src/nvim/drawline.c11
-rw-r--r--src/nvim/eval.c27
-rw-r--r--src/nvim/eval.lua5
-rw-r--r--src/nvim/eval/funcs.c127
-rw-r--r--src/nvim/eval/typval.c17
-rw-r--r--src/nvim/eval/typval.h5
-rw-r--r--src/nvim/eval/userfunc.c4
-rw-r--r--src/nvim/ex_cmds.c11
-rw-r--r--src/nvim/ex_docmd.c9
-rw-r--r--src/nvim/ex_eval.c7
-rw-r--r--src/nvim/ex_getln.c2
-rw-r--r--src/nvim/fileio.c48
-rw-r--r--src/nvim/globals.h6
-rw-r--r--src/nvim/highlight_group.c2
-rw-r--r--src/nvim/indent.c80
-rw-r--r--src/nvim/linematch.c376
-rw-r--r--src/nvim/linematch.h10
-rw-r--r--src/nvim/lua/xdiff.c138
-rw-r--r--src/nvim/mapping.c2
-rw-r--r--src/nvim/match.c2
-rw-r--r--src/nvim/message.c7
-rw-r--r--src/nvim/mouse.c9
-rw-r--r--src/nvim/ops.c2
-rw-r--r--src/nvim/option.c11
-rw-r--r--src/nvim/options.lua2
-rw-r--r--src/nvim/optionstr.c10
-rw-r--r--src/nvim/path.c2
-rw-r--r--src/nvim/plines.c4
-rw-r--r--src/nvim/quickfix.c6
-rw-r--r--src/nvim/regexp.c57
-rw-r--r--src/nvim/regexp_bt.c11
-rw-r--r--src/nvim/regexp_nfa.c59
-rw-r--r--src/nvim/runtime.c2
-rw-r--r--src/nvim/search.c2
-rw-r--r--src/nvim/shada.c18
-rw-r--r--src/nvim/spell.c4
-rw-r--r--src/nvim/spellfile.c2
-rw-r--r--src/nvim/syntax.c4
-rw-r--r--src/nvim/tag.c4
-rw-r--r--src/nvim/testdir/check.vim3
-rw-r--r--src/nvim/testdir/samples/re.freeze.txt6
-rw-r--r--src/nvim/testdir/shared.vim15
-rw-r--r--src/nvim/testdir/test49.ok44
-rw-r--r--src/nvim/testdir/test49.vim5284
-rw-r--r--src/nvim/testdir/test_arglist.vim5
-rw-r--r--src/nvim/testdir/test_assert.vim60
-rw-r--r--src/nvim/testdir/test_autochdir.vim4
-rw-r--r--src/nvim/testdir/test_autocmd.vim8
-rw-r--r--src/nvim/testdir/test_backup.vim18
-rw-r--r--src/nvim/testdir/test_blob.vim4
-rw-r--r--src/nvim/testdir/test_breakindent.vim157
-rw-r--r--src/nvim/testdir/test_buffer.vim4
-rw-r--r--src/nvim/testdir/test_bufline.vim32
-rw-r--r--src/nvim/testdir/test_cd.vim32
-rw-r--r--src/nvim/testdir/test_charsearch.vim61
-rw-r--r--src/nvim/testdir/test_clientserver.vim7
-rw-r--r--src/nvim/testdir/test_cmdline.vim24
-rw-r--r--src/nvim/testdir/test_cpoptions.vim927
-rw-r--r--src/nvim/testdir/test_display.vim2
-rw-r--r--src/nvim/testdir/test_edit.vim146
-rw-r--r--src/nvim/testdir/test_eval_stuff.vim9
-rw-r--r--src/nvim/testdir/test_ex_mode.vim1
-rw-r--r--src/nvim/testdir/test_excmd.vim4
-rw-r--r--src/nvim/testdir/test_exists.vim6
-rw-r--r--src/nvim/testdir/test_expand.vim18
-rw-r--r--src/nvim/testdir/test_expand_func.vim1
-rw-r--r--src/nvim/testdir/test_expr.vim46
-rw-r--r--src/nvim/testdir/test_fixeol.vim67
-rw-r--r--src/nvim/testdir/test_functions.vim136
-rw-r--r--src/nvim/testdir/test_global.vim2
-rw-r--r--src/nvim/testdir/test_increment.vim35
-rw-r--r--src/nvim/testdir/test_let.vim2
-rw-r--r--src/nvim/testdir/test_listdict.vim16
-rw-r--r--src/nvim/testdir/test_mapping.vim24
-rw-r--r--src/nvim/testdir/test_marks.vim2
-rw-r--r--src/nvim/testdir/test_match.vim16
-rw-r--r--src/nvim/testdir/test_matchfuzzy.vim18
-rw-r--r--src/nvim/testdir/test_menu.vim3
-rw-r--r--src/nvim/testdir/test_method.vim4
-rw-r--r--src/nvim/testdir/test_normal.vim248
-rw-r--r--src/nvim/testdir/test_options.vim2
-rw-r--r--src/nvim/testdir/test_partial.vim5
-rw-r--r--src/nvim/testdir/test_quickfix.vim21
-rw-r--r--src/nvim/testdir/test_random.vim10
-rw-r--r--src/nvim/testdir/test_regexp_latin.vim55
-rw-r--r--src/nvim/testdir/test_registers.vim18
-rw-r--r--src/nvim/testdir/test_reltime.vim4
-rw-r--r--src/nvim/testdir/test_search.vim34
-rw-r--r--src/nvim/testdir/test_selectmode.vim3
-rw-r--r--src/nvim/testdir/test_signals.vim33
-rw-r--r--src/nvim/testdir/test_signs.vim10
-rw-r--r--src/nvim/testdir/test_sort.vim2
-rw-r--r--src/nvim/testdir/test_spell.vim4
-rw-r--r--src/nvim/testdir/test_startup.vim8
-rw-r--r--src/nvim/testdir/test_startup_utf8.vim4
-rw-r--r--src/nvim/testdir/test_substitute.vim88
-rw-r--r--src/nvim/testdir/test_syntax.vim8
-rw-r--r--src/nvim/testdir/test_tabpage.vim15
-rw-r--r--src/nvim/testdir/test_tagfunc.vim2
-rw-r--r--src/nvim/testdir/test_tagjump.vim60
-rw-r--r--src/nvim/testdir/test_taglist.vim6
-rw-r--r--src/nvim/testdir/test_textformat.vim14
-rw-r--r--src/nvim/testdir/test_timers.vim4
-rw-r--r--src/nvim/testdir/test_trycatch.vim23
-rw-r--r--src/nvim/testdir/test_utf8.vim11
-rw-r--r--src/nvim/testdir/test_vartabs.vim2
-rw-r--r--src/nvim/testdir/test_vimscript.vim5003
-rw-r--r--src/nvim/testdir/test_virtualedit.vim56
-rw-r--r--src/nvim/testdir/test_visual.vim9
-rw-r--r--src/nvim/testdir/test_winbuf_close.vim2
-rw-r--r--src/nvim/testdir/test_window_cmd.vim37
-rw-r--r--src/nvim/testdir/test_window_id.vim12
-rw-r--r--src/nvim/testdir/test_writefile.vim187
-rw-r--r--src/nvim/testing.c103
-rw-r--r--src/nvim/usercmd.c4
-rw-r--r--src/nvim/version.c2
-rw-r--r--src/nvim/window.c6
121 files changed, 8818 insertions, 6100 deletions
diff --git a/src/nvim/README.md b/src/nvim/README.md
index 91fb3ca2f6..e710c3ef58 100644
--- a/src/nvim/README.md
+++ b/src/nvim/README.md
@@ -391,8 +391,8 @@ implemented by libuv, the platform layer used by Nvim.
Since Nvim inherited its code from Vim, the states are not prepared to receive
"arbitrary events", so we use a special key to represent those (When a state
-receives an "arbitrary event", it normally doesn't do anything other update the
-screen).
+receives an "arbitrary event", it normally doesn't do anything other than
+update the screen).
Main loop
---------
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index d8edd4b2d0..4e46a1aef2 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -894,6 +894,8 @@ struct diffblock_S {
diff_T *df_next;
linenr_T df_lnum[DB_COUNT]; // line number in buffer
linenr_T df_count[DB_COUNT]; // nr of inserted/changed lines
+ bool is_linematched; // has the linematch algorithm ran on this diff hunk to divide it into
+ // smaller diff hunks?
};
#define SNAP_HELP_IDX 0
@@ -961,7 +963,8 @@ struct frame_S {
// for first
// fr_child and fr_win are mutually exclusive
frame_T *fr_child; // first contained frame
- win_T *fr_win; // window that fills this frame
+ win_T *fr_win; // window that fills this frame; for a snapshot
+ // set to the current window
};
#define FR_LEAF 0 // frame is a leaf
@@ -1338,6 +1341,7 @@ struct window_S {
int w_briopt_shift; // additional shift for breakindent
bool w_briopt_sbr; // sbr in 'briopt'
int w_briopt_list; // additional indent for lists
+ int w_briopt_vcol; // indent for specific column
// transform a pointer to a "onebuf" option into a "allbuf" option
#define GLOBAL_WO(p) ((char *)(p) + sizeof(winopt_T))
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index ca95a9a24e..480f3aa18c 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -1246,7 +1246,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
arg = (const char *)skipwhite(skiptowhite(arg));
if (*arg != NUL) {
xp->xp_context = EXPAND_NOTHING;
- arg = (const char *)skip_regexp((char *)arg + 1, (uint8_t)(*arg), p_magic, NULL);
+ arg = (const char *)skip_regexp((char *)arg + 1, (uint8_t)(*arg), p_magic);
}
}
return (const char *)find_nextcmd(arg);
@@ -1285,7 +1285,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
if (delim) {
// Skip "from" part.
arg++;
- arg = (const char *)skip_regexp((char *)arg, delim, p_magic, NULL);
+ arg = (const char *)skip_regexp((char *)arg, delim, p_magic);
}
// Skip "to" part.
while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 964db599a3..503e546562 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -27,6 +27,7 @@
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
+#include "nvim/linematch.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -62,10 +63,12 @@ static bool diff_need_update = false; // ex_diffupdate needs to be called
#define DIFF_INTERNAL 0x200 // use internal xdiff algorithm
#define DIFF_CLOSE_OFF 0x400 // diffoff when closing window
#define DIFF_FOLLOWWRAP 0x800 // follow the wrap option
+#define DIFF_LINEMATCH 0x1000 // match most similar lines within diff
#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL)
static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF;
static long diff_algorithm = 0;
+static int linematch_lines = 0;
#define LBUFLEN 50 // length of line in diff file
@@ -362,7 +365,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
if (last >= line1 - 1) {
// 6. change below line2: only adjust for amount_after; also when
// "deleted" became zero when deleted all lines between two diffs.
- if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2) {
+ if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2 - dp->is_linematched) {
if (amount_after == 0) {
// nothing left to change
break;
@@ -454,7 +457,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
}
// check if this block touches the previous one, may merge them.
- if ((dprev != NULL)
+ if ((dprev != NULL) && !dp->is_linematched
&& (dprev->df_lnum[idx] + dprev->df_count[idx] == dp->df_lnum[idx])) {
int i;
for (i = 0; i < DB_COUNT; i++) {
@@ -513,6 +516,7 @@ static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp)
{
diff_T *dnew = xmalloc(sizeof(*dnew));
+ dnew->is_linematched = false;
dnew->df_next = dp;
if (dprev == NULL) {
tp->tp_first_diff = dnew;
@@ -727,12 +731,16 @@ static void clear_diffout(diffout_T *dout)
/// @param din
///
/// @return FAIL for failure.
-static int diff_write_buffer(buf_T *buf, mmfile_t *m)
+static int diff_write_buffer(buf_T *buf, mmfile_t *m, linenr_T start, linenr_T end)
{
size_t len = 0;
+ if (end < 0) {
+ end = buf->b_ml.ml_line_count;
+ }
+
// xdiff requires one big block of memory with all the text.
- for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
+ for (linenr_T lnum = start; lnum <= end; lnum++) {
len += strlen(ml_get_buf(buf, lnum, false)) + 1;
}
char *ptr = try_malloc(len);
@@ -753,7 +761,7 @@ static int diff_write_buffer(buf_T *buf, mmfile_t *m)
m->size = (long)len;
len = 0;
- for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
+ for (linenr_T lnum = start; lnum <= end; lnum++) {
char *s = ml_get_buf(buf, lnum, false);
if (diff_flags & DIFF_ICASE) {
while (*s != NUL) {
@@ -792,7 +800,7 @@ static int diff_write_buffer(buf_T *buf, mmfile_t *m)
static int diff_write(buf_T *buf, diffin_T *din)
{
if (din->din_fname == NULL) {
- return diff_write_buffer(buf, &din->din_mmfile);
+ return diff_write_buffer(buf, &din->din_mmfile, 1, -1);
}
// Always use 'fileformat' set to "unix".
@@ -1784,6 +1792,294 @@ void diff_clear(tabpage_T *tp)
tp->tp_first_diff = NULL;
}
+///
+/// return true if the options are set to use diff linematch
+///
+bool diff_linematch(diff_T *dp)
+{
+ if (!(diff_flags & DIFF_LINEMATCH)) {
+ return false;
+ }
+ // are there more than three diff buffers?
+ int tsize = 0;
+ for (int i = 0; i < DB_COUNT; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ // for the rare case (bug?) that the count of a diff block is negative, do
+ // not run the algorithm because this will try to allocate a negative
+ // amount of space and crash
+ if (dp->df_count[i] < 0) {
+ return false;
+ }
+ tsize += dp->df_count[i];
+ }
+ }
+ // avoid allocating a huge array because it will lag
+ return tsize <= linematch_lines;
+}
+
+static int get_max_diff_length(const diff_T *dp)
+{
+ int maxlength = 0;
+ for (int k = 0; k < DB_COUNT; k++) {
+ if (curtab->tp_diffbuf[k] != NULL) {
+ if (dp->df_count[k] > maxlength) {
+ maxlength = dp->df_count[k];
+ }
+ }
+ }
+ return maxlength;
+}
+
+static void find_top_diff_block(diff_T **thistopdiff, diff_T **nextblockblock, int fromidx,
+ int topline)
+{
+ diff_T *topdiff = NULL;
+ diff_T *localtopdiff = NULL;
+ int topdiffchange = 0;
+
+ for (topdiff = curtab->tp_first_diff; topdiff != NULL; topdiff = topdiff->df_next) {
+ // set the top of the current overlapping diff block set as we
+ // iterate through all of the sets of overlapping diff blocks
+ if (!localtopdiff || topdiffchange) {
+ localtopdiff = topdiff;
+ topdiffchange = 0;
+ }
+
+ // check if the fromwin topline is matched by the current diff. if so, set it to the top of the diff block
+ if (topline >= topdiff->df_lnum[fromidx] && topline <=
+ (topdiff->df_lnum[fromidx] + topdiff->df_count[fromidx])) {
+ // this line is inside the current diff block, so we will save the
+ // top block of the set of blocks to refer to later
+ if ((*thistopdiff) == NULL) {
+ (*thistopdiff) = localtopdiff;
+ }
+ }
+
+ // check if the next set of overlapping diff blocks is next
+ if (!(topdiff->df_next && (topdiff->df_next->df_lnum[fromidx] ==
+ (topdiff->df_lnum[fromidx] + topdiff->df_count[fromidx])))) {
+ // mark that the next diff block is belongs to a different set of
+ // overlapping diff blocks
+ topdiffchange = 1;
+
+ // if we already have found that the line number is inside a diff block,
+ // set the marker of the next block and finish the iteration
+ if (*thistopdiff) {
+ (*nextblockblock) = topdiff->df_next;
+ break;
+ }
+ }
+ }
+}
+
+static void count_filler_lines_and_topline(int *curlinenum_to, int *linesfiller,
+ const diff_T *thistopdiff, const int toidx,
+ int virtual_lines_passed)
+{
+ const diff_T *curdif = thistopdiff;
+ int ch_virtual_lines = 0;
+ int isfiller = 0;
+ while (virtual_lines_passed) {
+ if (ch_virtual_lines) {
+ virtual_lines_passed--;
+ ch_virtual_lines--;
+ if (!isfiller) {
+ (*curlinenum_to)++;
+ } else {
+ (*linesfiller)++;
+ }
+ } else {
+ (*linesfiller) = 0;
+ ch_virtual_lines = get_max_diff_length(curdif);
+ isfiller = (curdif->df_count[toidx] ? 0 : 1);
+ if (isfiller) {
+ while (curdif && curdif->df_next && curdif->df_lnum[toidx] ==
+ curdif->df_next->df_lnum[toidx]
+ && curdif->df_next->df_count[toidx] == 0) {
+ curdif = curdif->df_next;
+ ch_virtual_lines += get_max_diff_length(curdif);
+ }
+ }
+ if (curdif) {
+ curdif = curdif->df_next;
+ }
+ }
+ }
+}
+
+static void calculate_topfill_and_topline(const int fromidx, const int toidx, const
+ int from_topline, const int from_topfill, int *topfill,
+ linenr_T *topline)
+{
+ // 1. find the position from the top of the diff block, and the start
+ // of the next diff block
+ diff_T *thistopdiff = NULL;
+ diff_T *nextblockblock = NULL;
+ int virtual_lines_passed = 0;
+
+ find_top_diff_block(&thistopdiff, &nextblockblock, fromidx, from_topline);
+
+ // count the virtual lines that have been passed
+
+ diff_T *curdif = thistopdiff;
+ while (curdif && (curdif->df_lnum[fromidx] + curdif->df_count[fromidx])
+ <= from_topline) {
+ virtual_lines_passed += get_max_diff_length(curdif);
+
+ curdif = curdif->df_next;
+ }
+
+ if (curdif != nextblockblock) {
+ virtual_lines_passed += from_topline - curdif->df_lnum[fromidx];
+ }
+ virtual_lines_passed -= from_topfill;
+
+ // count the same amount of virtual lines in the toidx buffer
+ curdif = thistopdiff;
+ int curlinenum_to = thistopdiff->df_lnum[toidx];
+ int linesfiller = 0;
+ count_filler_lines_and_topline(&curlinenum_to, &linesfiller,
+ thistopdiff, toidx, virtual_lines_passed);
+
+ // count the number of filler lines that would normally be above this line
+ int maxfiller = 0;
+ for (diff_T *dpfillertest = thistopdiff; dpfillertest != NULL;
+ dpfillertest = dpfillertest->df_next) {
+ if (dpfillertest->df_lnum[toidx] == curlinenum_to) {
+ while (dpfillertest && dpfillertest->df_lnum[toidx] == curlinenum_to) {
+ maxfiller += dpfillertest->df_count[toidx] ? 0 : get_max_diff_length(dpfillertest);
+ dpfillertest = dpfillertest->df_next;
+ }
+ break;
+ }
+ }
+ (*topfill) = maxfiller - linesfiller;
+ (*topline) = curlinenum_to;
+}
+
+static int linematched_filler_lines(diff_T *dp, int idx, linenr_T lnum, int *linestatus)
+{
+ int filler_lines_d1 = 0;
+ while (dp && dp->df_next
+ && lnum == (dp->df_lnum[idx] + dp->df_count[idx])
+ && dp->df_next->df_lnum[idx] == lnum) {
+ if (dp->df_count[idx] == 0) {
+ filler_lines_d1 += get_max_diff_length(dp);
+ }
+ dp = dp->df_next;
+ }
+
+ if (dp->df_count[idx] == 0) {
+ filler_lines_d1 += get_max_diff_length(dp);
+ }
+
+ if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) {
+ int j = 0;
+ for (int i = 0; i < DB_COUNT; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ if (dp->df_count[i]) {
+ j++;
+ }
+ }
+ // is this an added line or a changed line?
+ if (linestatus) {
+ (*linestatus) = (j == 1) ? -2 : -1;
+ }
+ }
+ }
+ return filler_lines_d1;
+}
+
+// Apply results from the linematch algorithm and apply to 'dp' by splitting it into multiple
+// adjacent diff blocks.
+static void apply_linematch_results(diff_T *dp, size_t decisions_length, const int *decisions)
+{
+ // get the start line number here in each diff buffer, and then increment
+ int line_numbers[DB_COUNT];
+ int outputmap[DB_COUNT];
+ size_t ndiffs = 0;
+ for (int i = 0; i < DB_COUNT; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ line_numbers[i] = dp->df_lnum[i];
+ dp->df_count[i] = 0;
+
+ // Keep track of the index of the diff buffer we are using here.
+ // We will use this to write the output of the algorithm to
+ // diff_T structs at the correct indexes
+ outputmap[ndiffs] = i;
+ ndiffs++;
+ }
+ }
+
+ // write the diffs starting with the current diff block
+ diff_T *dp_s = dp;
+ for (size_t i = 0; i < decisions_length; i++) {
+ // Don't allocate on first iter since we can reuse the initial diffblock
+ if (i != 0 && (decisions[i - 1] != decisions[i])) {
+ // create new sub diff blocks to segment the original diff block which we
+ // further divided by running the linematch algorithm
+ dp_s = diff_alloc_new(curtab, dp_s, dp_s->df_next);
+ dp_s->is_linematched = true;
+ for (int j = 0; j < DB_COUNT; j++) {
+ if (curtab->tp_diffbuf[j] != NULL) {
+ dp_s->df_lnum[j] = line_numbers[j];
+ dp_s->df_count[j] = 0;
+ }
+ }
+ }
+ for (size_t j = 0; j < ndiffs; j++) {
+ if (decisions[i] & (1 << j)) {
+ // will need to use the map here
+ dp_s->df_count[outputmap[j]]++;
+ line_numbers[outputmap[j]]++;
+ }
+ }
+ }
+ dp->is_linematched = true;
+}
+
+static void run_linematch_algorithm(diff_T *dp)
+{
+ // define buffers for diff algorithm
+ mmfile_t diffbufs_mm[DB_COUNT];
+ const char *diffbufs[DB_COUNT];
+ int diff_length[DB_COUNT];
+ size_t ndiffs = 0;
+ for (int i = 0; i < DB_COUNT; i++) {
+ if (curtab->tp_diffbuf[i] != NULL) {
+ // write the contents of the entire buffer to
+ // diffbufs_mm[diffbuffers_count]
+ diff_write_buffer(curtab->tp_diffbuf[i], &diffbufs_mm[ndiffs],
+ dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i] - 1);
+
+ // we want to get the char* to the diff buffer that was just written
+ // we add it to the array of char*, diffbufs
+ diffbufs[ndiffs] = diffbufs_mm[ndiffs].ptr;
+
+ // keep track of the length of this diff block to pass it to the linematch
+ // algorithm
+ diff_length[ndiffs] = dp->df_count[i];
+
+ // increment the amount of diff buffers we are passing to the algorithm
+ ndiffs++;
+ }
+ }
+
+ // we will get the output of the linematch algorithm in the format of an array
+ // of integers (*decisions) and the length of that array (decisions_length)
+ int *decisions = NULL;
+ const bool iwhite = (diff_flags & (DIFF_IWHITEALL | DIFF_IWHITE)) > 0;
+ size_t decisions_length = linematch_nbuffers(diffbufs, diff_length, ndiffs, &decisions, iwhite);
+
+ for (size_t i = 0; i < ndiffs; i++) {
+ XFREE_CLEAR(diffbufs_mm[i].ptr);
+ }
+
+ apply_linematch_results(dp, decisions_length, decisions);
+
+ xfree(decisions);
+}
+
/// Check diff status for line "lnum" in buffer "buf":
///
/// Returns 0 for nothing special
@@ -1795,9 +2091,10 @@ void diff_clear(tabpage_T *tp)
///
/// @param wp
/// @param lnum
+/// @param[out] linestatus
///
/// @return diff status.
-int diff_check(win_T *wp, linenr_T lnum)
+int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
{
buf_T *buf = wp->w_buffer;
@@ -1840,6 +2137,14 @@ int diff_check(win_T *wp, linenr_T lnum)
return 0;
}
+ if (!dp->is_linematched && diff_linematch(dp)) {
+ run_linematch_algorithm(dp);
+ }
+
+ if (dp->is_linematched) {
+ return linematched_filler_lines(dp, idx, lnum, linestatus);
+ }
+
if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) {
int zero = false;
@@ -1894,15 +2199,16 @@ int diff_check(win_T *wp, linenr_T lnum)
// Insert filler lines above the line just below the change. Will return
// 0 when this buf had the max count.
- linenr_T maxcount = 0;
- for (int i = 0; i < DB_COUNT; i++) {
- if ((curtab->tp_diffbuf[i] != NULL) && (dp->df_count[i] > maxcount)) {
- maxcount = dp->df_count[i];
- }
- }
+ int maxcount = get_max_diff_length(dp);
return maxcount - dp->df_count[idx];
}
+/// See diff_check_with_linestatus
+int diff_check(win_T *wp, linenr_T lnum)
+{
+ return diff_check_with_linestatus(wp, lnum, NULL);
+}
+
/// Compare two entries in diff "dp" and return true if they are equal.
///
/// @param dp diff
@@ -2062,46 +2368,51 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
towin->w_topline = lnum + (dp->df_lnum[toidx] - dp->df_lnum[fromidx]);
if (lnum >= dp->df_lnum[fromidx]) {
- // Inside a change: compute filler lines. With three or more
- // buffers we need to know the largest count.
- linenr_T max_count = 0;
-
- for (int i = 0; i < DB_COUNT; i++) {
- if ((curtab->tp_diffbuf[i] != NULL) && (max_count < dp->df_count[i])) {
- max_count = dp->df_count[i];
- }
- }
+ if (diff_flags & DIFF_LINEMATCH) {
+ calculate_topfill_and_topline(fromidx, toidx, fromwin->w_topline,
+ fromwin->w_topfill, &towin->w_topfill, &towin->w_topline);
+ } else {
+ // Inside a change: compute filler lines. With three or more
+ // buffers we need to know the largest count.
+ linenr_T max_count = 0;
- if (dp->df_count[toidx] == dp->df_count[fromidx]) {
- // same number of lines: use same filler count
- towin->w_topfill = fromwin->w_topfill;
- } else if (dp->df_count[toidx] > dp->df_count[fromidx]) {
- if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
- // more lines in towin and fromwin doesn't show diff
- // lines, only filler lines
- if (max_count - fromwin->w_topfill >= dp->df_count[toidx]) {
- // towin also only shows filler lines
- towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
- towin->w_topfill = fromwin->w_topfill;
- } else {
- // towin still has some diff lines to show
- towin->w_topline = dp->df_lnum[toidx]
- + max_count - fromwin->w_topfill;
+ for (int i = 0; i < DB_COUNT; i++) {
+ if ((curtab->tp_diffbuf[i] != NULL) && (max_count < dp->df_count[i])) {
+ max_count = dp->df_count[i];
}
}
- } else if (towin->w_topline >= dp->df_lnum[toidx]
- + dp->df_count[toidx]) {
- // less lines in towin and no diff lines to show: compute
- // filler lines
- towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
- if (diff_flags & DIFF_FILLER) {
+ if (dp->df_count[toidx] == dp->df_count[fromidx]) {
+ // same number of lines: use same filler count
+ towin->w_topfill = fromwin->w_topfill;
+ } else if (dp->df_count[toidx] > dp->df_count[fromidx]) {
if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
- // fromwin is also out of diff lines
- towin->w_topfill = fromwin->w_topfill;
- } else {
- // fromwin has some diff lines
- towin->w_topfill = dp->df_lnum[fromidx] + max_count - lnum;
+ // more lines in towin and fromwin doesn't show diff
+ // lines, only filler lines
+ if (max_count - fromwin->w_topfill >= dp->df_count[toidx]) {
+ // towin also only shows filler lines
+ towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
+ towin->w_topfill = fromwin->w_topfill;
+ } else {
+ // towin still has some diff lines to show
+ towin->w_topline = dp->df_lnum[toidx]
+ + max_count - fromwin->w_topfill;
+ }
+ }
+ } else if (towin->w_topline >= dp->df_lnum[toidx]
+ + dp->df_count[toidx]) {
+ // less lines in towin and no diff lines to show: compute
+ // filler lines
+ towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
+
+ if (diff_flags & DIFF_FILLER) {
+ if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
+ // fromwin is also out of diff lines
+ towin->w_topfill = fromwin->w_topfill;
+ } else {
+ // fromwin has some diff lines
+ towin->w_topfill = dp->df_lnum[fromidx] + max_count - lnum;
+ }
}
}
}
@@ -2136,6 +2447,7 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
int diffopt_changed(void)
{
int diff_context_new = 6;
+ int linematch_lines_new = 0;
int diff_flags_new = 0;
int diff_foldcolumn_new = 2;
long diff_algorithm_new = 0;
@@ -2205,6 +2517,10 @@ int diffopt_changed(void)
} else {
return FAIL;
}
+ } else if ((STRNCMP(p, "linematch:", 10) == 0) && ascii_isdigit(p[10])) {
+ p += 10;
+ linematch_lines_new = getdigits_int(&p, false, linematch_lines_new);
+ diff_flags_new |= DIFF_LINEMATCH;
}
if ((*p != ',') && (*p != NUL)) {
@@ -2233,6 +2549,7 @@ int diffopt_changed(void)
diff_flags = diff_flags_new;
diff_context = diff_context_new == 0 ? 1 : diff_context_new;
+ linematch_lines = linematch_lines_new;
diff_foldcolumn = diff_foldcolumn_new;
diff_algorithm = diff_algorithm_new;
@@ -2300,6 +2617,13 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
break;
}
}
+ if (dp->is_linematched) {
+ while (dp && dp->df_next
+ && lnum == dp->df_count[idx] + dp->df_lnum[idx]
+ && dp->df_next->df_lnum[idx] == lnum) {
+ dp = dp->df_next;
+ }
+ }
if ((dp == NULL) || (diff_check_sanity(curtab, dp) == FAIL)) {
xfree(line_org);
@@ -2674,6 +2998,17 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr
diff_T *dprev = NULL;
for (diff_T *dp = curtab->tp_first_diff; dp != NULL;) {
+ if (!addr_count) {
+ // handle the case with adjacent diff blocks
+ while (dp->is_linematched
+ && dp->df_next
+ && dp->df_next->df_lnum[idx_cur] == dp->df_lnum[idx_cur] + dp->df_count[idx_cur]
+ && dp->df_next->df_lnum[idx_cur] == line1 + off + 1) {
+ dprev = dp;
+ dp = dp->df_next;
+ }
+ }
+
if (dp->df_lnum[idx_cur] > line2 + off) {
// past the range that was specified
break;
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 08b6fd89af..cb7d85a467 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -804,9 +804,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
int bg_attr = win_bg_attr(wp);
- filler_lines = diff_check(wp, lnum);
- if (filler_lines < 0) {
- if (filler_lines == -1) {
+ int linestatus = 0;
+ filler_lines = diff_check_with_linestatus(wp, lnum, &linestatus);
+ if (filler_lines < 0 || linestatus < 0) {
+ if (filler_lines == -1 || linestatus == -1) {
if (diff_find_change(wp, lnum, &change_start, &change_end)) {
diff_hlf = HLF_ADD; // added line
} else if (change_start == 0) {
@@ -817,7 +818,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
} else {
diff_hlf = HLF_ADD; // added line
}
- filler_lines = 0;
+ if (linestatus == 0) {
+ filler_lines = 0;
+ }
area_highlighting = true;
}
VirtLines virt_lines = KV_INITIAL_VALUE;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 89ae4a2cd0..1200ba20ba 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -545,7 +545,7 @@ int var_redir_start(char *name, int append)
// check if we can write to the variable: set it to or append an empty
// string
- int save_emsg = did_emsg;
+ const int called_emsg_before = called_emsg;
did_emsg = false;
typval_T tv;
tv.v_type = VAR_STRING;
@@ -556,9 +556,7 @@ int var_redir_start(char *name, int append)
set_var_lval(redir_lval, redir_endp, &tv, true, false, "=");
}
clear_lval(redir_lval);
- int err = did_emsg;
- did_emsg |= save_emsg;
- if (err) {
+ if (called_emsg > called_emsg_before) {
redir_endp = NULL; // don't store a value, only cleanup
var_redir_stop();
return FAIL;
@@ -2185,7 +2183,7 @@ char *get_user_var_name(expand_T *xp, int idx)
/// Does not use 'cpo' and always uses 'magic'.
///
/// @return true if "pat" matches "text".
-int pattern_match(char *pat, char *text, bool ic)
+int pattern_match(const char *pat, const char *text, bool ic)
{
int matches = 0;
regmatch_T regmatch;
@@ -2193,7 +2191,7 @@ int pattern_match(char *pat, char *text, bool ic)
// avoid 'l' flag in 'cpoptions'
char *save_cpo = p_cpo;
p_cpo = empty_option;
- regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
+ regmatch.regprog = vim_regcomp((char *)pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
regmatch.rm_ic = ic;
matches = vim_regexec_nl(&regmatch, (char_u *)text, (colnr_T)0);
@@ -3051,7 +3049,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
case '#':
if ((*arg)[1] == '{') {
(*arg)++;
- ret = dict_get_tv(arg, rettv, evaluate, true);
+ ret = eval_dict(arg, rettv, evaluate, true);
} else {
ret = NOTDONE;
}
@@ -3062,7 +3060,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
case '{':
ret = get_lambda_tv(arg, rettv, evaluate);
if (ret == NOTDONE) {
- ret = dict_get_tv(arg, rettv, evaluate, false);
+ ret = eval_dict(arg, rettv, evaluate, false);
}
break;
@@ -4595,7 +4593,7 @@ static int get_literal_key(char **arg, typval_T *tv)
/// "literal" is true for #{key: val}
///
/// @return OK or FAIL. Returns NOTDONE for {expr}.
-static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal)
+static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
{
typval_T tv;
char *key = NULL;
@@ -5202,6 +5200,7 @@ linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf)
if (tv->v_type == VAR_STRING
&& tv->vval.v_string != NULL
&& tv->vval.v_string[0] == '$'
+ && tv->vval.v_string[1] == NUL
&& buf != NULL) {
return buf->b_ml.ml_line_count;
}
@@ -5581,6 +5580,13 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
const char *line = NULL;
if (lines->v_type == VAR_LIST) {
l = lines->vval.v_list;
+ if (l == NULL || tv_list_len(l) == 0) {
+ // set proper return code
+ if (lnum > curbuf->b_ml.ml_line_count) {
+ rettv->vval.v_number = 1; // FAIL
+ }
+ goto done;
+ }
li = tv_list_first(l);
} else {
line = tv_get_string_chk(lines);
@@ -5651,6 +5657,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
update_topline(curwin);
}
+done:
if (!is_curbuf) {
curbuf = curbuf_save;
curwin = curwin_save;
@@ -8873,7 +8880,7 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic)
case EXPR_MATCH:
case EXPR_NOMATCH:
- n1 = pattern_match((char *)s2, (char *)s1, ic);
+ n1 = pattern_match(s2, s1, ic);
if (type == EXPR_NOMATCH) {
n1 = !n1;
}
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index ecb411a652..8bfa6797d0 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -38,7 +38,7 @@ return {
assert_equal={args={2, 3}, base=2},
assert_equalfile={args={2, 3}, base=1},
assert_exception={args={1, 2}},
- assert_fails={args={1, 3}, base=1},
+ assert_fails={args={1, 5}, base=1},
assert_false={args={1, 2}, base=1},
assert_inrange={args={3, 4}, base=3},
assert_match={args={2, 3}, base=2},
@@ -118,7 +118,7 @@ return {
exists={args=1, base=1},
exp={args=1, base=1, float_func="exp"},
expand={args={1, 3}, base=1},
- expandcmd={args=1, base=1},
+ expandcmd={args={1, 2}, base=1},
extend={args={2, 3}, base=1},
feedkeys={args={1, 2}, base=1},
file_readable={args=1, base=1, func='f_filereadable'}, -- obsolete
@@ -376,6 +376,7 @@ return {
str2float={args=1, base=1},
str2list={args={1, 2}, base=1},
str2nr={args={1, 3}, base=1},
+ strcharlen={args=1, base=1},
strcharpart={args={2, 3}, base=1},
strchars={args={1, 2}, base=1},
strdisplaywidth={args={1, 2}, base=1},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 79c93f1917..26a5c133da 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -112,6 +112,8 @@ PRAGMA_DIAG_POP
static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
static char *e_invalwindow = N_("E957: Invalid window number");
static char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value");
+static char e_using_number_as_bool_nr[]
+ = N_("E1023: Using a Number as a Bool: %d");
static char e_cannot_resize_window_in_another_tab_page[]
= N_("E1308: Cannot resize a window in another tab page");
@@ -365,23 +367,34 @@ static void f_api_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "append(lnum, string/list)" function
static void f_append(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
+ const int did_emsg_before = did_emsg;
const linenr_T lnum = tv_get_lnum(&argvars[0]);
-
- set_buffer_lines(curbuf, lnum, true, &argvars[1], rettv);
+ if (did_emsg == did_emsg_before) {
+ set_buffer_lines(curbuf, lnum, true, &argvars[1], rettv);
+ }
}
-/// "appendbufline(buf, lnum, string/list)" function
-static void f_appendbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+/// Set or append lines to a buffer.
+static void buf_set_append_line(typval_T *argvars, typval_T *rettv, bool append)
{
+ const int did_emsg_before = did_emsg;
buf_T *const buf = tv_get_buf(&argvars[0], false);
if (buf == NULL) {
rettv->vval.v_number = 1; // FAIL
} else {
const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf);
- set_buffer_lines(buf, lnum, true, &argvars[2], rettv);
+ if (did_emsg == did_emsg_before) {
+ set_buffer_lines(buf, lnum, append, &argvars[2], rettv);
+ }
}
}
+/// "appendbufline(buf, lnum, string/list)" function
+static void f_appendbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ buf_set_append_line(argvars, rettv, true);
+}
+
/// "atan2()" function
static void f_atan2(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -895,7 +908,7 @@ static void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
countcc = (int)tv_get_number(&argvars[2]);
}
if (countcc < 0 || countcc > 1) {
- emsg(_(e_invarg));
+ semsg(_(e_using_number_as_bool_nr), countcc);
return;
}
@@ -1347,10 +1360,10 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
int noref = 0;
if (argvars[1].v_type != VAR_UNKNOWN) {
- noref = (int)tv_get_number_chk(&argvars[1], NULL);
+ noref = (int)tv_get_bool_chk(&argvars[1], NULL);
}
if (noref < 0 || noref > 1) {
- emsg(_(e_invarg));
+ semsg(_(e_using_number_as_bool_nr), noref);
} else {
var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0
? get_copyID()
@@ -1470,9 +1483,10 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, EvalFuncData fp
/// "deletebufline()" function
static void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
+ const int did_emsg_before = did_emsg;
+ rettv->vval.v_number = 1; // FAIL by default
buf_T *const buf = tv_get_buf(&argvars[0], false);
if (buf == NULL) {
- rettv->vval.v_number = 1; // FAIL
return;
}
const bool is_curbuf = buf == curbuf;
@@ -1480,6 +1494,9 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
linenr_T last;
const linenr_T first = tv_get_lnum_buf(&argvars[1], buf);
+ if (did_emsg > did_emsg_before) {
+ return;
+ }
if (argvars[2].v_type != VAR_UNKNOWN) {
last = tv_get_lnum_buf(&argvars[2], buf);
} else {
@@ -1488,7 +1505,6 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
if (buf->b_ml.ml_mfp == NULL || first < 1
|| first > buf->b_ml.ml_line_count || last < first) {
- rettv->vval.v_number = 1; // FAIL
return;
}
@@ -1541,6 +1557,7 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
curwin = curwin_save;
VIsual_active = save_VIsual_active;
}
+ rettv->vval.v_number = 0; // OK
}
/// "did_filetype()" function
@@ -1573,9 +1590,10 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|| changedtick != buf_get_changedtick(curbuf)
|| fnum != curbuf->b_fnum) {
// New line, buffer, change: need to get the values.
- int filler_lines = diff_check(curwin, lnum);
- if (filler_lines < 0) {
- if (filler_lines == -1) {
+ int linestatus = 0;
+ int filler_lines = diff_check_with_linestatus(curwin, lnum, &linestatus);
+ if (filler_lines < 0 || linestatus < 0) {
+ if (filler_lines == -1 || linestatus == -1) {
change_start = MAXCOL;
change_end = -1;
if (diff_find_change(curwin, lnum, &change_start, &change_end)) {
@@ -2040,6 +2058,12 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char *errormsg = NULL;
+ bool emsgoff = true;
+
+ if (argvars[1].v_type == VAR_DICT
+ && tv_dict_get_bool(argvars[1].vval.v_dict, "errmsg", kBoolVarFalse)) {
+ emsgoff = false;
+ }
rettv->v_type = VAR_STRING;
char *cmdstr = xstrdup(tv_get_string(&argvars[0]));
@@ -2053,9 +2077,17 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
};
eap.argt |= EX_NOSPC;
- emsg_off++;
- expand_filename(&eap, &cmdstr, &errormsg);
- emsg_off--;
+ if (emsgoff) {
+ emsg_off++;
+ }
+ if (expand_filename(&eap, &cmdstr, &errormsg) == FAIL) {
+ if (!emsgoff && errormsg != NULL && *errormsg != NUL) {
+ emsg(errormsg);
+ }
+ }
+ if (emsgoff) {
+ emsg_off--;
+ }
rettv->vval.v_string = cmdstr;
}
@@ -2574,9 +2606,12 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli
/// "getbufline()" function
static void f_getbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
+ const int did_emsg_before = did_emsg;
buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
-
const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf);
+ if (did_emsg > did_emsg_before) {
+ return;
+ }
const linenr_T end = (argvars[2].v_type == VAR_UNKNOWN
? lnum
: tv_get_lnum_buf(&argvars[2], buf));
@@ -7500,16 +7535,7 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "setbufline()" function
static void f_setbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- linenr_T lnum;
- buf_T *buf;
-
- buf = tv_get_buf(&argvars[0], false);
- if (buf == NULL) {
- rettv->vval.v_number = 1; // FAIL
- } else {
- lnum = tv_get_lnum_buf(&argvars[1], buf);
- set_buffer_lines(buf, lnum, false, &argvars[2], rettv);
- }
+ buf_set_append_line(argvars, rettv, false);
}
/// Set the cursor or mark position.
@@ -7638,8 +7664,11 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "setline()" function
static void f_setline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
+ const int did_emsg_before = did_emsg;
linenr_T lnum = tv_get_lnum(&argvars[0]);
- set_buffer_lines(curbuf, lnum, false, &argvars[1], rettv);
+ if (did_emsg == did_emsg_before) {
+ set_buffer_lines(curbuf, lnum, false, &argvars[1], rettv);
+ }
}
/// "setpos()" function
@@ -8147,7 +8176,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
typeerr = true;
}
if (argvars[2].v_type != VAR_UNKNOWN) {
- keepempty = (bool)tv_get_number_chk(&argvars[2], &typeerr);
+ keepempty = (bool)tv_get_bool_chk(&argvars[2], &typeerr);
}
}
if (pat == NULL || *pat == NUL) {
@@ -8277,7 +8306,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
emsg(_(e_invarg));
return;
}
- if (argvars[2].v_type != VAR_UNKNOWN && tv_get_number(&argvars[2])) {
+ if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2])) {
what |= STR2NR_QUOTE;
}
}
@@ -8432,26 +8461,38 @@ static void f_strlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0]));
}
-/// "strchars()" function
-static void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+static void strchar_common(typval_T *argvars, typval_T *rettv, bool skipcc)
{
const char *s = tv_get_string(&argvars[0]);
- int skipcc = 0;
varnumber_T len = 0;
int (*func_mb_ptr2char_adv)(const char_u **pp);
+ func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
+ while (*s != NUL) {
+ func_mb_ptr2char_adv((const char_u **)&s);
+ len++;
+ }
+ rettv->vval.v_number = len;
+}
+
+/// "strcharlen()" function
+static void f_strcharlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ strchar_common(argvars, rettv, true);
+}
+
+/// "strchars()" function
+static void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ int skipcc = false;
+
if (argvars[1].v_type != VAR_UNKNOWN) {
- skipcc = (int)tv_get_number_chk(&argvars[1], NULL);
+ skipcc = (int)tv_get_bool(&argvars[1]);
}
if (skipcc < 0 || skipcc > 1) {
- emsg(_(e_invarg));
+ semsg(_(e_using_number_as_bool_nr), skipcc);
} else {
- func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
- while (*s != NUL) {
- func_mb_ptr2char_adv((const char_u **)&s);
- len++;
- }
- rettv->vval.v_number = len;
+ strchar_common(argvars, rettv, skipcc);
}
}
@@ -9690,6 +9731,10 @@ static void f_win_move_separator(typval_T *argvars, typval_T *rettv, EvalFuncDat
if (wp == NULL || wp->w_floating) {
return;
}
+ if (!win_valid(wp)) {
+ emsg(_(e_cannot_resize_window_in_another_tab_page));
+ return;
+ }
int offset = (int)tv_get_number(&argvars[1]);
win_drag_vsep_line(wp, offset);
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 11b2b6e5fd..31b73f8d48 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -2061,9 +2061,24 @@ int tv_dict_get_tv(dict_T *d, const char *const key, typval_T *rettv)
varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
+ return tv_dict_get_number_def(d, key, 0);
+}
+
+/// Get a number item from a dictionary.
+///
+/// Returns "def" if the entry doesn't exist.
+///
+/// @param[in] d Dictionary to get item from.
+/// @param[in] key Key to find in dictionary.
+/// @param[in] def Default value.
+///
+/// @return Dictionary item.
+varnumber_T tv_dict_get_number_def(const dict_T *const d, const char *const key, const int def)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
dictitem_T *const di = tv_dict_find(d, key, -1);
if (di == NULL) {
- return 0;
+ return def;
}
return tv_get_number(&di->di_tv);
}
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index fcc933c967..6757d058ef 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -559,4 +559,9 @@ EXTERN const size_t kTVTranslate INIT(= TV_TRANSLATE);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/typval.h.generated.h"
#endif
+
+#define tv_get_bool tv_get_number
+#define tv_get_bool_chk tv_get_number_chk
+#define tv_dict_get_bool tv_dict_get_number_def
+
#endif // NVIM_EVAL_TYPVAL_H
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index c22718c65d..4024410a04 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -460,7 +460,7 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex
int i = 0;
if (get_vim_var_nr(VV_TESTING)) {
- // Prepare for calling garbagecollect_for_testing(), need to know
+ // Prepare for calling test_garbagecollect_now(), need to know
// what variables are used on the call stack.
if (funcargs.ga_itemsize == 0) {
ga_init(&funcargs, (int)sizeof(typval_T *), 50);
@@ -1964,7 +1964,7 @@ void ex_function(exarg_T *eap)
// ":function /pat": list functions matching pattern.
if (*eap->arg == '/') {
- p = skip_regexp(eap->arg + 1, '/', true, NULL);
+ p = skip_regexp(eap->arg + 1, '/', true);
if (!eap->skip) {
regmatch_T regmatch;
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 87c4f4e654..89e6d47950 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -504,9 +504,8 @@ void ex_sort(exarg_T *eap)
eap->nextcmd = check_nextcmd(p);
break;
} else if (!ASCII_ISALPHA(*p) && regmatch.regprog == NULL) {
- s = skip_regexp(p + 1, *p, true, NULL);
- if (*s != *p) {
- emsg(_(e_invalpat));
+ s = skip_regexp_err(p + 1, *p, true);
+ if (s == NULL) {
goto sortend;
}
*s = NUL;
@@ -3503,7 +3502,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
which_pat = RE_LAST; // use last used regexp
delimiter = (char_u)(*cmd++); // remember delimiter character
pat = cmd; // remember start of search pat
- cmd = skip_regexp(cmd, delimiter, p_magic, &eap->arg);
+ cmd = skip_regexp_ex(cmd, delimiter, p_magic, &eap->arg, NULL);
if (cmd[0] == delimiter) { // end delimiter found
*cmd++ = NUL; // replace it with a NUL
has_second_delim = true;
@@ -4536,7 +4535,7 @@ void ex_global(exarg_T *eap)
delim = *cmd; // get the delimiter
cmd++; // skip delimiter if there is one
pat = cmd; // remember start of pattern
- cmd = skip_regexp(cmd, delim, p_magic, &eap->arg);
+ cmd = skip_regexp_ex(cmd, delim, p_magic, &eap->arg, NULL);
if (cmd[0] == delim) { // end delimiter found
*cmd++ = NUL; // replace it with a NUL
}
@@ -4849,7 +4848,7 @@ char *skip_vimgrep_pat(char *p, char **s, int *flags)
*s = p + 1;
}
c = (char_u)(*p);
- p = skip_regexp(p + 1, c, true, NULL);
+ p = skip_regexp(p + 1, c, true);
if (*p != c) {
return NULL;
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index e867bd57d4..dc2b7247f1 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -726,7 +726,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
if (cstack.cs_idx >= 0) {
// If a sourced file or executed function ran to its end, report the
// unclosed conditional.
- if (!got_int && !did_throw
+ if (!got_int && !did_throw && !aborting()
&& ((getline_equal(fgetline, cookie, getsourceline)
&& !source_finished(fgetline, cookie))
|| (getline_equal(fgetline, cookie, get_func_line)
@@ -807,6 +807,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
next = messages->next;
emsg(messages->msg);
xfree(messages->msg);
+ xfree(messages->sfile);
xfree(messages);
messages = next;
} while (messages != NULL);
@@ -3351,7 +3352,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
goto error;
}
if (skip) { // skip "/pat/"
- cmd = skip_regexp(cmd, c, p_magic, NULL);
+ cmd = skip_regexp(cmd, c, p_magic);
if (*cmd == c) {
cmd++;
}
@@ -5212,7 +5213,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
old_curwin == NULL ? curwin : NULL);
} else if ((eap->cmdidx != CMD_split && eap->cmdidx != CMD_vsplit)
|| *eap->arg != NUL) {
- // Can't edit another file when "curbuf->b_ro_lockec" is set. Only ":edit"
+ // Can't edit another file when "curbuf->b_ro_locked" is set. Only ":edit"
// can bring us here, others are stopped earlier.
if (*eap->arg != NUL && curbuf_locked()) {
return;
@@ -6494,7 +6495,7 @@ static void ex_findpat(exarg_T *eap)
if (*eap->arg == '/') { // Match regexp, not just whole words
whole = false;
eap->arg++;
- char *p = skip_regexp(eap->arg, '/', p_magic, NULL);
+ char *p = skip_regexp(eap->arg, '/', p_magic);
if (*p) {
*p++ = NUL;
p = skipwhite(p);
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 2d6b236007..8c2ac895cb 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -900,12 +900,12 @@ void ex_else(exarg_T *eap)
if (eap->cmdidx == CMD_elseif) {
bool error;
result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
+
// When throwing error exceptions, we want to throw always the first
// of several errors in a row. This is what actually happens when
// a conditional error was detected above and there is another failure
// when parsing the expression. Since the skip flag is set in this
// case, the parsing error will be ignored by emsg().
-
if (!skip && !error) {
if (result) {
cstack->cs_flags[cstack->cs_idx] = CSF_ACTIVE | CSF_TRUE;
@@ -1296,7 +1296,10 @@ void ex_catch(exarg_T *eap)
eap->nextcmd = find_nextcmd(eap->arg);
} else {
pat = eap->arg + 1;
- end = skip_regexp(pat, *eap->arg, true, NULL);
+ end = skip_regexp_err(pat, *eap->arg, true);
+ if (end == NULL) {
+ give_up = true;
+ }
}
if (!give_up) {
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index a1e4bc96b5..1d242e4ed2 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -314,7 +314,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
p = skipwhite(p);
delim = (delim_optional && vim_isIDc(*p)) ? ' ' : *p++;
*search_delim = delim;
- end = skip_regexp(p, delim, p_magic, NULL);
+ end = skip_regexp(p, delim, p_magic);
use_last_pat = end == p && *end == delim;
if (end == p && !use_last_pat) {
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 2ba2f4c9c9..c780f64a7a 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -1624,23 +1624,31 @@ failed:
error = false;
}
+ // In Dos format ignore a trailing CTRL-Z, unless 'binary' is set.
+ // In old days the file length was in sector count and the CTRL-Z the
+ // marker where the file really ended. Assuming we write it to a file
+ // system that keeps file length properly the CTRL-Z should be dropped.
+ // Set the 'endoffile' option so the user can decide what to write later.
+ // In Unix format the CTRL-Z is just another character.
+ if (linerest != 0
+ && !curbuf->b_p_bin
+ && fileformat == EOL_DOS
+ && ptr[-1] == Ctrl_Z) {
+ ptr--;
+ linerest--;
+ if (set_options) {
+ curbuf->b_p_eof = true;
+ }
+ }
+
// If we get EOF in the middle of a line, note the fact and
// complete the line ourselves.
- // In Dos format ignore a trailing CTRL-Z, unless 'binary' set.
if (!error
&& !got_int
- && linerest != 0
- // TODO(vim): should we handle CTRL-Z differently here for 'endoffile'?
- && !(!curbuf->b_p_bin
- && fileformat == EOL_DOS
- && *line_start == Ctrl_Z
- && ptr == line_start + 1)) {
+ && linerest != 0) {
// remember for when writing
if (set_options) {
curbuf->b_p_eol = false;
- if (*line_start == Ctrl_Z && ptr == line_start + 1) {
- curbuf->b_p_eof = true;
- }
}
*ptr = NUL;
len = (colnr_T)(ptr - line_start + 1);
@@ -2773,10 +2781,11 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
#endif
// copy the file
- if (os_copy(fname, backup, UV_FS_COPYFILE_FICLONE)
- != 0) {
- SET_ERRMSG(_("E506: Can't write to backup file "
- "(add ! to override)"));
+ if (os_copy(fname, backup, UV_FS_COPYFILE_FICLONE) != 0) {
+ SET_ERRMSG(_("E509: Cannot create backup file (add ! to override)"));
+ XFREE_CLEAR(backup);
+ backup = NULL;
+ continue;
}
#ifdef UNIX
@@ -2787,6 +2796,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
#ifdef HAVE_ACL
mch_set_acl((char_u *)backup, acl);
#endif
+ SET_ERRMSG(NULL);
break;
}
}
@@ -3197,11 +3207,6 @@ restore_backup:
len = 0;
write_info.bw_start_lnum = lnum;
}
- if (!buf->b_p_fixeol && buf->b_p_eof) {
- // write trailing CTRL-Z
- (void)write_eintr(write_info.bw_fd, "\x1a", 1);
- }
-
// write failed or last line has no EOL: stop here
if (end == 0
|| (lnum == end
@@ -3253,6 +3258,11 @@ restore_backup:
nchars += len;
}
+ if (!buf->b_p_fixeol && buf->b_p_eof) {
+ // write trailing CTRL-Z
+ (void)write_eintr(write_info.bw_fd, "\x1a", 1);
+ }
+
// Stop when writing done or an error was encountered.
if (!checking_conversion || end == 0) {
break;
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index c556aac1fd..14266fc859 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -194,6 +194,12 @@ EXTERN int emsg_skip INIT(= 0); // don't display errors for
// expression that is skipped
EXTERN bool emsg_severe INIT(= false); // use message of next of several
// emsg() calls for throw
+// used by assert_fails()
+EXTERN bool emsg_assert_fails_used INIT(= false);
+EXTERN char *emsg_assert_fails_msg INIT(= NULL);
+EXTERN long emsg_assert_fails_lnum INIT(= 0);
+EXTERN char *emsg_assert_fails_context INIT(= NULL);
+
EXTERN bool did_endif INIT(= false); // just had ":endif"
EXTERN dict_T vimvardict; // Dictionary with v: variables
EXTERN dict_T globvardict; // Dictionary with g: variables
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 4c253480be..9a09118939 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -851,7 +851,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
bool did_highlight_changed = false;
// If no argument, list current highlighting.
- if (ends_excmd((uint8_t)(*line))) {
+ if (!init && ends_excmd((uint8_t)(*line))) {
for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) {
// TODO(brammool): only call when the group has attributes set
highlight_list_one(i);
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index cb5819e946..d372e41459 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -743,6 +743,7 @@ bool briopt_check(win_T *wp)
int bri_min = 20;
bool bri_sbr = false;
int bri_list = 0;
+ int bri_vcol = 0;
char *p = wp->w_p_briopt;
while (*p != NUL) {
@@ -759,6 +760,9 @@ bool briopt_check(win_T *wp)
} else if (STRNCMP(p, "list:", 5) == 0) {
p += 5;
bri_list = (int)getdigits(&p, false, 0);
+ } else if (STRNCMP(p, "column:", 7) == 0) {
+ p += 7;
+ bri_vcol = (int)getdigits(&p, false, 0);
}
if (*p != ',' && *p != NUL) {
return false;
@@ -771,7 +775,8 @@ bool briopt_check(win_T *wp)
wp->w_briopt_shift = bri_shift;
wp->w_briopt_min = bri_min;
wp->w_briopt_sbr = bri_sbr;
- wp->w_briopt_list = bri_list;
+ wp->w_briopt_list = bri_list;
+ wp->w_briopt_vcol = bri_vcol;
return true;
}
@@ -783,51 +788,78 @@ int get_breakindent_win(win_T *wp, char_u *line)
FUNC_ATTR_NONNULL_ALL
{
static int prev_indent = 0; // Cached indent value.
- static long prev_ts = 0; // Cached tabstop value.
+ static long prev_ts = 0L; // Cached tabstop value.
static const char_u *prev_line = NULL; // cached pointer to line.
static varnumber_T prev_tick = 0; // Changedtick of cached value.
- static long *prev_vts = NULL; // Cached vartabs values.
+ static long *prev_vts = NULL; // Cached vartabs values.
+ static int prev_list = 0; // cached list value
+ static int prev_listopt = 0; // cached w_p_briopt_list value
+ static char *prev_flp = NULL; // cached formatlistpat value
int bri = 0;
// window width minus window margin space, i.e. what rests for text
const int eff_wwidth = wp->w_width_inner -
((wp->w_p_nu || wp->w_p_rnu)
&& (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) ? number_width(wp) + 1 : 0);
- // used cached indent, unless pointer or 'tabstop' changed
+ // used cached indent, unless
+ // - line pointer changed
+ // - 'tabstop' changed
+ // - 'briopt_list changed' changed or
+ // - 'formatlistpattern' changed
if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts
|| prev_tick != buf_get_changedtick(wp->w_buffer)
+ || prev_listopt != wp->w_briopt_list
+ || (prev_flp == NULL || (strcmp(prev_flp, get_flp_value(wp->w_buffer)) != 0))
|| prev_vts != wp->w_buffer->b_p_vts_array) {
prev_line = line;
prev_ts = wp->w_buffer->b_p_ts;
prev_tick = buf_get_changedtick(wp->w_buffer);
prev_vts = wp->w_buffer->b_p_vts_array;
- prev_indent = get_indent_str_vtab((char *)line,
- wp->w_buffer->b_p_ts,
- wp->w_buffer->b_p_vts_array,
- wp->w_p_list);
+ if (wp->w_briopt_vcol == 0) {
+ prev_indent = get_indent_str_vtab((char *)line,
+ wp->w_buffer->b_p_ts,
+ wp->w_buffer->b_p_vts_array,
+ wp->w_p_list);
+ }
+ prev_listopt = wp->w_briopt_list;
+ prev_list = 0;
+ xfree(prev_flp);
+ prev_flp = xstrdup(get_flp_value(wp->w_buffer));
+ // add additional indent for numbered lists
+ if (wp->w_briopt_list != 0 && wp->w_briopt_vcol == 0) {
+ regmatch_T regmatch = {
+ .regprog = vim_regcomp(prev_flp, RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT),
+ };
+ if (regmatch.regprog != NULL) {
+ regmatch.rm_ic = false;
+ if (vim_regexec(&regmatch, (char *)line, 0)) {
+ if (wp->w_briopt_list > 0) {
+ prev_list += wp->w_briopt_list;
+ } else {
+ prev_list = (int)(*regmatch.endp - *regmatch.startp);
+ }
+ }
+ vim_regfree(regmatch.regprog);
+ }
+ }
+ }
+ if (wp->w_briopt_vcol != 0) {
+ // column value has priority
+ bri = wp->w_briopt_vcol;
+ prev_list = 0;
+ } else {
+ bri = prev_indent + wp->w_briopt_shift;
}
- bri = prev_indent + wp->w_briopt_shift;
// Add offset for number column, if 'n' is in 'cpoptions'
bri += win_col_off2(wp);
// add additional indent for numbered lists
if (wp->w_briopt_list != 0) {
- regmatch_T regmatch = {
- .regprog = vim_regcomp(curbuf->b_p_flp,
- RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT),
- };
-
- if (regmatch.regprog != NULL) {
- regmatch.rm_ic = false;
- if (vim_regexec(&regmatch, (char *)line, 0)) {
- if (wp->w_briopt_list > 0) {
- bri += wp->w_briopt_list;
- } else {
- bri = (int)(*regmatch.endp - *regmatch.startp);
- }
- }
- vim_regfree(regmatch.regprog);
+ if (wp->w_briopt_list > 0) {
+ bri += prev_list;
+ } else {
+ bri = prev_list;
}
}
diff --git a/src/nvim/linematch.c b/src/nvim/linematch.c
new file mode 100644
index 0000000000..04209bb836
--- /dev/null
+++ b/src/nvim/linematch.c
@@ -0,0 +1,376 @@
+#include "nvim/linematch.h"
+#include "nvim/memory.h"
+#include "nvim/vim.h"
+
+// struct for running the diff linematch algorithm
+typedef struct {
+ int *df_decision; // to keep track of this path traveled
+ int df_lev_score; // to keep track of the total score of this path
+ size_t df_path_idx; // current index of this path
+} diffcmppath_T;
+
+#define LN_MAX_BUFS 8
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "linematch.c.generated.h"
+#endif
+
+static size_t line_len(const char *s)
+{
+ char *end = strchr(s, '\n');
+ if (end) {
+ return (size_t)(end - s);
+ }
+ return STRLEN(s);
+}
+
+/// Same as matching_chars but ignore whitespace
+///
+/// @param s1
+/// @param s2
+static int matching_chars_iwhite(const char *s1, const char *s2)
+{
+ // the newly processed strings that will be compared
+ // delete the white space characters, and/or replace all upper case with lower
+ char *strsproc[2];
+ const char *strsorig[2] = { s1, s2 };
+ for (int k = 0; k < 2; k++) {
+ size_t d = 0;
+ size_t i = 0;
+ size_t slen = line_len(strsorig[k]);
+ strsproc[k] = xmalloc((slen + 1) * sizeof(char));
+ while (d + i < slen) {
+ char e = strsorig[k][i + d];
+ if (e != ' ' && e != '\t') {
+ strsproc[k][i] = e;
+ i++;
+ } else {
+ d++;
+ }
+ }
+ strsproc[k][i] = '\0';
+ }
+ int matching = matching_chars(strsproc[0], strsproc[1]);
+ xfree(strsproc[0]);
+ xfree(strsproc[1]);
+ return matching;
+}
+
+/// update the path of a point in the diff linematch algorithm
+/// @param diffcmppath
+/// @param score
+/// @param to
+/// @param from
+/// @param choice
+static void update_path_flat(diffcmppath_T *diffcmppath, int score, size_t to, size_t from,
+ const int choice)
+{
+ size_t path_idx = diffcmppath[from].df_path_idx;
+
+ for (size_t k = 0; k < path_idx; k++) {
+ diffcmppath[to].df_decision[k] = diffcmppath[from].df_decision[k];
+ }
+
+ diffcmppath[to].df_decision[path_idx] = choice;
+ diffcmppath[to].df_lev_score = score;
+ diffcmppath[to].df_path_idx = path_idx + 1;
+}
+
+#define MATCH_CHAR_MAX_LEN 500
+
+/// Return matching characters between "s1" and "s2" whilst respecting sequence order.
+/// Consider the case of two strings 'AAACCC' and 'CCCAAA', the
+/// return value from this function will be 3, either to match
+/// the 3 C's, or the 3 A's.
+///
+/// Examples:
+/// matching_chars("aabc", "acba") -> 2 // 'a' and 'b' in common
+/// matching_chars("123hello567", "he123ll567o") -> 8 // '123', 'll' and '567' in common
+/// matching_chars("abcdefg", "gfedcba") -> 1 // all characters in common,
+/// // but only at most 1 in sequence
+///
+/// @param s1
+/// @param s2
+static int matching_chars(const char *s1, const char *s2)
+{
+ size_t s1len = MIN(MATCH_CHAR_MAX_LEN, line_len(s1));
+ size_t s2len = MIN(MATCH_CHAR_MAX_LEN, line_len(s2));
+ int matrix[2][MATCH_CHAR_MAX_LEN] = { 0 };
+ bool icur = 1; // save space by storing only two rows for i axis
+ for (size_t i = 0; i < s1len; i++) {
+ icur = !icur;
+ int *e1 = matrix[icur];
+ int *e2 = matrix[!icur];
+ for (size_t j = 0; j < s2len; j++) {
+ // skip char in s1
+ if (e2[j + 1] > e1[j + 1]) {
+ e1[j + 1] = e2[j + 1];
+ }
+ // skip char in s2
+ if (e1[j] > e1[j + 1]) {
+ e1[j + 1] = e1[j];
+ }
+ // compare char in s1 and s2
+ if ((s1[i] == s2[j]) && (e2[j] + 1) > e1[j + 1]) {
+ e1[j + 1] = e2[j] + 1;
+ }
+ }
+ }
+ return matrix[icur][s2len];
+}
+
+/// count the matching characters between a variable number of strings "sp"
+/// mark the strings that have already been compared to extract them later
+/// without re-running the character match counting.
+/// @param sp
+/// @param fomvals
+/// @param n
+static int count_n_matched_chars(const char **sp, const size_t n, bool iwhite)
+{
+ int matched_chars = 0;
+ int matched = 0;
+ for (size_t i = 0; i < n; i++) {
+ for (size_t j = i + 1; j < n; j++) {
+ if (sp[i] != NULL && sp[j] != NULL) {
+ matched++;
+ // TODO(lewis6991): handle whitespace ignoring higher up in the stack
+ matched_chars += iwhite ? matching_chars_iwhite(sp[i], sp[j])
+ : matching_chars(sp[i], sp[j]);
+ }
+ }
+ }
+
+ // prioritize a match of 3 (or more lines) equally to a match of 2 lines
+ if (matched >= 2) {
+ matched_chars *= 2;
+ matched_chars /= matched;
+ }
+
+ return matched_chars;
+}
+
+void fastforward_buf_to_lnum(const char **s, long lnum)
+{
+ for (long i = 0; i < lnum - 1; i++) {
+ *s = strchr(*s, '\n');
+ (*s)++;
+ }
+}
+
+/// try all the different ways to compare these lines and use the one that
+/// results in the most matching characters
+/// @param df_iters
+/// @param paths
+/// @param npaths
+/// @param path_idx
+/// @param choice
+/// @param diffcmppath
+/// @param diff_len
+/// @param ndiffs
+/// @param diff_blk
+static void try_possible_paths(const int *df_iters, const size_t *paths, const int npaths,
+ const int path_idx, int *choice, diffcmppath_T *diffcmppath,
+ const int *diff_len, const size_t ndiffs, const char **diff_blk,
+ bool iwhite)
+{
+ if (path_idx == npaths) {
+ if ((*choice) > 0) {
+ int from_vals[LN_MAX_BUFS];
+ const int *to_vals = df_iters;
+ const char *current_lines[LN_MAX_BUFS];
+ for (size_t k = 0; k < ndiffs; k++) {
+ from_vals[k] = df_iters[k];
+ // get the index at all of the places
+ if ((*choice) & (1 << k)) {
+ from_vals[k]--;
+ const char *p = diff_blk[k];
+ fastforward_buf_to_lnum(&p, df_iters[k]);
+ current_lines[k] = p;
+ } else {
+ current_lines[k] = NULL;
+ }
+ }
+ size_t unwrapped_idx_from = unwrap_indexes(from_vals, diff_len, ndiffs);
+ size_t unwrapped_idx_to = unwrap_indexes(to_vals, diff_len, ndiffs);
+ int matched_chars = count_n_matched_chars(current_lines, ndiffs, iwhite);
+ int score = diffcmppath[unwrapped_idx_from].df_lev_score + matched_chars;
+ if (score > diffcmppath[unwrapped_idx_to].df_lev_score) {
+ update_path_flat(diffcmppath, score, unwrapped_idx_to, unwrapped_idx_from, *choice);
+ }
+ } else {
+ // initialize the 0, 0, 0 ... choice
+ size_t i = 0;
+ while (i < ndiffs && df_iters[i] == 0) {
+ i++;
+ if (i == ndiffs) {
+ diffcmppath[0].df_lev_score = 0;
+ diffcmppath[0].df_path_idx = 0;
+ }
+ }
+ }
+ return;
+ }
+ size_t bit_place = paths[path_idx];
+ *(choice) |= (1 << bit_place); // set it to 1
+ try_possible_paths(df_iters, paths, npaths, path_idx + 1, choice,
+ diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
+ *(choice) &= ~(1 << bit_place); // set it to 0
+ try_possible_paths(df_iters, paths, npaths, path_idx + 1, choice,
+ diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
+}
+
+/// unwrap indexes to access n dimensional tensor
+/// @param values
+/// @param diff_len
+/// @param ndiffs
+static size_t unwrap_indexes(const int *values, const int *diff_len, const size_t ndiffs)
+{
+ size_t num_unwrap_scalar = 1;
+ for (size_t k = 0; k < ndiffs; k++) {
+ num_unwrap_scalar *= (size_t)diff_len[k] + 1;
+ }
+
+ size_t path_idx = 0;
+ for (size_t k = 0; k < ndiffs; k++) {
+ num_unwrap_scalar /= (size_t)diff_len[k] + 1;
+
+ // (k == 0) space optimization
+ int n = k == 0 ? values[k] % 2 : values[k];
+ path_idx += num_unwrap_scalar * (size_t)n;
+ }
+ return path_idx;
+}
+
+/// populate the values of the linematch algorithm tensor, and find the best
+/// decision for how to compare the relevant lines from each of the buffers at
+/// each point in the tensor
+/// @param df_iters
+/// @param ch_dim
+/// @param diffcmppath
+/// @param diff_len
+/// @param ndiffs
+/// @param diff_blk
+static void populate_tensor(int *df_iters, const size_t ch_dim, diffcmppath_T *diffcmppath,
+ const int *diff_len, const size_t ndiffs, const char **diff_blk,
+ bool iwhite)
+{
+ if (ch_dim == ndiffs) {
+ int npaths = 0;
+ size_t paths[LN_MAX_BUFS];
+
+ for (size_t j = 0; j < ndiffs; j++) {
+ if (df_iters[j] > 0) {
+ paths[npaths] = j;
+ npaths++;
+ }
+ }
+ int choice = 0;
+ size_t unwrapper_idx_to = unwrap_indexes(df_iters, diff_len, ndiffs);
+ diffcmppath[unwrapper_idx_to].df_lev_score = -1;
+ try_possible_paths(df_iters, paths, npaths, 0, &choice, diffcmppath,
+ diff_len, ndiffs, diff_blk, iwhite);
+ return;
+ }
+
+ for (int i = 0; i <= diff_len[ch_dim]; i++) {
+ df_iters[ch_dim] = i;
+ populate_tensor(df_iters, ch_dim + 1, diffcmppath, diff_len,
+ ndiffs, diff_blk, iwhite);
+ }
+}
+
+/// algorithm to find an optimal alignment of lines of a diff block with 2 or
+/// more files. The algorithm is generalized to work for any number of files
+/// which corresponds to another dimmension added to the tensor used in the
+/// algorithm
+///
+/// for questions and information about the linematch algorithm please contact
+/// Jonathon White (jonathonwhite@protonmail.com)
+///
+/// for explanation, a summary of the algorithm in 3 dimmensions (3 files
+/// compared) follows
+///
+/// The 3d case (for 3 buffers) of the algorithm implemented when diffopt
+/// 'linematch' is enabled. The algorithm constructs a 3d tensor to
+/// compare a diff between 3 buffers. The dimmensions of the tensor are
+/// the length of the diff in each buffer plus 1 A path is constructed by
+/// moving from one edge of the cube/3d tensor to the opposite edge.
+/// Motions from one cell of the cube to the next represent decisions. In
+/// a 3d cube, there are a total of 7 decisions that can be made,
+/// represented by the enum df_path3_choice which is defined in
+/// buffer_defs.h a comparison of buffer 0 and 1 represents a motion
+/// toward the opposite edge of the cube with components along the 0 and
+/// 1 axes. a comparison of buffer 0, 1, and 2 represents a motion
+/// toward the opposite edge of the cube with components along the 0, 1,
+/// and 2 axes. A skip of buffer 0 represents a motion along only the 0
+/// axis. For each action, a point value is awarded, and the path is
+/// saved for reference later, if it is found to have been the optimal
+/// path. The optimal path has the highest score. The score is
+/// calculated as the summation of the total characters matching between
+/// all of the lines which were compared. The structure of the algorithm
+/// is that of a dynamic programming problem. We can calculate a point
+/// i,j,k in the cube as a function of i-1, j-1, and k-1. To find the
+/// score and path at point i,j,k, we must determine which path we want
+/// to use, this is done by looking at the possibilities and choosing
+/// the one which results in the local highest score. The total highest
+/// scored path is, then in the end represented by the cell in the
+/// opposite corner from the start location. The entire algorithm
+/// consits of populating the 3d cube with the optimal paths from which
+/// it may have came.
+///
+/// Optimizations:
+/// As the function to calculate the cell of a tensor at point i,j,k is a
+/// function of the cells at i-1, j-1, k-1, the whole tensor doesn't need
+/// to be stored in memory at once. In the case of the 3d cube, only two
+/// slices (along k and j axis) are stored in memory. For the 2d matrix
+/// (for 2 files), only two rows are stored at a time. The next/previous
+/// slice (or row) is always calculated from the other, and they alternate
+/// at each iteration.
+/// In the 3d case, 3 arrays are populated to memorize the score (matched
+/// characters) of the 3 buffers, so a redundant calculation of the
+/// scores does not occur
+/// @param diff_blk
+/// @param diff_len
+/// @param ndiffs
+/// @param [out] [allocated] decisions
+/// @return the length of decisions
+size_t linematch_nbuffers(const char **diff_blk, const int *diff_len, const size_t ndiffs,
+ int **decisions, bool iwhite)
+{
+ assert(ndiffs <= LN_MAX_BUFS);
+
+ size_t memsize = 1;
+ size_t memsize_decisions = 0;
+ for (size_t i = 0; i < ndiffs; i++) {
+ assert(diff_len[i] >= 0);
+ memsize *= i == 0 ? 2 : (size_t)(diff_len[i] + 1);
+ memsize_decisions += (size_t)diff_len[i];
+ }
+
+ // create the flattened path matrix
+ diffcmppath_T *diffcmppath = xmalloc(sizeof(diffcmppath_T) * memsize);
+ // allocate memory here
+ for (size_t i = 0; i < memsize; i++) {
+ diffcmppath[i].df_decision = xmalloc(memsize_decisions * sizeof(int));
+ }
+
+ // memory for avoiding repetitive calculations of score
+ int df_iters[LN_MAX_BUFS];
+ populate_tensor(df_iters, 0, diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
+
+ const size_t u = unwrap_indexes(diff_len, diff_len, ndiffs);
+ const size_t best_path_idx = diffcmppath[u].df_path_idx;
+ const int *best_path_decisions = diffcmppath[u].df_decision;
+
+ *decisions = xmalloc(sizeof(int) * best_path_idx);
+ for (size_t i = 0; i < best_path_idx; i++) {
+ (*decisions)[i] = best_path_decisions[i];
+ }
+
+ for (size_t i = 0; i < memsize; i++) {
+ xfree(diffcmppath[i].df_decision);
+ }
+ xfree(diffcmppath);
+
+ return best_path_idx;
+}
diff --git a/src/nvim/linematch.h b/src/nvim/linematch.h
new file mode 100644
index 0000000000..052d438617
--- /dev/null
+++ b/src/nvim/linematch.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_LINEMATCH_H
+#define NVIM_LINEMATCH_H
+
+#include <stddef.h>
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "linematch.h.generated.h"
+#endif
+
+#endif // NVIM_LINEMATCH_H
diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index b2b5dfedee..cd542b0e00 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -10,12 +10,16 @@
#include <string.h>
#include "nvim/api/private/helpers.h"
+#include "nvim/linematch.h"
#include "nvim/lua/converter.h"
#include "nvim/lua/executor.h"
#include "nvim/lua/xdiff.h"
#include "nvim/vim.h"
#include "xdiff/xdiff.h"
+#define COMPARED_BUFFER0 (1 << 0)
+#define COMPARED_BUFFER1 (1 << 1)
+
typedef enum {
kNluaXdiffModeUnified = 0,
kNluaXdiffModeOnHunkCB,
@@ -25,12 +29,81 @@ typedef enum {
typedef struct {
lua_State *lstate;
Error *err;
+ mmfile_t *ma;
+ mmfile_t *mb;
+ bool linematch;
+ bool iwhite;
} hunkpriv_t;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/xdiff.c.generated.h"
#endif
+static void lua_pushhunk(lua_State *lstate, long start_a, long count_a, long start_b, long count_b)
+{
+ // Mimic extra offsets done by xdiff, see:
+ // src/xdiff/xemit.c:284
+ // src/xdiff/xutils.c:(356,368)
+ if (count_a > 0) {
+ start_a += 1;
+ }
+ if (count_b > 0) {
+ start_b += 1;
+ }
+ lua_createtable(lstate, 0, 0);
+ lua_pushinteger(lstate, start_a);
+ lua_rawseti(lstate, -2, 1);
+ lua_pushinteger(lstate, count_a);
+ lua_rawseti(lstate, -2, 2);
+ lua_pushinteger(lstate, start_b);
+ lua_rawseti(lstate, -2, 3);
+ lua_pushinteger(lstate, count_b);
+ lua_rawseti(lstate, -2, 4);
+ lua_rawseti(lstate, -2, (signed)lua_objlen(lstate, -2) + 1);
+}
+
+static void get_linematch_results(lua_State *lstate, mmfile_t *ma, mmfile_t *mb, long start_a,
+ long count_a, long start_b, long count_b, bool iwhite)
+{
+ // get the pointer to char of the start of the diff to pass it to linematch algorithm
+ const char *diff_begin[2] = { ma->ptr, mb->ptr };
+ int diff_length[2] = { (int)count_a, (int)count_b };
+
+ fastforward_buf_to_lnum(&diff_begin[0], start_a + 1);
+ fastforward_buf_to_lnum(&diff_begin[1], start_b + 1);
+
+ int *decisions = NULL;
+ size_t decisions_length = linematch_nbuffers(diff_begin, diff_length, 2, &decisions, iwhite);
+
+ long lnuma = start_a, lnumb = start_b;
+
+ long hunkstarta = lnuma;
+ long hunkstartb = lnumb;
+ long hunkcounta = 0;
+ long hunkcountb = 0;
+ for (size_t i = 0; i < decisions_length; i++) {
+ if (i && (decisions[i - 1] != decisions[i])) {
+ lua_pushhunk(lstate, hunkstarta, hunkcounta, hunkstartb, hunkcountb);
+
+ hunkstarta = lnuma;
+ hunkstartb = lnumb;
+ hunkcounta = 0;
+ hunkcountb = 0;
+ // create a new hunk
+ }
+ if (decisions[i] & COMPARED_BUFFER0) {
+ lnuma++;
+ hunkcounta++;
+ }
+ if (decisions[i] & COMPARED_BUFFER1) {
+ lnumb++;
+ hunkcountb++;
+ }
+ }
+ lua_pushhunk(lstate, hunkstarta, hunkcounta, hunkstartb, hunkcountb);
+ xfree(decisions);
+}
+
static int write_string(void *priv, mmbuffer_t *mb, int nbuf)
{
luaL_Buffer *buf = (luaL_Buffer *)priv;
@@ -52,30 +125,15 @@ static int write_string(void *priv, mmbuffer_t *mb, int nbuf)
// hunk_func callback used when opts.hunk_lines = true
static int hunk_locations_cb(long start_a, long count_a, long start_b, long count_b, void *cb_data)
{
- // Mimic extra offsets done by xdiff, see:
- // src/xdiff/xemit.c:284
- // src/xdiff/xutils.c:(356,368)
- if (count_a > 0) {
- start_a += 1;
- }
- if (count_b > 0) {
- start_b += 1;
+ hunkpriv_t *priv = (hunkpriv_t *)cb_data;
+ lua_State *lstate = priv->lstate;
+ if (priv->linematch) {
+ get_linematch_results(lstate, priv->ma, priv->mb, start_a, count_a, start_b, count_b,
+ priv->iwhite);
+ } else {
+ lua_pushhunk(lstate, start_a, count_a, start_b, count_b);
}
- lua_State *lstate = (lua_State *)cb_data;
- lua_createtable(lstate, 0, 0);
-
- lua_pushinteger(lstate, start_a);
- lua_rawseti(lstate, -2, 1);
- lua_pushinteger(lstate, count_a);
- lua_rawseti(lstate, -2, 2);
- lua_pushinteger(lstate, start_b);
- lua_rawseti(lstate, -2, 3);
- lua_pushinteger(lstate, count_b);
- lua_rawseti(lstate, -2, 4);
-
- lua_rawseti(lstate, -2, (signed)lua_objlen(lstate, -2) + 1);
-
return 0;
}
@@ -149,7 +207,7 @@ static bool check_xdiff_opt(ObjectType actType, ObjectType expType, const char *
}
static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg, xpparam_t *params,
- Error *err)
+ bool *linematch, Error *err)
{
const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, err);
@@ -205,6 +263,11 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg,
goto exit_1;
}
cfg->interhunkctxlen = v->data.integer;
+ } else if (strequal("linematch", k.data)) {
+ *linematch = api_object_to_bool(*v, "linematch", false, err);
+ if (ERROR_SET(err)) {
+ goto exit_1;
+ }
} else {
struct {
const char *name;
@@ -244,10 +307,8 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg,
if (had_on_hunk) {
mode = kNluaXdiffModeOnHunkCB;
- cfg->hunk_func = call_on_hunk_cb;
} else if (had_result_type_indices) {
mode = kNluaXdiffModeLocations;
- cfg->hunk_func = hunk_locations_cb;
}
exit_1:
@@ -268,6 +329,7 @@ int nlua_xdl_diff(lua_State *lstate)
xdemitconf_t cfg;
xpparam_t params;
xdemitcb_t ecb;
+ bool linematch = false;
CLEAR_FIELD(cfg);
CLEAR_FIELD(params);
@@ -280,7 +342,7 @@ int nlua_xdl_diff(lua_State *lstate)
return luaL_argerror(lstate, 3, "expected table");
}
- mode = process_xdl_diff_opts(lstate, &cfg, &params, &err);
+ mode = process_xdl_diff_opts(lstate, &cfg, &params, &linematch, &err);
if (ERROR_SET(&err)) {
goto exit_0;
@@ -288,7 +350,7 @@ int nlua_xdl_diff(lua_State *lstate)
}
luaL_Buffer buf;
- hunkpriv_t *priv = NULL;
+ hunkpriv_t priv;
switch (mode) {
case kNluaXdiffModeUnified:
luaL_buffinit(lstate, &buf);
@@ -296,14 +358,24 @@ int nlua_xdl_diff(lua_State *lstate)
ecb.out_line = write_string;
break;
case kNluaXdiffModeOnHunkCB:
- priv = xmalloc(sizeof(*priv));
- priv->lstate = lstate;
- priv->err = &err;
- ecb.priv = priv;
+ cfg.hunk_func = call_on_hunk_cb;
+ priv = (hunkpriv_t) {
+ .lstate = lstate,
+ .err = &err,
+ };
+ ecb.priv = &priv;
break;
case kNluaXdiffModeLocations:
+ cfg.hunk_func = hunk_locations_cb;
+ priv = (hunkpriv_t) {
+ .lstate = lstate,
+ .ma = &ma,
+ .mb = &mb,
+ .linematch = linematch,
+ .iwhite = (params.flags & XDF_IGNORE_WHITESPACE) > 0
+ };
+ ecb.priv = &priv;
lua_createtable(lstate, 0, 0);
- ecb.priv = lstate;
break;
}
@@ -314,8 +386,6 @@ int nlua_xdl_diff(lua_State *lstate)
}
}
- XFREE_CLEAR(priv);
-
exit_0:
if (ERROR_SET(&err)) {
luaL_where(lstate, 1);
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 2b42d7725b..0fff48019b 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -176,7 +176,7 @@ static void showmap(mapblock_T *mp, bool local)
// Display the LHS. Get length of what we write.
len = (size_t)msg_outtrans_special((char *)mp->m_keys, true, 0);
do {
- msg_putchar(' '); // padd with blanks
+ msg_putchar(' '); // pad with blanks
len++;
} while (len < 12);
diff --git a/src/nvim/match.c b/src/nvim/match.c
index b422dc0ba8..916bb44d8c 100644
--- a/src/nvim/match.c
+++ b/src/nvim/match.c
@@ -1206,7 +1206,7 @@ void ex_match(exarg_T *eap)
semsg(_(e_invarg2), eap->arg);
return;
}
- end = skip_regexp(p + 1, *p, true, NULL);
+ end = skip_regexp(p + 1, *p, true);
if (!eap->skip) {
if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) {
xfree(g);
diff --git a/src/nvim/message.c b/src/nvim/message.c
index b608b59c9b..fa1c8036e6 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -663,6 +663,13 @@ static bool emsg_multiline(const char *s, bool multiline)
return true;
}
+ if (emsg_assert_fails_used && emsg_assert_fails_msg == NULL) {
+ emsg_assert_fails_msg = xstrdup(s);
+ emsg_assert_fails_lnum = SOURCING_LNUM;
+ xfree(emsg_assert_fails_context);
+ emsg_assert_fails_context = xstrdup(SOURCING_NAME == NULL ? "" : SOURCING_NAME);
+ }
+
// set "v:errmsg", also when using ":silent! cmd"
set_vim_var_string(VV_ERRMSG, s, -1);
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 88dd81da8b..53431187af 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -969,6 +969,14 @@ static bool mouse_model_popup(void)
return p_mousem[0] == 'p';
}
+static win_T *dragwin = NULL; ///< window being dragged
+
+/// Reset the window being dragged. To be called when switching tab page.
+void reset_dragwin(void)
+{
+ dragwin = NULL;
+}
+
/// Move the cursor to the specified row and column on the screen.
/// Change current window if necessary. Returns an integer with the
/// CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
@@ -1005,7 +1013,6 @@ int jump_to_mouse(int flags, bool *inclusive, int which_button)
static bool on_winbar = false;
static int prev_row = -1;
static int prev_col = -1;
- static win_T *dragwin = NULL; // window being dragged
static int did_drag = false; // drag was noticed
win_T *wp, *old_curwin;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 7f8a5b6f2e..2d53918ded 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -3272,7 +3272,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
shortline = (vcol < col) || (vcol == col && !*ptr);
- if (vcol < col) { // line too short, padd with spaces
+ if (vcol < col) { // line too short, pad with spaces
bd.startspaces = col - vcol;
} else if (vcol > col) {
bd.endspaces = vcol - col;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 8de86ce76e..8142be4eb1 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -5220,6 +5220,17 @@ unsigned int get_bkc_value(buf_T *buf)
return buf->b_bkc_flags ? buf->b_bkc_flags : bkc_flags;
}
+/// Get the local or global value of 'formatlistpat'.
+///
+/// @param buf The buffer.
+char *get_flp_value(buf_T *buf)
+{
+ if (buf->b_p_flp == NULL || *buf->b_p_flp == NUL) {
+ return p_flp;
+ }
+ return buf->b_p_flp;
+}
+
/// Get the local or global value of the 'virtualedit' flags.
unsigned int get_ve_flags(void)
{
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 088aa40fda..3a59becb33 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -19,7 +19,7 @@
-- types: bool, number, string
-- lists: (nil), comma, onecomma, flags, flagscomma
-- scopes: global, buffer, window
--- redraw options: statuslines, tabline, current_window, curent_window_only,
+-- redraw options: statuslines, tabline, current_window, current_window_only,
-- current_buffer, all_windows, curswant
-- defaults: {condition=#if condition, if_true=default, if_false=default}
-- #if condition:
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 43628d2842..65bc9f60df 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -697,6 +697,10 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
if (briopt_check(curwin) == FAIL) {
errmsg = e_invarg;
}
+ // list setting requires a redraw
+ if (curwin->w_briopt_list) {
+ redraw_all_later(UPD_NOT_VALID);
+ }
} else if (varp == &p_isi
|| varp == &(curbuf->b_p_isk)
|| varp == &p_isp
@@ -1601,6 +1605,12 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
setmouse(); // in case 'mouse' changed
}
+ // Changing Formatlistpattern when briopt includes the list setting:
+ // redraw
+ if ((varp == &p_flp || varp == &(curbuf->b_p_flp)) && curwin->w_briopt_list) {
+ redraw_all_later(UPD_NOT_VALID);
+ }
+
if (curwin->w_curswant != MAXCOL
&& (opt->flags & (P_CURSWANT | P_RALL)) != 0) {
curwin->w_set_curswant = true;
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 625a3c7922..f568ba8854 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -1244,7 +1244,7 @@ int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, i
add_pat = expand_backtick(&ga, (char *)p, flags);
if (add_pat == -1) {
recursive = false;
- FreeWild(ga.ga_len, ga.ga_data);
+ ga_clear_strings(&ga);
*num_file = 0;
*file = NULL;
return FAIL;
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index 42218ac847..bed15f9e36 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -444,9 +444,9 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
// May have to add something for 'breakindent' and/or 'showbreak'
// string at start of line.
// Set *headp to the size of what we add.
+ // Do not use 'showbreak' at the NUL after the text.
added = 0;
-
- char *const sbr = (char *)get_showbreak_value(wp);
+ char *const sbr = c == NUL ? empty_option : (char *)get_showbreak_value(wp);
if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && vcol != 0) {
colnr_T sbrlen = 0;
int numberwidth = win_col_off(wp);
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index acf9b881f8..5d101ee415 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -5325,10 +5325,8 @@ static int vgr_process_args(exarg_T *eap, vgr_args_T *args)
}
// Parse the list of arguments, wildcards have already been expanded.
- if (get_arglist_exp(p, &args->fcount, &args->fnames, true) == FAIL) {
- return FAIL;
- }
- if (args->fcount == 0) {
+ if (get_arglist_exp(p, &args->fcount, &args->fnames, true) == FAIL
+ || args->fcount == 0) {
emsg(_(e_nomatch));
return FAIL;
}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index d6f207a248..7a96889f22 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -97,20 +97,24 @@ static int toggle_Magic(int x)
#define MAX_LIMIT (32767L << 16L)
-static char_u e_missingbracket[] = N_("E769: Missing ] after %s[");
-static char_u e_reverse_range[] = N_("E944: Reverse range in character class");
-static char_u e_large_class[] = N_("E945: Range too large in character class");
-static char_u e_unmatchedpp[] = N_("E53: Unmatched %s%%(");
-static char_u e_unmatchedp[] = N_("E54: Unmatched %s(");
-static char_u e_unmatchedpar[] = N_("E55: Unmatched %s)");
-static char_u e_z_not_allowed[] = N_("E66: \\z( not allowed here");
-static char_u e_z1_not_allowed[] = N_("E67: \\z1 - \\z9 not allowed here");
-static char_u e_missing_sb[] = N_("E69: Missing ] after %s%%[");
-static char_u e_empty_sb[] = N_("E70: Empty %s%%[]");
-static char_u e_recursive[] = N_("E956: Cannot use pattern recursively");
-static char_u e_regexp_number_after_dot_pos_search[]
+static char e_missingbracket[] = N_("E769: Missing ] after %s[");
+static char e_reverse_range[] = N_("E944: Reverse range in character class");
+static char e_large_class[] = N_("E945: Range too large in character class");
+static char e_unmatchedpp[] = N_("E53: Unmatched %s%%(");
+static char e_unmatchedp[] = N_("E54: Unmatched %s(");
+static char e_unmatchedpar[] = N_("E55: Unmatched %s)");
+static char e_z_not_allowed[] = N_("E66: \\z( not allowed here");
+static char e_z1_not_allowed[] = N_("E67: \\z1 - \\z9 not allowed here");
+static char e_missing_sb[] = N_("E69: Missing ] after %s%%[");
+static char e_empty_sb[] = N_("E70: Empty %s%%[]");
+static char e_recursive[] = N_("E956: Cannot use pattern recursively");
+static char e_regexp_number_after_dot_pos_search_chr[]
= N_("E1204: No Number allowed after .: '\\%%%c'");
-static char_u e_substitute_nesting_too_deep[] = N_("E1290: substitute nesting too deep");
+static char e_nfa_regexp_missing_value_in_chr[]
+ = N_("E1273: (NFA regexp) missing value in '\\%%%c'");
+static char e_atom_engine_must_be_at_start_of_pattern[]
+ = N_("E1281: Atom '\\%%#=%c' must be at the start of the pattern");
+static char e_substitute_nesting_too_deep[] = N_("E1290: substitute nesting too deep");
#define NOT_MULTI 0
#define MULTI_ONE 1
@@ -481,13 +485,33 @@ static char_u *skip_anyof(char *p)
}
/// Skip past regular expression.
-/// Stop at end of "startp" or where "dirc" is found ('/', '?', etc).
+/// Stop at end of "startp" or where "delim" is found ('/', '?', etc).
/// Take care of characters with a backslash in front of it.
/// Skip strings inside [ and ].
+char *skip_regexp(char *startp, int delim, int magic)
+{
+ return skip_regexp_ex(startp, delim, magic, NULL, NULL);
+}
+
+/// Call skip_regexp() and when the delimiter does not match give an error and
+/// return NULL.
+char *skip_regexp_err(char *startp, int delim, int magic)
+{
+ char *p = skip_regexp(startp, delim, magic);
+
+ if (*p != delim) {
+ semsg(_("E654: missing delimiter after search pattern: %s"), startp);
+ return NULL;
+ }
+ return p;
+}
+
+/// skip_regexp() with extra arguments:
/// When "newp" is not NULL and "dirc" is '?', make an allocated copy of the
/// expression and change "\?" to "?". If "*newp" is not NULL the expression
/// is changed in-place.
-char *skip_regexp(char *startp, int dirc, int magic, char **newp)
+/// If a "\?" is changed to "?" then "dropped" is incremented, unless NULL.
+char *skip_regexp_ex(char *startp, int dirc, int magic, char **newp, int *dropped)
{
int mymagic;
char *p = startp;
@@ -516,6 +540,9 @@ char *skip_regexp(char *startp, int dirc, int magic, char **newp)
*newp = xstrdup(startp);
p = *newp + (p - startp);
}
+ if (dropped != NULL) {
+ (*dropped)++;
+ }
STRMOVE(p, p + 1);
} else {
p++; // skip next character
diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c
index ac33fc0f13..6f63b38a90 100644
--- a/src/nvim/regexp_bt.c
+++ b/src/nvim/regexp_bt.c
@@ -1971,6 +1971,11 @@ static char_u *regatom(int *flagp)
break;
case '#':
+ if (regparse[0] == '=' && regparse[1] >= 48 && regparse[1] <= 50) {
+ // misplaced \%#=1
+ semsg(_(e_atom_engine_must_be_at_start_of_pattern), regparse[1]);
+ return FAIL;
+ }
ret = regnode(CURSOR);
break;
@@ -2091,6 +2096,7 @@ static char_u *regatom(int *flagp)
uint32_t n = 0;
int cmp;
bool cur = false;
+ bool got_digit = false;
cmp = c;
if (cmp == '<' || cmp == '>') {
@@ -2101,6 +2107,7 @@ static char_u *regatom(int *flagp)
c = getchr();
}
while (ascii_isdigit(c)) {
+ got_digit = true;
n = n * 10 + (uint32_t)(c - '0');
c = getchr();
}
@@ -2115,9 +2122,9 @@ static char_u *regatom(int *flagp)
*regcode++ = (char_u)cmp;
}
break;
- } else if (c == 'l' || c == 'c' || c == 'v') {
+ } else if ((c == 'l' || c == 'c' || c == 'v') && (cur || got_digit)) {
if (cur && n) {
- semsg(_(e_regexp_number_after_dot_pos_search), no_Magic(c));
+ semsg(_(e_regexp_number_after_dot_pos_search_chr), no_Magic(c));
rc_did_emsg = true;
return NULL;
}
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index fbd4e26c75..d4d2ed28cc 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -2094,6 +2094,12 @@ static int nfa_regatom(void)
break;
case '#':
+ if (regparse[0] == '=' && regparse[1] >= 48
+ && regparse[1] <= 50) {
+ // misplaced \%#=1
+ semsg(_(e_atom_engine_must_be_at_start_of_pattern), regparse[1]);
+ return FAIL;
+ }
EMIT(NFA_CURSOR);
break;
@@ -2141,6 +2147,7 @@ static int nfa_regatom(void)
int64_t n = 0;
const int cmp = c;
bool cur = false;
+ bool got_digit = false;
if (c == '<' || c == '>') {
c = getchr();
@@ -2151,7 +2158,7 @@ static int nfa_regatom(void)
}
while (ascii_isdigit(c)) {
if (cur) {
- semsg(_(e_regexp_number_after_dot_pos_search), no_Magic(c));
+ semsg(_(e_regexp_number_after_dot_pos_search_chr), no_Magic(c));
return FAIL;
}
if (n > (INT32_MAX - (c - '0')) / 10) {
@@ -2161,10 +2168,15 @@ static int nfa_regatom(void)
}
n = n * 10 + (c - '0');
c = getchr();
+ got_digit = true;
}
if (c == 'l' || c == 'c' || c == 'v') {
int32_t limit = INT32_MAX;
+ if (!cur && !got_digit) {
+ semsg(_(e_nfa_regexp_missing_value_in_chr), no_Magic(c));
+ return FAIL;
+ }
if (c == 'l') {
if (cur) {
n = curwin->w_cursor.lnum;
@@ -3358,8 +3370,8 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)
int last = indent->ga_len - 3;
char_u save[2];
- STRNCPY(save, &p[last], 2);
- STRNCPY(&p[last], "+-", 2);
+ STRNCPY(save, &p[last], 2); // NOLINT(runtime/printf)
+ memcpy(&p[last], "+-", 2);
fprintf(debugf, " %s", p);
STRNCPY(&p[last], save, 2); // NOLINT(runtime/printf)
} else {
@@ -4623,6 +4635,20 @@ static bool sub_equal(regsub_T *sub1, regsub_T *sub2)
}
#ifdef REGEXP_DEBUG
+static void open_debug_log(TriState result)
+{
+ log_fd = fopen(NFA_REGEXP_RUN_LOG, "a");
+ if (log_fd == NULL) {
+ emsg(_(e_log_open_failed));
+ log_fd = stderr;
+ }
+
+ fprintf(log_fd, "****************************\n");
+ fprintf(log_fd, "FINISHED RUNNING nfa_regmatch() recursively\n");
+ fprintf(log_fd, "MATCH = %s\n", result == kTrue ? "OK" : result == kNone ? "MAYBE" : "FALSE");
+ fprintf(log_fd, "****************************\n");
+}
+
static void report_state(char *action, regsub_T *sub, nfa_state_T *state, int lid, nfa_pim_T *pim)
{
int col;
@@ -4635,6 +4661,9 @@ static void report_state(char *action, regsub_T *sub, nfa_state_T *state, int li
col = (int)(sub->list.line[0].start - rex.line);
}
nfa_set_code(state->c);
+ if (log_fd == NULL) {
+ open_debug_log(kNone);
+ }
fprintf(log_fd, "> %s state %d to list %d. char %d: %s (start col %d)%s\n",
action, abs(state->id), lid, state->c, code, col,
pim_info(pim));
@@ -5656,16 +5685,7 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T
nfa_endp = save_nfa_endp;
#ifdef REGEXP_DEBUG
- log_fd = fopen(NFA_REGEXP_RUN_LOG, "a");
- if (log_fd != NULL) {
- fprintf(log_fd, "****************************\n");
- fprintf(log_fd, "FINISHED RUNNING nfa_regmatch() recursively\n");
- fprintf(log_fd, "MATCH = %s\n", !result ? "false" : "OK");
- fprintf(log_fd, "****************************\n");
- } else {
- emsg(_(e_log_open_failed));
- log_fd = stderr;
- }
+ open_debug_log(result);
#endif
return result;
@@ -5971,16 +5991,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
#ifdef REGEXP_DEBUG
log_fd = fopen(NFA_REGEXP_RUN_LOG, "a");
- if (log_fd != NULL) {
- fprintf(log_fd, "**********************************\n");
- nfa_set_code(start->c);
- fprintf(log_fd, " RUNNING nfa_regmatch() starting with state %d, code %s\n",
- abs(start->id), code);
- fprintf(log_fd, "**********************************\n");
- } else {
+ if (log_fd == NULL) {
emsg(_(e_log_open_failed));
log_fd = stderr;
}
+ fprintf(log_fd, "**********************************\n");
+ nfa_set_code(start->c);
+ fprintf(log_fd, " RUNNING nfa_regmatch() starting with state %d, code %s\n",
+ abs(start->id), code);
+ fprintf(log_fd, "**********************************\n");
#endif
thislist = &list[0];
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index fa7aa35a4d..6becd50910 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -1633,7 +1633,7 @@ void ex_options(exarg_T *eap)
bool multi_mods = 0;
buf[0] = NUL;
- (void)add_win_cmd_modifers(buf, &cmdmod, &multi_mods);
+ (void)add_win_cmd_modifiers(buf, &cmdmod, &multi_mods);
os_setenv("OPTWIN_CMD", buf, 1);
cmd_source(SYS_OPTWIN_FILE, NULL);
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 49892a4cc5..2f3e5a2cb6 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -1080,7 +1080,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
// Find end of regular expression.
// If there is a matching '/' or '?', toss it.
ps = (char_u *)strcopy;
- p = (char_u *)skip_regexp((char *)pat, search_delim, p_magic, &strcopy);
+ p = (char_u *)skip_regexp_ex((char *)pat, search_delim, p_magic, &strcopy, NULL);
if (strcopy != (char *)ps) {
// made a copy of "pat" to change "\?" to "?"
searchcmdlen += (int)(STRLEN(pat) - strlen(strcopy));
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 7830dd5206..40b3429de9 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -166,7 +166,7 @@ typedef enum {
/// Possible results of shada_write function.
typedef enum {
- kSDWriteSuccessfull, ///< Writing was successful.
+ kSDWriteSuccessful, ///< Writing was successful.
kSDWriteReadNotShada, ///< Writing was successful, but when reading it
///< attempted to read file that did not look like
///< a ShaDa file.
@@ -1511,7 +1511,7 @@ static char *shada_filename(const char *file)
/// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no
/// restrictions.
///
-/// @return kSDWriteSuccessfull, kSDWriteFailed or kSDWriteIgnError.
+/// @return kSDWriteSuccessful, kSDWriteFailed or kSDWriteIgnError.
static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntry entry,
const size_t max_kbyte)
FUNC_ATTR_NONNULL_ALL
@@ -1819,7 +1819,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr
}
msgpack_packer_free(spacker);
msgpack_sbuffer_destroy(&sbuf);
- return kSDWriteSuccessfull;
+ return kSDWriteSuccessful;
shada_pack_entry_error:
msgpack_packer_free(spacker);
msgpack_sbuffer_destroy(&sbuf);
@@ -1839,7 +1839,7 @@ static inline ShaDaWriteResult shada_pack_pfreed_entry(msgpack_packer *const pac
const size_t max_kbyte)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE
{
- ShaDaWriteResult ret = kSDWriteSuccessfull;
+ ShaDaWriteResult ret = kSDWriteSuccessful;
ret = shada_pack_entry(packer, entry.data, max_kbyte);
if (entry.can_free_entry) {
shada_free_shada_entry(&entry.data);
@@ -2053,7 +2053,7 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re
msgpack_packer *const packer)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- ShaDaWriteResult ret = kSDWriteSuccessfull;
+ ShaDaWriteResult ret = kSDWriteSuccessful;
ShadaEntry entry;
ShaDaReadResult srni_ret;
@@ -2492,7 +2492,7 @@ static int hist_type2char(const int type)
static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef *const sd_reader)
FUNC_ATTR_NONNULL_ARG(1)
{
- ShaDaWriteResult ret = kSDWriteSuccessfull;
+ ShaDaWriteResult ret = kSDWriteSuccessful;
int max_kbyte_i = get_shada_parameter('s');
if (max_kbyte_i < 0) {
max_kbyte_i = 10;
@@ -2652,7 +2652,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
}
tv_clear(&vartv);
tv_clear(&tgttv);
- if (spe_ret == kSDWriteSuccessfull) {
+ if (spe_ret == kSDWriteSuccessful) {
int kh_ret;
(void)kh_put(strset, &wms->dumped_variables, name, &kh_ret);
}
@@ -2817,7 +2817,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
if (sd_reader != NULL) {
const ShaDaWriteResult srww_ret = shada_read_when_writing(sd_reader, srni_flags, max_kbyte, wms,
packer);
- if (srww_ret != kSDWriteSuccessfull) {
+ if (srww_ret != kSDWriteSuccessful) {
ret = srww_ret;
}
}
@@ -3078,7 +3078,7 @@ shada_write_file_nomerge: {}
if (!nomerge) {
sd_reader.close(&sd_reader);
bool did_remove = false;
- if (sw_ret == kSDWriteSuccessfull) {
+ if (sw_ret == kSDWriteSuccessful) {
#ifdef UNIX
// For Unix we check the owner of the file. It's not very nice to
// overwrite a user’s viminfo file after a "su root", with a
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 1bd0b9c85f..e76ac49b5d 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -50,7 +50,7 @@
// Use SPELL_PRINTTREE for debugging: dump the word tree after adding a word.
// Only use it for small word lists!
-// Use SPELL_COMPRESS_ALLWAYS for debugging: compress the word tree after
+// Use SPELL_COMPRESS_ALWAYS for debugging: compress the word tree after
// adding a word. Only use it for small word lists!
// Use DEBUG_TRIEWALK to print the changes made in suggest_trie_walk() for a
@@ -160,7 +160,7 @@ typedef struct matchinf_S {
win_T *mi_win; // buffer being checked
// for NOBREAK
- int mi_result2; // "mi_resul" without following word
+ int mi_result2; // "mi_result" without following word
char_u *mi_end2; // "mi_end" without following word
} matchinf_T;
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index d5fbbaff1f..7837f242b5 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -4089,7 +4089,7 @@ static int tree_add_word(spellinfo_T *spin, const char_u *word, wordnode_T *root
// 3. When compressed before, added "compress_added" words
// (si_compress_cnt == 1) and the number of free nodes drops below the
// maximum word length.
-#ifndef SPELL_COMPRESS_ALLWAYS
+#ifndef SPELL_COMPRESS_ALWAYS
if (spin->si_compress_cnt == 1 // NOLINT(readability/braces)
? spin->si_free_count < MAXWLEN
: spin->si_blocks_cnt >= compress_start)
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index e5962cd273..fb82df4fe9 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -4749,7 +4749,7 @@ static char *get_syn_pattern(char *arg, synpat_T *ci)
return NULL;
}
- end = skip_regexp(arg + 1, *arg, true, NULL);
+ end = skip_regexp(arg + 1, *arg, true);
if (*end != *arg) { // end delimiter not found
semsg(_("E401: Pattern delimiter not found: %s"), arg);
return NULL;
@@ -4902,7 +4902,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
finished = true;
break;
}
- arg_end = skip_regexp(next_arg + 1, *next_arg, true, NULL);
+ arg_end = skip_regexp(next_arg + 1, *next_arg, true);
if (*arg_end != *next_arg) { // end delimiter not found
illegal = true;
break;
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 90b21320d2..264f961b43 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -2670,7 +2670,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
// anything following.
str = pbuf;
if (pbuf[0] == '/' || pbuf[0] == '?') {
- str = (char_u *)skip_regexp((char *)pbuf + 1, pbuf[0], false, NULL) + 1;
+ str = (char_u *)skip_regexp((char *)pbuf + 1, pbuf[0], false) + 1;
}
if (str > pbuf_end - 1) { // search command with nothing following
save_p_ws = p_ws;
@@ -2883,7 +2883,7 @@ static int find_extra(char_u **pp)
if (ascii_isdigit(*str)) {
str = (char_u *)skipdigits((char *)str + 1);
} else if (*str == '/' || *str == '?') {
- str = (char_u *)skip_regexp((char *)str + 1, *str, false, NULL);
+ str = (char_u *)skip_regexp((char *)str + 1, *str, false);
if (*str != first_char) {
str = NULL;
} else {
diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim
index 92a51d4371..1459b70243 100644
--- a/src/nvim/testdir/check.vim
+++ b/src/nvim/testdir/check.vim
@@ -4,6 +4,9 @@ source term_util.vim
" Command to check for the presence of a feature.
command -nargs=1 CheckFeature call CheckFeature(<f-args>)
func CheckFeature(name)
+ " if !has(a:name, 1)
+ " throw 'Checking for non-existent feature ' .. a:name
+ " endif
if !has(a:name)
throw 'Skipped: ' .. a:name .. ' feature missing'
endif
diff --git a/src/nvim/testdir/samples/re.freeze.txt b/src/nvim/testdir/samples/re.freeze.txt
new file mode 100644
index 0000000000..d768c23c5e
--- /dev/null
+++ b/src/nvim/testdir/samples/re.freeze.txt
@@ -0,0 +1,6 @@
+:set re=0 or 2
+Search for the pattern: /\s\+\%#\@<!$/
+vim should not freeze.
+
+<td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td><td style="border-bottom windowtext 0.5pt solid; border-left windowtext;" class=abc align=right><font face=arial><font color=#ff0000><b>5</b></font></font></td>
+
diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim
index c2809844ac..ef7cc4ac5f 100644
--- a/src/nvim/testdir/shared.vim
+++ b/src/nvim/testdir/shared.vim
@@ -364,4 +364,19 @@ func GetMessages()
return msg_list
endfunc
+" Run the list of commands in 'cmds' and look for 'errstr' in exception.
+" Note that assert_fails() cannot be used in some places and this function
+" can be used.
+func AssertException(cmds, errstr)
+ let save_exception = ''
+ try
+ for cmd in a:cmds
+ exe cmd
+ endfor
+ catch
+ let save_exception = v:exception
+ endtry
+ call assert_match(a:errstr, save_exception)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test49.ok b/src/nvim/testdir/test49.ok
index 9f283e808b..d687f5bc77 100644
--- a/src/nvim/testdir/test49.ok
+++ b/src/nvim/testdir/test49.ok
@@ -1,48 +1,4 @@
Results of test49.vim:
-*** Test 18: OK (67224583)
-*** Test 19: OK (69275973)
-*** Test 20: OK (1874575085)
-*** Test 21: OK (147932225)
-*** Test 22: OK (4161)
-*** Test 23: OK (49)
-*** Test 24: OK (41)
-*** Test 27: OK (1996459)
-*** Test 28: OK (1996459)
-*** Test 29: OK (170428555)
-*** Test 30: OK (190905173)
-*** Test 31: OK (190905173)
-*** Test 34: OK (2146584868)
-*** Test 35: OK (2146584868)
-*** Test 36: OK (1071644672)
-*** Test 37: OK (1071644672)
-*** Test 38: OK (357908480)
-*** Test 39: OK (357908480)
-*** Test 40: OK (357908480)
-*** Test 49: OK (179000669)
-*** Test 50: OK (363550045)
-*** Test 52: OK (1247112011)
-*** Test 53: OK (131071)
-*** Test 54: OK (2047)
-*** Test 55: OK (1023)
-*** Test 56: OK (511)
-*** Test 57: OK (2147450880)
-*** Test 58: OK (624945)
-*** Test 59: OK (2038431743)
-*** Test 60: OK (311511339)
-*** Test 61: OK (374889517)
-*** Test 62: OK (286331153)
-*** Test 63: OK (236978127)
-*** Test 64: OK (1499645335)
-*** Test 65: OK (70187)
-*** Test 66: OK (5464)
-*** Test 67: OK (212514423)
-*** Test 68: OK (212514423)
-*** Test 76: OK (1610087935)
-*** Test 77: OK (1388671)
-*** Test 78: OK (134217728)
-*** Test 79: OK (70288929)
-*** Test 80: OK (17895765)
-*** Test 81: OK (387)
*** Test 82: OK (8454401)
*** Test 83: OK (2835)
*** Test 84: OK (934782101)
diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim
index 3ee5e9ff7c..d5f8cd3b60 100644
--- a/src/nvim/testdir/test49.vim
+++ b/src/nvim/testdir/test49.vim
@@ -1,6 +1,6 @@
" Vim script language tests
" Author: Servatius Brandt <Servatius.Brandt@fujitsu-siemens.com>
-" Last Change: 2019 Nov 03
+" Last Change: 2020 Jun 07
"-------------------------------------------------------------------------------
" Test environment {{{1
@@ -456,7 +456,7 @@ function ExtraVim(...)
" messing up the user's viminfo file.
let redirect = a:0 ?
\ " -c 'au VimLeave * redir END' -c 'redir\\! >" . a:1 . "'" : ""
- exec "!echo '" . debug_quits . "q' | " .. v:progpath .. " -u NONE -N -es" . redirect .
+ exec "!echo '" . debug_quits . "q' | " .. v:progpath .. " -u NONE -N -Xes" . redirect .
\ " -c 'debuggreedy|set viminfo+=nviminfo'" .
\ " -c 'let ExtraVimBegin = " . extra_begin . "'" .
\ " -c 'let ExtraVimResult = \"" . resultfile . "\"'" . breakpoints .
@@ -607,1660 +607,6 @@ com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>)
" END_OF_TEST_ENVIRONMENT - do not change or remove this line.
-
-" Tests 1 to 17 were moved to test_vimscript.vim
-let Xtest = 18
-
-"-------------------------------------------------------------------------------
-" Test 18: Interrupt (Ctrl-C pressed) {{{1
-"
-" On an interrupt, the script processing is terminated immediately.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- if 1
- Xpath 1 " X: 1
- while 1
- Xpath 2 " X: 2
- if 1
- Xpath 4 " X: 4
- "INTERRUPT
- Xpath 8 " X: 0
- break
- finish
- endif | Xpath 16 " X: 0
- Xpath 32 " X: 0
- endwhile | Xpath 64 " X: 0
- Xpath 128 " X: 0
- endif | Xpath 256 " X: 0
- Xpath 512 " X: 0
-endif
-
-if ExtraVim()
- try
- Xpath 1024 " X: 1024
- "INTERRUPT
- Xpath 2048 " X: 0
- endtry | Xpath 4096 " X: 0
- Xpath 8192 " X: 0
-endif
-
-if ExtraVim()
- function! F()
- if 1
- Xpath 16384 " X: 16384
- while 1
- Xpath 32768 " X: 32768
- if 1
- Xpath 65536 " X: 65536
- "INTERRUPT
- Xpath 131072 " X: 0
- break
- return
- endif | Xpath 262144 " X: 0
- Xpath Xpath 524288 " X: 0
- endwhile | Xpath 1048576 " X: 0
- Xpath Xpath 2097152 " X: 0
- endif | Xpath Xpath 4194304 " X: 0
- Xpath Xpath 8388608 " X: 0
- endfunction
-
- call F() | Xpath 16777216 " X: 0
- Xpath 33554432 " X: 0
-endif
-
-if ExtraVim()
- function! G()
- try
- Xpath 67108864 " X: 67108864
- "INTERRUPT
- Xpath 134217728 " X: 0
- endtry | Xpath 268435456 " X: 0
- Xpath 536870912 " X: 0
- endfunction
-
- call G() | Xpath 1073741824 " X: 0
- " The Xpath command does not accept 2^31 (negative); display explicitly:
- exec "!echo 2147483648 >>" . g:ExtraVimResult
- " X: 0
-endif
-
-Xcheck 67224583
-
-
-"-------------------------------------------------------------------------------
-" Test 19: Aborting on errors inside :try/:endtry {{{1
-"
-" An error in a command dynamically enclosed in a :try/:endtry region
-" aborts script processing immediately. It does not matter whether
-" the failing command is outside or inside a function and whether a
-" function has an "abort" attribute.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- function! F() abort
- Xpath 1 " X: 1
- asdf
- Xpath 2 " X: 0
- endfunction
-
- try
- Xpath 4 " X: 4
- call F()
- Xpath 8 " X: 0
- endtry | Xpath 16 " X: 0
- Xpath 32 " X: 0
-endif
-
-if ExtraVim()
- function! G()
- Xpath 64 " X: 64
- asdf
- Xpath 128 " X: 0
- endfunction
-
- try
- Xpath 256 " X: 256
- call G()
- Xpath 512 " X: 0
- endtry | Xpath 1024 " X: 0
- Xpath 2048 " X: 0
-endif
-
-if ExtraVim()
- try
- Xpath 4096 " X: 4096
- asdf
- Xpath 8192 " X: 0
- endtry | Xpath 16384 " X: 0
- Xpath 32768 " X: 0
-endif
-
-if ExtraVim()
- if 1
- try
- Xpath 65536 " X: 65536
- asdf
- Xpath 131072 " X: 0
- endtry | Xpath 262144 " X: 0
- endif | Xpath 524288 " X: 0
- Xpath 1048576 " X: 0
-endif
-
-if ExtraVim()
- let p = 1
- while p
- let p = 0
- try
- Xpath 2097152 " X: 2097152
- asdf
- Xpath 4194304 " X: 0
- endtry | Xpath 8388608 " X: 0
- endwhile | Xpath 16777216 " X: 0
- Xpath 33554432 " X: 0
-endif
-
-if ExtraVim()
- let p = 1
- while p
- let p = 0
-" try
- Xpath 67108864 " X: 67108864
- endwhile | Xpath 134217728 " X: 0
- Xpath 268435456 " X: 0
-endif
-
-Xcheck 69275973
-"-------------------------------------------------------------------------------
-" Test 20: Aborting on errors after :try/:endtry {{{1
-"
-" When an error occurs after the last active :try/:endtry region has
-" been left, termination behavior is as if no :try/:endtry has been
-" seen.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- let p = 1
- while p
- let p = 0
- try
- Xpath 1 " X: 1
- endtry
- asdf
- endwhile | Xpath 2 " X: 0
- Xpath 4 " X: 4
-endif
-
-if ExtraVim()
- while 1
- try
- Xpath 8 " X: 8
- break
- Xpath 16 " X: 0
- endtry
- endwhile
- Xpath 32 " X: 32
- asdf
- Xpath 64 " X: 64
-endif
-
-if ExtraVim()
- while 1
- try
- Xpath 128 " X: 128
- break
- Xpath 256 " X: 0
- finally
- Xpath 512 " X: 512
- endtry
- endwhile
- Xpath 1024 " X: 1024
- asdf
- Xpath 2048 " X: 2048
-endif
-
-if ExtraVim()
- while 1
- try
- Xpath 4096 " X: 4096
- finally
- Xpath 8192 " X: 8192
- break
- Xpath 16384 " X: 0
- endtry
- endwhile
- Xpath 32768 " X: 32768
- asdf
- Xpath 65536 " X: 65536
-endif
-
-if ExtraVim()
- let p = 1
- while p
- let p = 0
- try
- Xpath 131072 " X: 131072
- continue
- Xpath 262144 " X: 0
- endtry
- endwhile
- Xpath 524288 " X: 524288
- asdf
- Xpath 1048576 " X: 1048576
-endif
-
-if ExtraVim()
- let p = 1
- while p
- let p = 0
- try
- Xpath 2097152 " X: 2097152
- continue
- Xpath 4194304 " X: 0
- finally
- Xpath 8388608 " X: 8388608
- endtry
- endwhile
- Xpath 16777216 " X: 16777216
- asdf
- Xpath 33554432 " X: 33554432
-endif
-
-if ExtraVim()
- let p = 1
- while p
- let p = 0
- try
- Xpath 67108864 " X: 67108864
- finally
- Xpath 134217728 " X: 134217728
- continue
- Xpath 268435456 " X: 0
- endtry
- endwhile
- Xpath 536870912 " X: 536870912
- asdf
- Xpath 1073741824 " X: 1073741824
-endif
-
-Xcheck 1874575085
-
-
-"-------------------------------------------------------------------------------
-" Test 21: :finally for :try after :continue/:break/:return/:finish {{{1
-"
-" If a :try conditional stays inactive due to a preceding :continue,
-" :break, :return, or :finish, its :finally clause should not be
-" executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- function F()
- let loops = 2
- XloopINIT! 1 256
- while loops > 0
- XloopNEXT
- let loops = loops - 1
- try
- if loops == 1
- Xloop 1 " X: 1
- continue
- Xloop 2 " X: 0
- elseif loops == 0
- Xloop 4 " X: 4*256
- break
- Xloop 8 " X: 0
- endif
-
- try " inactive
- Xloop 16 " X: 0
- finally
- Xloop 32 " X: 0
- endtry
- finally
- Xloop 64 " X: 64 + 64*256
- endtry
- Xloop 128 " X: 0
- endwhile
-
- try
- Xpath 65536 " X: 65536
- return
- Xpath 131072 " X: 0
- try " inactive
- Xpath 262144 " X: 0
- finally
- Xpath 524288 " X: 0
- endtry
- finally
- Xpath 1048576 " X: 1048576
- endtry
- Xpath 2097152 " X: 0
- endfunction
-
- try
- Xpath 4194304 " X: 4194304
- call F()
- Xpath 8388608 " X: 8388608
- finish
- Xpath 16777216 " X: 0
- try " inactive
- Xpath 33554432 " X: 0
- finally
- Xpath 67108864 " X: 0
- endtry
- finally
- Xpath 134217728 " X: 134217728
- endtry
- Xpath 268435456 " X: 0
-endif
-
-Xcheck 147932225
-
-
-"-------------------------------------------------------------------------------
-" Test 22: :finally for a :try after an error/interrupt/:throw {{{1
-"
-" If a :try conditional stays inactive due to a preceding error or
-" interrupt or :throw, its :finally clause should not be executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- function! Error()
- try
- asdf " aborting error, triggering error exception
- endtry
- endfunction
-
- Xpath 1 " X: 1
- call Error()
- Xpath 2 " X: 0
-
- if 1 " not active due to error
- try " not active since :if inactive
- Xpath 4 " X: 0
- finally
- Xpath 8 " X: 0
- endtry
- endif
-
- try " not active due to error
- Xpath 16 " X: 0
- finally
- Xpath 32 " X: 0
- endtry
-endif
-
-if ExtraVim()
- function! Interrupt()
- try
- "INTERRUPT " triggering interrupt exception
- endtry
- endfunction
-
- Xpath 64 " X: 64
- call Interrupt()
- Xpath 128 " X: 0
-
- if 1 " not active due to interrupt
- try " not active since :if inactive
- Xpath 256 " X: 0
- finally
- Xpath 512 " X: 0
- endtry
- endif
-
- try " not active due to interrupt
- Xpath 1024 " X: 0
- finally
- Xpath 2048 " X: 0
- endtry
-endif
-
-if ExtraVim()
- function! Throw()
- throw "xyz"
- endfunction
-
- Xpath 4096 " X: 4096
- call Throw()
- Xpath 8192 " X: 0
-
- if 1 " not active due to :throw
- try " not active since :if inactive
- Xpath 16384 " X: 0
- finally
- Xpath 32768 " X: 0
- endtry
- endif
-
- try " not active due to :throw
- Xpath 65536 " X: 0
- finally
- Xpath 131072 " X: 0
- endtry
-endif
-
-Xcheck 4161
-
-
-"-------------------------------------------------------------------------------
-" Test 23: :catch clauses for a :try after a :throw {{{1
-"
-" If a :try conditional stays inactive due to a preceding :throw,
-" none of its :catch clauses should be executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- try
- Xpath 1 " X: 1
- throw "xyz"
- Xpath 2 " X: 0
-
- if 1 " not active due to :throw
- try " not active since :if inactive
- Xpath 4 " X: 0
- catch /xyz/
- Xpath 8 " X: 0
- endtry
- endif
- catch /xyz/
- Xpath 16 " X: 16
- endtry
-
- Xpath 32 " X: 32
- throw "abc"
- Xpath 64 " X: 0
-
- try " not active due to :throw
- Xpath 128 " X: 0
- catch /abc/
- Xpath 256 " X: 0
- endtry
-endif
-
-Xcheck 49
-
-
-"-------------------------------------------------------------------------------
-" Test 24: :endtry for a :try after a :throw {{{1
-"
-" If a :try conditional stays inactive due to a preceding :throw,
-" its :endtry should not rethrow the exception to the next surrounding
-" active :try conditional.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- try " try 1
- try " try 2
- Xpath 1 " X: 1
- throw "xyz" " makes try 2 inactive
- Xpath 2 " X: 0
-
- try " try 3
- Xpath 4 " X: 0
- endtry " no rethrow to try 1
- catch /xyz/ " should catch although try 2 inactive
- Xpath 8 " X: 8
- endtry
- catch /xyz/ " try 1 active, but exception already caught
- Xpath 16 " X: 0
- endtry
- Xpath 32 " X: 32
-endif
-
-Xcheck 41
-
-" Tests 25 and 26 were moved to test_trycatch.vim
-let Xtest = 27
-
-
-"-------------------------------------------------------------------------------
-" Test 27: Executing :finally clauses after :return {{{1
-"
-" For a :return command dynamically enclosed in a :try/:endtry region,
-" :finally clauses are executed and the called function is ended.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! F()
- try
- Xpath 1 " X: 1
- try
- Xpath 2 " X: 2
- return
- Xpath 4 " X: 0
- finally
- Xpath 8 " X: 8
- endtry
- Xpath 16 " X: 0
- finally
- Xpath 32 " X: 32
- endtry
- Xpath 64 " X: 0
-endfunction
-
-function! G()
- try
- Xpath 128 " X: 128
- return
- Xpath 256 " X: 0
- finally
- Xpath 512 " X: 512
- call F()
- Xpath 1024 " X: 1024
- endtry
- Xpath 2048 " X: 0
-endfunction
-
-function! H()
- try
- Xpath 4096 " X: 4096
- call G()
- Xpath 8192 " X: 8192
- finally
- Xpath 16384 " X: 16384
- return
- Xpath 32768 " X: 0
- endtry
- Xpath 65536 " X: 0
-endfunction
-
-try
- Xpath 131072 " X: 131072
- call H()
- Xpath 262144 " X: 262144
-finally
- Xpath 524288 " X: 524288
-endtry
-Xpath 1048576 " X: 1048576
-
-Xcheck 1996459
-
-" Leave F, G, and H for execution as scripts in the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 28: Executing :finally clauses after :finish {{{1
-"
-" For a :finish command dynamically enclosed in a :try/:endtry region,
-" :finally clauses are executed and the sourced file is finished.
-"
-" This test executes the bodies of the functions F, G, and H from the
-" previous test as script files (:return replaced by :finish).
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let scriptF = MakeScript("F") " X: 1 + 2 + 8 + 32
-let scriptG = MakeScript("G", scriptF) " X: 128 + 512 + 1024
-let scriptH = MakeScript("H", scriptG) " X: 4096 + 8192 + 16384
-
-try
- Xpath 131072 " X: 131072
- exec "source" scriptH
- Xpath 262144 " X: 262144
-finally
- Xpath 524288 " X: 524288
-endtry
-Xpath 1048576 " X: 1048576
-
-call delete(scriptF)
-call delete(scriptG)
-call delete(scriptH)
-unlet scriptF scriptG scriptH
-delfunction F
-delfunction G
-delfunction H
-
-Xcheck 1996459
-
-
-"-------------------------------------------------------------------------------
-" Test 29: Executing :finally clauses on errors {{{1
-"
-" After an error in a command dynamically enclosed in a :try/:endtry
-" region, :finally clauses are executed and the script processing is
-" terminated.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- function! F()
- while 1
- try
- Xpath 1 " X: 1
- while 1
- try
- Xpath 2 " X: 2
- asdf " error
- Xpath 4 " X: 0
- finally
- Xpath 8 " X: 8
- endtry | Xpath 16 " X: 0
- Xpath 32 " X: 0
- break
- endwhile
- Xpath 64 " X: 0
- finally
- Xpath 128 " X: 128
- endtry | Xpath 256 " X: 0
- Xpath 512 " X: 0
- break
- endwhile
- Xpath 1024 " X: 0
- endfunction
-
- while 1
- try
- Xpath 2048 " X: 2048
- while 1
- call F()
- Xpath 4096 " X: 0
- break
- endwhile | Xpath 8192 " X: 0
- Xpath 16384 " X: 0
- finally
- Xpath 32768 " X: 32768
- endtry | Xpath 65536 " X: 0
- endwhile | Xpath 131072 " X: 0
- Xpath 262144 " X: 0
-endif
-
-if ExtraVim()
- function! G() abort
- if 1
- try
- Xpath 524288 " X: 524288
- asdf " error
- Xpath 1048576 " X: 0
- finally
- Xpath 2097152 " X: 2097152
- endtry | Xpath 4194304 " X: 0
- endif | Xpath 8388608 " X: 0
- Xpath 16777216 " X: 0
- endfunction
-
- if 1
- try
- Xpath 33554432 " X: 33554432
- call G()
- Xpath 67108864 " X: 0
- finally
- Xpath 134217728 " X: 134217728
- endtry | Xpath 268435456 " X: 0
- endif | Xpath 536870912 " X: 0
- Xpath 1073741824 " X: 0
-endif
-
-Xcheck 170428555
-
-
-"-------------------------------------------------------------------------------
-" Test 30: Executing :finally clauses on interrupt {{{1
-"
-" After an interrupt in a command dynamically enclosed in
-" a :try/:endtry region, :finally clauses are executed and the
-" script processing is terminated.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- XloopINIT 1 16
-
- function! F()
- try
- Xloop 1 " X: 1 + 1*16
- "INTERRUPT
- Xloop 2 " X: 0
- finally
- Xloop 4 " X: 4 + 4*16
- endtry
- Xloop 8 " X: 0
- endfunction
-
- try
- Xpath 256 " X: 256
- try
- Xpath 512 " X: 512
- "INTERRUPT
- Xpath 1024 " X: 0
- finally
- Xpath 2048 " X: 2048
- try
- Xpath 4096 " X: 4096
- try
- Xpath 8192 " X: 8192
- finally
- Xpath 16384 " X: 16384
- try
- Xpath 32768 " X: 32768
- "INTERRUPT
- Xpath 65536 " X: 0
- endtry
- Xpath 131072 " X: 0
- endtry
- Xpath 262144 " X: 0
- endtry
- Xpath 524288 " X: 0
- endtry
- Xpath 1048576 " X: 0
- finally
- Xpath 2097152 " X: 2097152
- try
- Xpath 4194304 " X: 4194304
- call F()
- Xpath 8388608 " X: 0
- finally
- Xpath 16777216 " X: 16777216
- try
- Xpath 33554432 " X: 33554432
- XloopNEXT
- ExecAsScript F
- Xpath 67108864 " X: 0
- finally
- Xpath 134217728 " X: 134217728
- endtry
- Xpath 268435456 " X: 0
- endtry
- Xpath 536870912 " X: 0
- endtry
- Xpath 1073741824 " X: 0
-endif
-
-Xcheck 190905173
-
-
-"-------------------------------------------------------------------------------
-" Test 31: Executing :finally clauses after :throw {{{1
-"
-" After a :throw dynamically enclosed in a :try/:endtry region,
-" :finally clauses are executed and the script processing is
-" terminated.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- XloopINIT 1 16
-
- function! F()
- try
- Xloop 1 " X: 1 + 1*16
- throw "exception"
- Xloop 2 " X: 0
- finally
- Xloop 4 " X: 4 + 4*16
- endtry
- Xloop 8 " X: 0
- endfunction
-
- try
- Xpath 256 " X: 256
- try
- Xpath 512 " X: 512
- throw "exception"
- Xpath 1024 " X: 0
- finally
- Xpath 2048 " X: 2048
- try
- Xpath 4096 " X: 4096
- try
- Xpath 8192 " X: 8192
- finally
- Xpath 16384 " X: 16384
- try
- Xpath 32768 " X: 32768
- throw "exception"
- Xpath 65536 " X: 0
- endtry
- Xpath 131072 " X: 0
- endtry
- Xpath 262144 " X: 0
- endtry
- Xpath 524288 " X: 0
- endtry
- Xpath 1048576 " X: 0
- finally
- Xpath 2097152 " X: 2097152
- try
- Xpath 4194304 " X: 4194304
- call F()
- Xpath 8388608 " X: 0
- finally
- Xpath 16777216 " X: 16777216
- try
- Xpath 33554432 " X: 33554432
- XloopNEXT
- ExecAsScript F
- Xpath 67108864 " X: 0
- finally
- Xpath 134217728 " X: 134217728
- endtry
- Xpath 268435456 " X: 0
- endtry
- Xpath 536870912 " X: 0
- endtry
- Xpath 1073741824 " X: 0
-endif
-
-Xcheck 190905173
-
-" Tests 32 and 33 were moved to test_trycatch.vim
-let Xtest = 34
-
-
-"-------------------------------------------------------------------------------
-" Test 34: :finally reason discarded by :continue {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by a :continue in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 8
-
- function! C(jump)
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "return" || a:jump == "finish"
- return
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- continue " discards jump that caused the :finally
- Xloop 1 " X: 0
- endtry
- Xloop 2 " X: 0
- elseif loop == 2
- Xloop 4 " X: 4*(1+8+64+512+4096+32768+262144)
- endif
- endwhile
- endfunction
-
- call C("continue")
- Xpath 2097152 " X: 2097152
- call C("break")
- Xpath 4194304 " X: 4194304
- call C("return")
- Xpath 8388608 " X: 8388608
- let g:jump = "finish"
- ExecAsScript C
- unlet g:jump
- Xpath 16777216 " X: 16777216
- try
- call C("error")
- Xpath 33554432 " X: 33554432
- finally
- Xpath 67108864 " X: 67108864
- try
- call C("interrupt")
- Xpath 134217728 " X: 134217728
- finally
- Xpath 268435456 " X: 268435456
- call C("throw")
- Xpath 536870912 " X: 536870912
- endtry
- endtry
- Xpath 1073741824 " X: 1073741824
-
- delfunction C
-
-endif
-
-Xcheck 2146584868
-
-
-"-------------------------------------------------------------------------------
-" Test 35: :finally reason discarded by :break {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by a :break in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 8
-
- function! B(jump)
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "return" || a:jump == "finish"
- return
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- break " discards jump that caused the :finally
- Xloop 1 " X: 0
- endtry
- elseif loop == 2
- Xloop 2 " X: 0
- endif
- endwhile
- Xloop 4 " X: 4*(1+8+64+512+4096+32768+262144)
- endfunction
-
- call B("continue")
- Xpath 2097152 " X: 2097152
- call B("break")
- Xpath 4194304 " X: 4194304
- call B("return")
- Xpath 8388608 " X: 8388608
- let g:jump = "finish"
- ExecAsScript B
- unlet g:jump
- Xpath 16777216 " X: 16777216
- try
- call B("error")
- Xpath 33554432 " X: 33554432
- finally
- Xpath 67108864 " X: 67108864
- try
- call B("interrupt")
- Xpath 134217728 " X: 134217728
- finally
- Xpath 268435456 " X: 268435456
- call B("throw")
- Xpath 536870912 " X: 536870912
- endtry
- endtry
- Xpath 1073741824 " X: 1073741824
-
- delfunction B
-
-endif
-
-Xcheck 2146584868
-
-
-"-------------------------------------------------------------------------------
-" Test 36: :finally reason discarded by :return {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by a :return in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 8
-
- function! R(jump, retval) abort
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "return"
- return
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- return a:retval " discards jump that caused the :finally
- Xloop 1 " X: 0
- endtry
- elseif loop == 2
- Xloop 2 " X: 0
- endif
- endwhile
- Xloop 4 " X: 0
- endfunction
-
- let sum = -R("continue", -8)
- Xpath 2097152 " X: 2097152
- let sum = sum - R("break", -16)
- Xpath 4194304 " X: 4194304
- let sum = sum - R("return", -32)
- Xpath 8388608 " X: 8388608
- try
- let sum = sum - R("error", -64)
- Xpath 16777216 " X: 16777216
- finally
- Xpath 33554432 " X: 33554432
- try
- let sum = sum - R("interrupt", -128)
- Xpath 67108864 " X: 67108864
- finally
- Xpath 134217728 " X: 134217728
- let sum = sum - R("throw", -256)
- Xpath 268435456 " X: 268435456
- endtry
- endtry
- Xpath 536870912 " X: 536870912
-
- let expected = 8 + 16 + 32 + 64 + 128 + 256
- if sum != expected
- Xpath 1073741824 " X: 0
- Xout "sum =" . sum . ", expected: " . expected
- endif
-
- unlet sum expected
- delfunction R
-
-endif
-
-Xcheck 1071644672
-
-
-"-------------------------------------------------------------------------------
-" Test 37: :finally reason discarded by :finish {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by a :finish in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 8
-
- function! F(jump) " not executed as function, transformed to a script
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "finish"
- finish
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- finish " discards jump that caused the :finally
- Xloop 1 " X: 0
- endtry
- elseif loop == 2
- Xloop 2 " X: 0
- endif
- endwhile
- Xloop 4 " X: 0
- endfunction
-
- let scriptF = MakeScript("F")
- delfunction F
-
- let g:jump = "continue"
- exec "source" scriptF
- Xpath 2097152 " X: 2097152
- let g:jump = "break"
- exec "source" scriptF
- Xpath 4194304 " X: 4194304
- let g:jump = "finish"
- exec "source" scriptF
- Xpath 8388608 " X: 8388608
- try
- let g:jump = "error"
- exec "source" scriptF
- Xpath 16777216 " X: 16777216
- finally
- Xpath 33554432 " X: 33554432
- try
- let g:jump = "interrupt"
- exec "source" scriptF
- Xpath 67108864 " X: 67108864
- finally
- Xpath 134217728 " X: 134217728
- try
- let g:jump = "throw"
- exec "source" scriptF
- Xpath 268435456 " X: 268435456
- finally
- Xpath 536870912 " X: 536870912
- endtry
- endtry
- endtry
- unlet g:jump
-
- call delete(scriptF)
- unlet scriptF
-
-endif
-
-Xcheck 1071644672
-
-
-"-------------------------------------------------------------------------------
-" Test 38: :finally reason discarded by an error {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by an error in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 4
-
- function! E(jump)
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "return" || a:jump == "finish"
- return
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- asdf " error; discards jump that caused the :finally
- endtry
- elseif loop == 2
- Xloop 1 " X: 0
- endif
- endwhile
- Xloop 2 " X: 0
- endfunction
-
- try
- Xpath 16384 " X: 16384
- call E("continue")
- Xpath 32768 " X: 0
- finally
- try
- Xpath 65536 " X: 65536
- call E("break")
- Xpath 131072 " X: 0
- finally
- try
- Xpath 262144 " X: 262144
- call E("return")
- Xpath 524288 " X: 0
- finally
- try
- Xpath 1048576 " X: 1048576
- let g:jump = "finish"
- ExecAsScript E
- Xpath 2097152 " X: 0
- finally
- unlet g:jump
- try
- Xpath 4194304 " X: 4194304
- call E("error")
- Xpath 8388608 " X: 0
- finally
- try
- Xpath 16777216 " X: 16777216
- call E("interrupt")
- Xpath 33554432 " X: 0
- finally
- try
- Xpath 67108864 " X: 67108864
- call E("throw")
- Xpath 134217728 " X: 0
- finally
- Xpath 268435456 " X: 268435456
- delfunction E
- endtry
- endtry
- endtry
- endtry
- endtry
- endtry
- endtry
- Xpath 536870912 " X: 0
-
-endif
-
-Xcheck 357908480
-
-
-"-------------------------------------------------------------------------------
-" Test 39: :finally reason discarded by an interrupt {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by an interrupt in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 4
-
- function! I(jump)
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "return" || a:jump == "finish"
- return
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- "INTERRUPT - discards jump that caused the :finally
- let dummy = 0
- endtry
- elseif loop == 2
- Xloop 1 " X: 0
- endif
- endwhile
- Xloop 2 " X: 0
- endfunction
-
- try
- Xpath 16384 " X: 16384
- call I("continue")
- Xpath 32768 " X: 0
- finally
- try
- Xpath 65536 " X: 65536
- call I("break")
- Xpath 131072 " X: 0
- finally
- try
- Xpath 262144 " X: 262144
- call I("return")
- Xpath 524288 " X: 0
- finally
- try
- Xpath 1048576 " X: 1048576
- let g:jump = "finish"
- ExecAsScript I
- Xpath 2097152 " X: 0
- finally
- unlet g:jump
- try
- Xpath 4194304 " X: 4194304
- call I("error")
- Xpath 8388608 " X: 0
- finally
- try
- Xpath 16777216 " X: 16777216
- call I("interrupt")
- Xpath 33554432 " X: 0
- finally
- try
- Xpath 67108864 " X: 67108864
- call I("throw")
- Xpath 134217728 " X: 0
- finally
- Xpath 268435456 " X: 268435456
- delfunction I
- endtry
- endtry
- endtry
- endtry
- endtry
- endtry
- endtry
- Xpath 536870912 " X: 0
-
-endif
-
-Xcheck 357908480
-
-
-"-------------------------------------------------------------------------------
-" Test 40: :finally reason discarded by :throw {{{1
-"
-" When a :finally clause is executed due to a :continue, :break,
-" :return, :finish, error, interrupt or :throw, the jump reason is
-" discarded by a :throw in the finally clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 4
-
- function! T(jump)
- XloopNEXT
- let loop = 0
- while loop < 2
- let loop = loop + 1
- if loop == 1
- try
- if a:jump == "continue"
- continue
- elseif a:jump == "break"
- break
- elseif a:jump == "return" || a:jump == "finish"
- return
- elseif a:jump == "error"
- asdf
- elseif a:jump == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:jump == "throw"
- throw "abc"
- endif
- finally
- throw "xyz" " discards jump that caused the :finally
- endtry
- elseif loop == 2
- Xloop 1 " X: 0
- endif
- endwhile
- Xloop 2 " X: 0
- endfunction
-
- try
- Xpath 16384 " X: 16384
- call T("continue")
- Xpath 32768 " X: 0
- finally
- try
- Xpath 65536 " X: 65536
- call T("break")
- Xpath 131072 " X: 0
- finally
- try
- Xpath 262144 " X: 262144
- call T("return")
- Xpath 524288 " X: 0
- finally
- try
- Xpath 1048576 " X: 1048576
- let g:jump = "finish"
- ExecAsScript T
- Xpath 2097152 " X: 0
- finally
- unlet g:jump
- try
- Xpath 4194304 " X: 4194304
- call T("error")
- Xpath 8388608 " X: 0
- finally
- try
- Xpath 16777216 " X: 16777216
- call T("interrupt")
- Xpath 33554432 " X: 0
- finally
- try
- Xpath 67108864 " X: 67108864
- call T("throw")
- Xpath 134217728 " X: 0
- finally
- Xpath 268435456 " X: 268435456
- delfunction T
- endtry
- endtry
- endtry
- endtry
- endtry
- endtry
- endtry
- Xpath 536870912 " X: 0
-
-endif
-
-Xcheck 357908480
-
-" Tests 41 to 48 were moved to test_trycatch.vim
-let Xtest = 49
-
-
-"-------------------------------------------------------------------------------
-" Test 49: Throwing exceptions across functions {{{1
-"
-" When an exception is thrown but not caught inside a function, the
-" caller is checked for a matching :catch clause.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! C()
- try
- Xpath 1 " X: 1
- throw "arrgh"
- Xpath 2 " X: 0
- catch /arrgh/
- Xpath 4 " X: 4
- endtry
- Xpath 8 " X: 8
-endfunction
-
-XloopINIT! 16 16
-
-function! T1()
- XloopNEXT
- try
- Xloop 1 " X: 16 + 16*16
- throw "arrgh"
- Xloop 2 " X: 0
- finally
- Xloop 4 " X: 64 + 64*16
- endtry
- Xloop 8 " X: 0
-endfunction
-
-function! T2()
- try
- Xpath 4096 " X: 4096
- call T1()
- Xpath 8192 " X: 0
- finally
- Xpath 16384 " X: 16384
- endtry
- Xpath 32768 " X: 0
-endfunction
-
-try
- Xpath 65536 " X: 65536
- call C() " throw and catch
- Xpath 131072 " X: 131072
-catch /.*/
- Xpath 262144 " X: 0
- Xout v:exception "in" v:throwpoint
-endtry
-
-try
- Xpath 524288 " X: 524288
- call T1() " throw, one level
- Xpath 1048576 " X: 0
-catch /arrgh/
- Xpath 2097152 " X: 2097152
-catch /.*/
- Xpath 4194304 " X: 0
- Xout v:exception "in" v:throwpoint
-endtry
-
-try
- Xpath 8388608 " X: 8388608
- call T2() " throw, two levels
- Xpath 16777216 " X: 0
-catch /arrgh/
- Xpath 33554432 " X: 33554432
-catch /.*/
- Xpath 67108864 " X: 0
- Xout v:exception "in" v:throwpoint
-endtry
-Xpath 134217728 " X: 134217728
-
-Xcheck 179000669
-
-" Leave C, T1, and T2 for execution as scripts in the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 50: Throwing exceptions across script files {{{1
-"
-" When an exception is thrown but not caught inside a script file,
-" the sourcing script or function is checked for a matching :catch
-" clause.
-"
-" This test executes the bodies of the functions C, T1, and T2 from
-" the previous test as script files (:return replaced by :finish).
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let scriptC = MakeScript("C") " X: 1 + 4 + 8
-delfunction C
-
-XloopINIT! 16 16
-
-let scriptT1 = MakeScript("T1") " X: 16 + 64 + 16*16 + 64*16
-delfunction T1
-
-let scriptT2 = MakeScript("T2", scriptT1) " X: 4096 + 16384
-delfunction T2
-
-function! F()
- try
- Xpath 65536 " X: 65536
- exec "source" g:scriptC
- Xpath 131072 " X: 131072
- catch /.*/
- Xpath 262144 " X: 0
- Xout v:exception "in" v:throwpoint
- endtry
-
- try
- Xpath 524288 " X: 524288
- exec "source" g:scriptT1
- Xpath 1048576 " X: 0
- catch /arrgh/
- Xpath 2097152 " X: 2097152
- catch /.*/
- Xpath 4194304 " X: 0
- Xout v:exception "in" v:throwpoint
- endtry
-endfunction
-
-try
- Xpath 8388608 " X: 8388608
- call F()
- Xpath 16777216 " X: 16777216
- exec "source" scriptT2
- Xpath 33554432 " X: 0
-catch /arrgh/
- Xpath 67108864 " X: 67108864
-catch /.*/
- Xpath 134217728 " X: 0
- Xout v:exception "in" v:throwpoint
-endtry
-Xpath 268435456 " X: 268435456
-
-call delete(scriptC)
-call delete(scriptT1)
-call delete(scriptT2)
-unlet scriptC scriptT1 scriptT2
-delfunction F
-
-Xcheck 363550045
-
-" Test 51 was moved to test_trycatch.vim
-let Xtest = 52
-
-
-"-------------------------------------------------------------------------------
-" Test 52: Uncaught exceptions {{{1
-"
-" When an exception is thrown but not caught, an error message is
-" displayed when the script is terminated. In case of an interrupt
-" or error exception, the normal interrupt or error message(s) are
-" displayed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
function! MESSAGES(...)
try
exec "edit" g:msgfile
@@ -2302,3627 +648,11 @@ function! MESSAGES(...)
return match
endfunction
-if ExtraVim(msgfile)
- Xpath 1 " X: 1
- throw "arrgh"
-endif
-
-Xpath 2 " X: 2
-if !MESSAGES('E605', "Exception not caught")
- Xpath 4 " X: 0
-endif
-
-if ExtraVim(msgfile)
- try
- Xpath 8 " X: 8
- throw "oops"
- catch /arrgh/
- Xpath 16 " X: 0
- endtry
- Xpath 32 " X: 0
-endif
-
-Xpath 64 " X: 64
-if !MESSAGES('E605', "Exception not caught")
- Xpath 128 " X: 0
-endif
-
-if ExtraVim(msgfile)
- function! T()
- throw "brrr"
- endfunction
-
- try
- Xpath 256 " X: 256
- throw "arrgh"
- catch /.*/
- Xpath 512 " X: 512
- call T()
- endtry
- Xpath 1024 " X: 0
-endif
-
-Xpath 2048 " X: 2048
-if !MESSAGES('E605', "Exception not caught")
- Xpath 4096 " X: 0
-endif
-
-if ExtraVim(msgfile)
- try
- Xpath 8192 " X: 8192
- throw "arrgh"
- finally
- Xpath 16384 " X: 16384
- throw "brrr"
- endtry
- Xpath 32768 " X: 0
-endif
-
-Xpath 65536 " X: 65536
-if !MESSAGES('E605', "Exception not caught")
- Xpath 131072 " X: 0
-endif
-
-if ExtraVim(msgfile)
- try
- Xpath 262144 " X: 262144
- "INTERRUPT
- endtry
- Xpath 524288 " X: 0
-endif
-
-Xpath 1048576 " X: 1048576
-if !MESSAGES('INT', "Interrupted")
- Xpath 2097152 " X: 0
-endif
-
-if ExtraVim(msgfile)
- try
- Xpath 4194304 " X: 4194304
- let x = novar " error E121/E15; exception: E121
- catch /E15:/ " should not catch
- Xpath 8388608 " X: 0
- endtry
- Xpath 16777216 " X: 0
-endif
-
-Xpath 33554432 " X: 33554432
-if !MESSAGES('E121', "Undefined variable", 'E15', "Invalid expression")
- Xpath 67108864 " X: 0
-endif
-
-if ExtraVim(msgfile)
- try
- Xpath 134217728 " X: 134217728
-" unlet novar # " error E108/E488; exception: E488
- catch /E108:/ " should not catch
- Xpath 268435456 " X: 0
- endtry
- Xpath 536870912 " X: 0
-endif
-
-Xpath 1073741824 " X: 1073741824
-if !MESSAGES('E108', "No such variable", 'E488', "Trailing characters")
- " The Xpath command does not accept 2^31 (negative); add explicitly:
- let Xpath = Xpath + 2147483648 " X: 0
-endif
-
-call delete(msgfile)
-unlet msgfile
-
-Xcheck 1247112011
-
-" Leave MESSAGES() for the next tests.
-
-
-"-------------------------------------------------------------------------------
-" Test 53: Nesting errors: :endif/:else/:elseif {{{1
-"
-" For nesting errors of :if conditionals the correct error messages
-" should be given.
-"
-" This test reuses the function MESSAGES() from the previous test.
-" This functions checks the messages in g:msgfile.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
-if ExtraVim(msgfile)
-" endif
-endif
-if MESSAGES('E580', ":endif without :if")
- Xpath 1 " X: 1
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" endif
-" endwhile
-endif
-if MESSAGES('E580', ":endif without :if")
- Xpath 2 " X: 2
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" endif
-" endtry
-endif
-if MESSAGES('E580', ":endif without :if")
- Xpath 4 " X: 4
-endif
-
-if ExtraVim(msgfile)
-" try
-" endif
-" endtry
-endif
-if MESSAGES('E580', ":endif without :if")
- Xpath 8 " X: 8
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" endif
-" endtry
-endif
-if MESSAGES('E580', ":endif without :if")
- Xpath 16 " X: 16
-endif
-
-if ExtraVim(msgfile)
-" else
-endif
-if MESSAGES('E581', ":else without :if")
- Xpath 32 " X: 32
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" else
-" endwhile
-endif
-if MESSAGES('E581', ":else without :if")
- Xpath 64 " X: 64
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" else
-" endtry
-endif
-if MESSAGES('E581', ":else without :if")
- Xpath 128 " X: 128
-endif
-
-if ExtraVim(msgfile)
-" try
-" else
-" endtry
-endif
-if MESSAGES('E581', ":else without :if")
- Xpath 256 " X: 256
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" else
-" endtry
-endif
-if MESSAGES('E581', ":else without :if")
- Xpath 512 " X: 512
-endif
-
-if ExtraVim(msgfile)
-" elseif
-endif
-if MESSAGES('E582', ":elseif without :if")
- Xpath 1024 " X: 1024
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" elseif
-" endwhile
-endif
-if MESSAGES('E582', ":elseif without :if")
- Xpath 2048 " X: 2048
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" elseif
-" endtry
-endif
-if MESSAGES('E582', ":elseif without :if")
- Xpath 4096 " X: 4096
-endif
-
-if ExtraVim(msgfile)
-" try
-" elseif
-" endtry
-endif
-if MESSAGES('E582', ":elseif without :if")
- Xpath 8192 " X: 8192
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" elseif
-" endtry
-endif
-if MESSAGES('E582', ":elseif without :if")
- Xpath 16384 " X: 16384
-endif
-
-if ExtraVim(msgfile)
-" if 1
-" else
-" else
-" endif
-endif
-if MESSAGES('E583', "multiple :else")
- Xpath 32768 " X: 32768
-endif
-
-if ExtraVim(msgfile)
-" if 1
-" else
-" elseif 1
-" endif
-endif
-if MESSAGES('E584', ":elseif after :else")
- Xpath 65536 " X: 65536
-endif
-
-call delete(msgfile)
-unlet msgfile
-
-Xcheck 131071
-
-" Leave MESSAGES() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 54: Nesting errors: :while/:endwhile {{{1
-"
-" For nesting errors of :while conditionals the correct error messages
-" should be given.
-"
-" This test reuses the function MESSAGES() from the previous test.
-" This functions checks the messages in g:msgfile.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
-if ExtraVim(msgfile)
-" endwhile
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 1 " X: 1
-endif
-
-if ExtraVim(msgfile)
-" if 1
-" endwhile
-" endif
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 2 " X: 2
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" if 1
-" endwhile
-endif
-if MESSAGES('E171', "Missing :endif")
- Xpath 4 " X: 4
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" endwhile
-" endtry
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 8 " X: 8
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" try
-" finally
-" endwhile
-endif
-if MESSAGES('E600', "Missing :endtry")
- Xpath 16 " X: 16
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" if 1
-" try
-" finally
-" endwhile
-endif
-if MESSAGES('E600', "Missing :endtry")
- Xpath 32 " X: 32
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" try
-" finally
-" if 1
-" endwhile
-endif
-if MESSAGES('E171', "Missing :endif")
- Xpath 64 " X: 64
-endif
-
-if ExtraVim(msgfile)
-" try
-" endwhile
-" endtry
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 128 " X: 128
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" try
-" endwhile
-" endtry
-" endwhile
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 256 " X: 256
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" endwhile
-" endtry
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 512 " X: 512
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" try
-" throw "a"
-" catch /a/
-" endwhile
-" endtry
-" endwhile
-endif
-if MESSAGES('E588', ":endwhile without :while")
- Xpath 1024 " X: 1024
-endif
-
-
-call delete(msgfile)
-unlet msgfile
-
-Xcheck 2047
-
-" Leave MESSAGES() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 55: Nesting errors: :continue/:break {{{1
-"
-" For nesting errors of :continue and :break commands the correct
-" error messages should be given.
-"
-" This test reuses the function MESSAGES() from the previous test.
-" This functions checks the messages in g:msgfile.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
-if ExtraVim(msgfile)
-" continue
-endif
-if MESSAGES('E586', ":continue without :while")
- Xpath 1 " X: 1
-endif
-
-if ExtraVim(msgfile)
-" if 1
-" continue
-" endif
-endif
-if MESSAGES('E586', ":continue without :while")
- Xpath 2 " X: 2
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" continue
-" endtry
-endif
-if MESSAGES('E586', ":continue without :while")
- Xpath 4 " X: 4
-endif
-
-if ExtraVim(msgfile)
-" try
-" continue
-" endtry
-endif
-if MESSAGES('E586', ":continue without :while")
- Xpath 8 " X: 8
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" continue
-" endtry
-endif
-if MESSAGES('E586', ":continue without :while")
- Xpath 16 " X: 16
-endif
-
-if ExtraVim(msgfile)
-" break
-endif
-if MESSAGES('E587', ":break without :while")
- Xpath 32 " X: 32
-endif
-
-if ExtraVim(msgfile)
-" if 1
-" break
-" endif
-endif
-if MESSAGES('E587', ":break without :while")
- Xpath 64 " X: 64
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" break
-" endtry
-endif
-if MESSAGES('E587', ":break without :while")
- Xpath 128 " X: 128
-endif
-
-if ExtraVim(msgfile)
-" try
-" break
-" endtry
-endif
-if MESSAGES('E587', ":break without :while")
- Xpath 256 " X: 256
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" break
-" endtry
-endif
-if MESSAGES('E587', ":break without :while")
- Xpath 512 " X: 512
-endif
-
-call delete(msgfile)
-unlet msgfile
-
-Xcheck 1023
-
-" Leave MESSAGES() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 56: Nesting errors: :endtry {{{1
-"
-" For nesting errors of :try conditionals the correct error messages
-" should be given.
-"
-" This test reuses the function MESSAGES() from the previous test.
-" This functions checks the messages in g:msgfile.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let msgfile = tempname()
-
-if ExtraVim(msgfile)
-" endtry
-endif
-if MESSAGES('E602', ":endtry without :try")
- Xpath 1 " X: 1
-endif
-
-if ExtraVim(msgfile)
-" if 1
-" endtry
-" endif
-endif
-if MESSAGES('E602', ":endtry without :try")
- Xpath 2 " X: 2
-endif
-
-if ExtraVim(msgfile)
-" while 1
-" endtry
-" endwhile
-endif
-if MESSAGES('E602', ":endtry without :try")
- Xpath 4 " X: 4
-endif
-
-if ExtraVim(msgfile)
-" try
-" if 1
-" endtry
-endif
-if MESSAGES('E171', "Missing :endif")
- Xpath 8 " X: 8
-endif
-
-if ExtraVim(msgfile)
-" try
-" while 1
-" endtry
-endif
-if MESSAGES('E170', "Missing :endwhile")
- Xpath 16 " X: 16
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" if 1
-" endtry
-endif
-if MESSAGES('E171', "Missing :endif")
- Xpath 32 " X: 32
-endif
-
-if ExtraVim(msgfile)
-" try
-" finally
-" while 1
-" endtry
-endif
-if MESSAGES('E170', "Missing :endwhile")
- Xpath 64 " X: 64
-endif
-
-if ExtraVim(msgfile)
- try
- Xpath 4194304 " X: 4194304
- let x = novar " error E121; exception: E121
- catch /E15:/ " should not catch
- Xpath 8388608 " X: 0
- endtry
- Xpath 16777216 " X: 0
-endif
-
-Xpath 33554432 " X: 33554432
-if !MESSAGES('E121', "Undefined variable")
- Xpath 67108864 " X: 0
-endif
-
-if ExtraVim(msgfile)
-" try
-" throw "a"
-" catch /a/
-" while 1
-" endtry
-endif
-if MESSAGES('E170', "Missing :endwhile")
- Xpath 256 " X: 256
-endif
-
-call delete(msgfile)
-unlet msgfile
-
-delfunction MESSAGES
-
-Xcheck 511
-
-
-"-------------------------------------------------------------------------------
-" Test 57: v:exception and v:throwpoint for user exceptions {{{1
-"
-" v:exception evaluates to the value of the exception that was caught
-" most recently and is not finished. (A caught exception is finished
-" when the next ":catch", ":finally", or ":endtry" is reached.)
-" v:throwpoint evaluates to the script/function name and line number
-" where that exception has been thrown.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! FuncException()
- let g:exception = v:exception
-endfunction
-
-function! FuncThrowpoint()
- let g:throwpoint = v:throwpoint
-endfunction
-
-let scriptException = MakeScript("FuncException")
-let scriptThrowPoint = MakeScript("FuncThrowpoint")
-
-command! CmdException let g:exception = v:exception
-command! CmdThrowpoint let g:throwpoint = v:throwpoint
-
-XloopINIT! 1 2
-
-function! CHECK(n, exception, throwname, throwline)
- XloopNEXT
- let error = 0
- if v:exception != a:exception
- Xout a:n.": v:exception is" v:exception "instead of" a:exception
- let error = 1
- endif
- if v:throwpoint !~ a:throwname
- let name = escape(a:throwname, '\')
- Xout a:n.": v:throwpoint (".v:throwpoint.") does not match" name
- let error = 1
- endif
- if v:throwpoint !~ a:throwline
- let line = escape(a:throwline, '\')
- Xout a:n.": v:throwpoint (".v:throwpoint.") does not match" line
- let error = 1
- endif
- if error
- Xloop 1 " X: 0
- endif
-endfunction
-
-function! T(arg, line)
- if a:line == 2
- throw a:arg " in line 2
- elseif a:line == 4
- throw a:arg " in line 4
- elseif a:line == 6
- throw a:arg " in line 6
- elseif a:line == 8
- throw a:arg " in line 8
- endif
-endfunction
-
-function! G(arg, line)
- call T(a:arg, a:line)
-endfunction
-
-function! F(arg, line)
- call G(a:arg, a:line)
-endfunction
-
-let scriptT = MakeScript("T")
-let scriptG = MakeScript("G", scriptT)
-let scriptF = MakeScript("F", scriptG)
-
-try
- Xpath 32768 " X: 32768
- call F("oops", 2)
-catch /.*/
- Xpath 65536 " X: 65536
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(1, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
- exec "let exception = v:exception"
- exec "let throwpoint = v:throwpoint"
- call CHECK(2, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
- CmdException
- CmdThrowpoint
- call CHECK(3, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
- call FuncException()
- call FuncThrowpoint()
- call CHECK(4, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
- exec "source" scriptException
- exec "source" scriptThrowPoint
- call CHECK(5, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
- try
- Xpath 131072 " X: 131072
- call G("arrgh", 4)
- catch /.*/
- Xpath 262144 " X: 262144
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(6, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
- try
- Xpath 524288 " X: 524288
- let g:arg = "autsch"
- let g:line = 6
- exec "source" scriptF
- catch /.*/
- Xpath 1048576 " X: 1048576
- let exception = v:exception
- let throwpoint = v:throwpoint
- " Symbolic links in tempname()s are not resolved, whereas resolving
- " is done for v:throwpoint. Resolve the temporary file name for
- " scriptT, so that it can be matched against v:throwpoint.
- call CHECK(7, "autsch", resolve(scriptT), '\<6\>')
- finally
- Xpath 2097152 " X: 2097152
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(8, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
- try
- Xpath 4194304 " X: 4194304
- let g:arg = "brrrr"
- let g:line = 8
- exec "source" scriptG
- catch /.*/
- Xpath 8388608 " X: 8388608
- let exception = v:exception
- let throwpoint = v:throwpoint
- " Resolve scriptT for matching it against v:throwpoint.
- call CHECK(9, "brrrr", resolve(scriptT), '\<8\>')
- finally
- Xpath 16777216 " X: 16777216
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(10, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
- endtry
- Xpath 33554432 " X: 33554432
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(11, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
- endtry
- Xpath 67108864 " X: 67108864
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(12, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
- finally
- Xpath 134217728 " X: 134217728
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(13, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
- endtry
- Xpath 268435456 " X: 268435456
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(14, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
-finally
- Xpath 536870912 " X: 536870912
- let exception = v:exception
- let throwpoint = v:throwpoint
- call CHECK(15, "", '^$', '^$')
-endtry
-
-Xpath 1073741824 " X: 1073741824
-
-unlet exception throwpoint
-delfunction FuncException
-delfunction FuncThrowpoint
-call delete(scriptException)
-call delete(scriptThrowPoint)
-unlet scriptException scriptThrowPoint
-delcommand CmdException
-delcommand CmdThrowpoint
-delfunction T
-delfunction G
-delfunction F
-call delete(scriptT)
-call delete(scriptG)
-call delete(scriptF)
-unlet scriptT scriptG scriptF
-
-Xcheck 2147450880
-
-
-"-------------------------------------------------------------------------------
-"
-" Test 58: v:exception and v:throwpoint for error/interrupt exceptions {{{1
-"
-" v:exception and v:throwpoint work also for error and interrupt
-" exceptions.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- function! T(line)
- if a:line == 2
- delfunction T " error (function in use) in line 2
- elseif a:line == 4
- let dummy = 0 " INTERRUPT1 - interrupt in line 4
- endif
- endfunction
-
- while 1
- try
- Xpath 1 " X: 1
- let caught = 0
- call T(2)
- catch /.*/
- let caught = 1
- if v:exception !~ 'Vim(delfunction):'
- Xpath 2 " X: 0
- endif
- if v:throwpoint !~ '\<T\>'
- Xpath 4 " X: 0
- endif
- if v:throwpoint !~ '\<2\>'
- Xpath 8 " X: 0
- endif
- finally
- Xpath 16 " X: 16
- if caught || $VIMNOERRTHROW
- Xpath 32 " X: 32
- endif
- if v:exception != ""
- Xpath 64 " X: 0
- endif
- if v:throwpoint != ""
- Xpath 128 " X: 0
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- Xpath 256 " X: 256
- if v:exception != ""
- Xpath 512 " X: 0
- endif
- if v:throwpoint != ""
- Xpath 1024 " X: 0
- endif
-
- while 1
- try
- Xpath 2048 " X: 2048
- let caught = 0
- call T(4)
- catch /.*/
- let caught = 1
- if v:exception != 'Vim:Interrupt'
- Xpath 4096 " X: 0
- endif
- if v:throwpoint !~ '\<T\>'
- Xpath 8192 " X: 0
- endif
- if v:throwpoint !~ '\<4\>'
- Xpath 16384 " X: 0
- endif
- finally
- Xpath 32768 " X: 32768
- if caught || $VIMNOINTTHROW
- Xpath 65536 " X: 65536
- endif
- if v:exception != ""
- Xpath 131072 " X: 0
- endif
- if v:throwpoint != ""
- Xpath 262144 " X: 0
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- Xpath 524288 " X: 524288
- if v:exception != ""
- Xpath 1048576 " X: 0
- endif
- if v:throwpoint != ""
- Xpath 2097152 " X: 0
- endif
-
-endif
-
-Xcheck 624945
-
-
-"-------------------------------------------------------------------------------
-"
-" Test 59: v:exception and v:throwpoint when discarding exceptions {{{1
-"
-" When a :catch clause is left by a ":break" etc or an error or
-" interrupt exception, v:exception and v:throwpoint are reset. They
-" are not affected by an exception that is discarded before being
-" caught.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- XloopINIT! 1 2
-
- let sfile = expand("<sfile>")
-
- function! LineNumber()
- return substitute(substitute(v:throwpoint, g:sfile, '', ""),
- \ '\D*\(\d*\).*', '\1', "")
- endfunction
-
- command! -nargs=1 SetLineNumber
- \ try | throw "line" | catch /.*/ | let <args> = LineNumber() | endtry
-
- " Check v:exception/v:throwpoint against second/fourth parameter if
- " specified, check for being empty else.
- function! CHECK(n, ...)
- XloopNEXT
- let exception = a:0 != 0 ? a:1 : "" " second parameter (optional)
- let emsg = a:0 != 0 ? a:2 : "" " third parameter (optional)
- let line = a:0 != 0 ? a:3 : 0 " fourth parameter (optional)
- let error = 0
- if emsg != ""
- " exception is the error number, emsg the English error message text
- if exception !~ '^E\d\+$'
- Xout "TODO: Add message number for:" emsg
- elseif v:lang == "C" || v:lang =~ '^[Ee]n'
- if exception == "E492" && emsg == "Not an editor command"
- let exception = '^Vim:' . exception . ': ' . emsg
- else
- let exception = '^Vim(\a\+):' . exception . ': ' . emsg
- endif
- else
- if exception == "E492"
- let exception = '^Vim:' . exception
- else
- let exception = '^Vim(\a\+):' . exception
- endif
- endif
- endif
- if exception == "" && v:exception != ""
- Xout a:n.": v:exception is set:" v:exception
- let error = 1
- elseif exception != "" && v:exception !~ exception
- Xout a:n.": v:exception (".v:exception.") does not match" exception
- let error = 1
- endif
- if line == 0 && v:throwpoint != ""
- Xout a:n.": v:throwpoint is set:" v:throwpoint
- let error = 1
- elseif line != 0 && v:throwpoint !~ '\<' . line . '\>'
- Xout a:n.": v:throwpoint (".v:throwpoint.") does not match" line
- let error = 1
- endif
- if !error
- Xloop 1 " X: 2097151
- endif
- endfunction
-
- while 1
- try
- throw "x1"
- catch /.*/
- break
- endtry
- endwhile
- call CHECK(1)
-
- while 1
- try
- throw "x2"
- catch /.*/
- break
- finally
- call CHECK(2)
- endtry
- break
- endwhile
- call CHECK(3)
-
- while 1
- try
- let errcaught = 0
- try
- try
- throw "x3"
- catch /.*/
- SetLineNumber line_before_error
- asdf
- endtry
- catch /.*/
- let errcaught = 1
- call CHECK(4, 'E492', "Not an editor command",
- \ line_before_error + 1)
- endtry
- finally
- if !errcaught && $VIMNOERRTHROW
- call CHECK(4)
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
- call CHECK(5)
-
- Xpath 2097152 " X: 2097152
-
- while 1
- try
- let intcaught = 0
- try
- try
- throw "x4"
- catch /.*/
- SetLineNumber two_lines_before_interrupt
- "INTERRUPT
- let dummy = 0
- endtry
- catch /.*/
- let intcaught = 1
- call CHECK(6, "Vim:Interrupt", '',
- \ two_lines_before_interrupt + 2)
- endtry
- finally
- if !intcaught && $VIMNOINTTHROW
- call CHECK(6)
- endif
- break " discard interrupt for $VIMNOINTTHROW
- endtry
- endwhile
- call CHECK(7)
-
- Xpath 4194304 " X: 4194304
-
- while 1
- try
- let errcaught = 0
- try
- try
-" if 1
- SetLineNumber line_before_throw
- throw "x5"
- " missing endif
- catch /.*/
- Xpath 8388608 " X: 0
- endtry
- catch /.*/
- let errcaught = 1
- call CHECK(8, 'E171', "Missing :endif", line_before_throw + 3)
- endtry
- finally
- if !errcaught && $VIMNOERRTHROW
- call CHECK(8)
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
- call CHECK(9)
-
- Xpath 16777216 " X: 16777216
-
- try
- while 1
- try
- throw "x6"
- finally
- break
- endtry
- break
- endwhile
- catch /.*/
- Xpath 33554432 " X: 0
- endtry
- call CHECK(10)
-
- try
- while 1
- try
- throw "x7"
- finally
- break
- endtry
- break
- endwhile
- catch /.*/
- Xpath 67108864 " X: 0
- finally
- call CHECK(11)
- endtry
- call CHECK(12)
-
- while 1
- try
- let errcaught = 0
- try
- try
- throw "x8"
- finally
- SetLineNumber line_before_error
- asdf
- endtry
- catch /.*/
- let errcaught = 1
- call CHECK(13, 'E492', "Not an editor command",
- \ line_before_error + 1)
- endtry
- finally
- if !errcaught && $VIMNOERRTHROW
- call CHECK(13)
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
- call CHECK(14)
-
- Xpath 134217728 " X: 134217728
-
- while 1
- try
- let intcaught = 0
- try
- try
- throw "x9"
- finally
- SetLineNumber two_lines_before_interrupt
- "INTERRUPT
- endtry
- catch /.*/
- let intcaught = 1
- call CHECK(15, "Vim:Interrupt", '',
- \ two_lines_before_interrupt + 2)
- endtry
- finally
- if !intcaught && $VIMNOINTTHROW
- call CHECK(15)
- endif
- break " discard interrupt for $VIMNOINTTHROW
- endtry
- endwhile
- call CHECK(16)
-
- Xpath 268435456 " X: 268435456
-
- while 1
- try
- let errcaught = 0
- try
- try
-" if 1
- SetLineNumber line_before_throw
- throw "x10"
- " missing endif
- finally
- call CHECK(17)
- endtry
- catch /.*/
- let errcaught = 1
- call CHECK(18, 'E171', "Missing :endif", line_before_throw + 3)
- endtry
- finally
- if !errcaught && $VIMNOERRTHROW
- call CHECK(18)
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
- call CHECK(19)
-
- Xpath 536870912 " X: 536870912
-
- while 1
- try
- let errcaught = 0
- try
- try
-" if 1
- SetLineNumber line_before_throw
- throw "x11"
- " missing endif
- endtry
- catch /.*/
- let errcaught = 1
- call CHECK(20, 'E171', "Missing :endif", line_before_throw + 3)
- endtry
- finally
- if !errcaught && $VIMNOERRTHROW
- call CHECK(20)
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
- call CHECK(21)
-
- Xpath 1073741824 " X: 1073741824
-
-endif
-
-Xcheck 2038431743
-
-
-"-------------------------------------------------------------------------------
-"
-" Test 60: (Re)throwing v:exception; :echoerr. {{{1
-"
-" A user exception can be rethrown after catching by throwing
-" v:exception. An error or interrupt exception cannot be rethrown
-" because Vim exceptions cannot be faked. A Vim exception using the
-" value of v:exception can, however, be triggered by the :echoerr
-" command.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-try
- try
- Xpath 1 " X: 1
- throw "oops"
- catch /oops/
- Xpath 2 " X: 2
- throw v:exception " rethrow user exception
- catch /.*/
- Xpath 4 " X: 0
- endtry
-catch /^oops$/ " catches rethrown user exception
- Xpath 8 " X: 8
-catch /.*/
- Xpath 16 " X: 0
-endtry
-
-function! F()
- try
- let caught = 0
- try
- Xpath 32 " X: 32
- write /n/o/n/w/r/i/t/a/b/l/e/_/f/i/l/e
- Xpath 64 " X: 0
- Xout "did_emsg was reset before executing " .
- \ "BufWritePost autocommands."
- catch /^Vim(write):/
- let caught = 1
- throw v:exception " throw error: cannot fake Vim exception
- catch /.*/
- Xpath 128 " X: 0
- finally
- Xpath 256 " X: 256
- if !caught && !$VIMNOERRTHROW
- Xpath 512 " X: 0
- endif
- endtry
- catch /^Vim(throw):/ " catches throw error
- let caught = caught + 1
- catch /.*/
- Xpath 1024 " X: 0
- finally
- Xpath 2048 " X: 2048
- if caught != 2
- if !caught && !$VIMNOERRTHROW
- Xpath 4096 " X: 0
- elseif caught
- Xpath 8192 " X: 0
- endif
- return | " discard error for $VIMNOERRTHROW
- endif
- endtry
-endfunction
-
-call F()
-delfunction F
-
-function! G()
- try
- let caught = 0
- try
- Xpath 16384 " X: 16384
- asdf
- catch /^Vim/ " catch error exception
- let caught = 1
- " Trigger Vim error exception with value specified after :echoerr
- let value = substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
- echoerr value
- catch /.*/
- Xpath 32768 " X: 0
- finally
- Xpath 65536 " X: 65536
- if !caught
- if !$VIMNOERRTHROW
- Xpath 131072 " X: 0
- else
- let value = "Error"
- echoerr value
- endif
- endif
- endtry
- catch /^Vim(echoerr):/
- let caught = caught + 1
- if v:exception !~ value
- Xpath 262144 " X: 0
- endif
- catch /.*/
- Xpath 524288 " X: 0
- finally
- Xpath 1048576 " X: 1048576
- if caught != 2
- if !caught && !$VIMNOERRTHROW
- Xpath 2097152 " X: 0
- elseif caught
- Xpath 4194304 " X: 0
- endif
- return | " discard error for $VIMNOERRTHROW
- endif
- endtry
-endfunction
-
-call G()
-delfunction G
-
-unlet! value caught
-
-if ExtraVim()
- try
- let errcaught = 0
- try
- Xpath 8388608 " X: 8388608
- let intcaught = 0
- "INTERRUPT
- catch /^Vim:/ " catch interrupt exception
- let intcaught = 1
- " Trigger Vim error exception with value specified after :echoerr
- echoerr substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
- catch /.*/
- Xpath 16777216 " X: 0
- finally
- Xpath 33554432 " X: 33554432
- if !intcaught
- if !$VIMNOINTTHROW
- Xpath 67108864 " X: 0
- else
- echoerr "Interrupt"
- endif
- endif
- endtry
- catch /^Vim(echoerr):/
- let errcaught = 1
- if v:exception !~ "Interrupt"
- Xpath 134217728 " X: 0
- endif
- finally
- Xpath 268435456 " X: 268435456
- if !errcaught && !$VIMNOERRTHROW
- Xpath 536870912 " X: 0
- endif
- endtry
-endif
-
-Xcheck 311511339
-
-
-"-------------------------------------------------------------------------------
-" Test 61: Catching interrupt exceptions {{{1
-"
-" When an interrupt occurs inside a :try/:endtry region, an
-" interrupt exception is thrown and can be caught. Its value is
-" "Vim:Interrupt". If the interrupt occurs after an error or a :throw
-" but before a matching :catch is reached, all following :catches of
-" that try block are ignored, but the interrupt exception can be
-" caught by the next surrounding try conditional. An interrupt is
-" ignored when there is a previous interrupt that has not been caught
-" or causes a :finally clause to be executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- while 1
- try
- try
- Xpath 1 " X: 1
- let caught = 0
- "INTERRUPT
- Xpath 2 " X: 0
- catch /^Vim:Interrupt$/
- let caught = 1
- finally
- Xpath 4 " X: 4
- if caught || $VIMNOINTTHROW
- Xpath 8 " X: 8
- endif
- endtry
- catch /.*/
- Xpath 16 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard interrupt for $VIMNOINTTHROW
- endtry
- endwhile
-
- while 1
- try
- try
- let caught = 0
- try
- Xpath 32 " X: 32
- asdf
- Xpath 64 " X: 0
- catch /do_not_catch/
- Xpath 128 " X: 0
- catch /.*/ "INTERRUPT - throw interrupt if !$VIMNOERRTHROW
- Xpath 256 " X: 0
- catch /.*/
- Xpath 512 " X: 0
- finally "INTERRUPT - throw interrupt if $VIMNOERRTHROW
- Xpath 1024 " X: 1024
- endtry
- catch /^Vim:Interrupt$/
- let caught = 1
- finally
- Xpath 2048 " X: 2048
- if caught || $VIMNOINTTHROW
- Xpath 4096 " X: 4096
- endif
- endtry
- catch /.*/
- Xpath 8192 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard interrupt for $VIMNOINTTHROW
- endtry
- endwhile
-
- while 1
- try
- try
- let caught = 0
- try
- Xpath 16384 " X: 16384
- throw "x"
- Xpath 32768 " X: 0
- catch /do_not_catch/
- Xpath 65536 " X: 0
- catch /x/ "INTERRUPT
- Xpath 131072 " X: 0
- catch /.*/
- Xpath 262144 " X: 0
- endtry
- catch /^Vim:Interrupt$/
- let caught = 1
- finally
- Xpath 524288 " X: 524288
- if caught || $VIMNOINTTHROW
- Xpath 1048576 " X: 1048576
- endif
- endtry
- catch /.*/
- Xpath 2097152 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard interrupt for $VIMNOINTTHROW
- endtry
- endwhile
-
- while 1
- try
- let caught = 0
- try
- Xpath 4194304 " X: 4194304
- "INTERRUPT
- Xpath 8388608 " X: 0
- catch /do_not_catch/ "INTERRUPT
- Xpath 16777216 " X: 0
- catch /^Vim:Interrupt$/
- let caught = 1
- finally
- Xpath 33554432 " X: 33554432
- if caught || $VIMNOINTTHROW
- Xpath 67108864 " X: 67108864
- endif
- endtry
- catch /.*/
- Xpath 134217728 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard interrupt for $VIMNOINTTHROW
- endtry
- endwhile
-
- Xpath 268435456 " X: 268435456
-
-endif
-
-Xcheck 374889517
-
-
-"-------------------------------------------------------------------------------
-" Test 62: Catching error exceptions {{{1
-"
-" An error inside a :try/:endtry region is converted to an exception
-" and can be caught. The error exception has a "Vim(cmdname):" prefix
-" where cmdname is the name of the failing command, or a "Vim:" prefix
-" if no command name is known. The "Vim" prefixes cannot be faked.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! MSG(enr, emsg)
- let english = v:lang == "C" || v:lang =~ '^[Ee]n'
- if a:enr == ""
- Xout "TODO: Add message number for:" a:emsg
- let v:errmsg = ":" . v:errmsg
- endif
- let match = 1
- if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
- let match = 0
- if v:errmsg == ""
- Xout "Message missing."
- else
- let v:errmsg = escape(v:errmsg, '"')
- Xout "Unexpected message:" v:errmsg
- endif
- endif
- return match
-endfunction
-
-while 1
- try
- try
- let caught = 0
- unlet novar
- catch /^Vim(unlet):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(unlet):', '', "")
- finally
- Xpath 1 " X: 1
- if !caught && !$VIMNOERRTHROW
- Xpath 2 " X: 0
- endif
- if !MSG('E108', "No such variable")
- Xpath 4 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 8 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- try
- let caught = 0
- throw novar " error in :throw
- catch /^Vim(throw):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
- finally
- Xpath 16 " X: 16
- if !caught && !$VIMNOERRTHROW
- Xpath 32 " X: 0
- endif
- if caught ? !MSG('E121', "Undefined variable")
- \ : !MSG('E15', "Invalid expression")
- Xpath 64 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 128 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- try
- let caught = 0
- throw "Vim:faked" " error: cannot fake Vim exception
- catch /^Vim(throw):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
- finally
- Xpath 256 " X: 256
- if !caught && !$VIMNOERRTHROW
- Xpath 512 " X: 0
- endif
- if !MSG('E608', "Cannot :throw exceptions with 'Vim' prefix")
- Xpath 1024 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 2048 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-function! F()
- while 1
- " Missing :endwhile
-endfunction
-
-while 1
- try
- try
- let caught = 0
- call F()
- catch /^Vim(endfunction):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(endfunction):', '', "")
- finally
- Xpath 4096 " X: 4096
- if !caught && !$VIMNOERRTHROW
- Xpath 8192 " X: 0
- endif
- if !MSG('E170', "Missing :endwhile")
- Xpath 16384 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 32768 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- try
- let caught = 0
- ExecAsScript F
- catch /^Vim:/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim:', '', "")
- finally
- Xpath 65536 " X: 65536
- if !caught && !$VIMNOERRTHROW
- Xpath 131072 " X: 0
- endif
- if !MSG('E170', "Missing :endwhile")
- Xpath 262144 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 524288 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-function! G()
- call G()
-endfunction
-
-while 1
- try
- let mfd_save = &mfd
- set mfd=3
- try
- let caught = 0
- call G()
- catch /^Vim(call):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(call):', '', "")
- finally
- Xpath 1048576 " X: 1048576
- if !caught && !$VIMNOERRTHROW
- Xpath 2097152 " X: 0
- endif
- if !MSG('E132', "Function call depth is higher than 'maxfuncdepth'")
- Xpath 4194304 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 8388608 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- let &mfd = mfd_save
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-function! H()
- return H()
-endfunction
-
-while 1
- try
- let mfd_save = &mfd
- set mfd=3
- try
- let caught = 0
- call H()
- catch /^Vim(return):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(return):', '', "")
- finally
- Xpath 16777216 " X: 16777216
- if !caught && !$VIMNOERRTHROW
- Xpath 33554432 " X: 0
- endif
- if !MSG('E132', "Function call depth is higher than 'maxfuncdepth'")
- Xpath 67108864 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 134217728 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- let &mfd = mfd_save
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-unlet! caught mfd_save
-delfunction F
-delfunction G
-delfunction H
-Xpath 268435456 " X: 268435456
-
-Xcheck 286331153
-
-" Leave MSG() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 63: Suppressing error exceptions by :silent!. {{{1
-"
-" A :silent! command inside a :try/:endtry region suppresses the
-" conversion of errors to an exception and the immediate abortion on
-" error. When the commands executed by the :silent! themselves open
-" a new :try/:endtry region, conversion of errors to exception and
-" immediate abortion is switched on again - until the next :silent!
-" etc. The :silent! has the effect of setting v:errmsg to the error
-" message text (without displaying it) and continuing with the next
-" script line.
-"
-" When a command triggering autocommands is executed by :silent!
-" inside a :try/:endtry, the autocommand execution is not suppressed
-" on error.
-"
-" This test reuses the function MSG() from the previous test.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-XloopINIT! 1 4
-
-let taken = ""
-
-function! S(n) abort
- XloopNEXT
- let g:taken = g:taken . "E" . a:n
- let v:errmsg = ""
- exec "asdf" . a:n
-
- " Check that ":silent!" continues:
- Xloop 1
-
- " Check that ":silent!" sets "v:errmsg":
- if MSG('E492', "Not an editor command")
- Xloop 2
- endif
-endfunction
-
-function! Foo()
- while 1
- try
- try
- let caught = 0
- " This is not silent:
- call S(3) " X: 0 * 16
- catch /^Vim:/
- let caught = 1
- let errmsg3 = substitute(v:exception, '^Vim:', '', "")
- silent! call S(4) " X: 3 * 64
- finally
- if !caught
- let errmsg3 = v:errmsg
- " Do call S(4) here if not executed in :catch.
- silent! call S(4)
- endif
- Xpath 1048576 " X: 1048576
- if !caught && !$VIMNOERRTHROW
- Xpath 2097152 " X: 0
- endif
- let v:errmsg = errmsg3
- if !MSG('E492', "Not an editor command")
- Xpath 4194304 " X: 0
- endif
- silent! call S(5) " X: 3 * 256
- " Break out of try conditionals that cover ":silent!". This also
- " discards the aborting error when $VIMNOERRTHROW is non-zero.
- break
- endtry
- catch /.*/
- Xpath 8388608 " X: 0
- Xout v:exception "in" v:throwpoint
- endtry
- endwhile
- " This is a double ":silent!" (see caller).
- silent! call S(6) " X: 3 * 1024
-endfunction
-
-function! Bar()
- try
- silent! call S(2) " X: 3 * 4
- " X: 3 * 4096
- silent! execute "call Foo() | call S(7)"
- silent! call S(8) " X: 3 * 16384
- endtry " normal end of try cond that covers ":silent!"
- " This has a ":silent!" from the caller:
- call S(9) " X: 3 * 65536
-endfunction
-
-silent! call S(1) " X: 3 * 1
-silent! call Bar()
-silent! call S(10) " X: 3 * 262144
-
-let expected = "E1E2E3E4E5E6E7E8E9E10"
-if taken != expected
- Xpath 16777216 " X: 0
- Xout "'taken' is" taken "instead of" expected
-endif
-
-augroup TMP
- autocmd BufWritePost * Xpath 33554432 " X: 33554432
-augroup END
-
-Xpath 67108864 " X: 67108864
-write /i/m/p/o/s/s/i/b/l/e
-Xpath 134217728 " X: 134217728
-
-autocmd! TMP
-unlet! caught errmsg3 taken expected
-delfunction S
-delfunction Foo
-delfunction Bar
-delfunction MSG
-
-Xcheck 236978127
-
-
-"-------------------------------------------------------------------------------
-" Test 64: Error exceptions after error, interrupt or :throw {{{1
-"
-" When an error occurs after an interrupt or a :throw but before
-" a matching :catch is reached, all following :catches of that try
-" block are ignored, but the error exception can be caught by the next
-" surrounding try conditional. Any previous error exception is
-" discarded. An error is ignored when there is a previous error that
-" has not been caught.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- while 1
- try
- try
- Xpath 1 " X: 1
- let caught = 0
- while 1
-" if 1
- " Missing :endif
- endwhile " throw error exception
- catch /^Vim(/
- let caught = 1
- finally
- Xpath 2 " X: 2
- if caught || $VIMNOERRTHROW
- Xpath 4 " X: 4
- endif
- endtry
- catch /.*/
- Xpath 8 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- try
- Xpath 16 " X: 16
- let caught = 0
- try
-" if 1
- " Missing :endif
- catch /.*/ " throw error exception
- Xpath 32 " X: 0
- catch /.*/
- Xpath 64 " X: 0
- endtry
- catch /^Vim(/
- let caught = 1
- finally
- Xpath 128 " X: 128
- if caught || $VIMNOERRTHROW
- Xpath 256 " X: 256
- endif
- endtry
- catch /.*/
- Xpath 512 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- try
- let caught = 0
- try
- Xpath 1024 " X: 1024
- "INTERRUPT
- catch /do_not_catch/
- Xpath 2048 " X: 0
-" if 1
- " Missing :endif
- catch /.*/ " throw error exception
- Xpath 4096 " X: 0
- catch /.*/
- Xpath 8192 " X: 0
- endtry
- catch /^Vim(/
- let caught = 1
- finally
- Xpath 16384 " X: 16384
- if caught || $VIMNOERRTHROW
- Xpath 32768 " X: 32768
- endif
- endtry
- catch /.*/
- Xpath 65536 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- try
- let caught = 0
- try
- Xpath 131072 " X: 131072
- throw "x"
- catch /do_not_catch/
- Xpath 262144 " X: 0
-" if 1
- " Missing :endif
- catch /x/ " throw error exception
- Xpath 524288 " X: 0
- catch /.*/
- Xpath 1048576 " X: 0
- endtry
- catch /^Vim(/
- let caught = 1
- finally
- Xpath 2097152 " X: 2097152
- if caught || $VIMNOERRTHROW
- Xpath 4194304 " X: 4194304
- endif
- endtry
- catch /.*/
- Xpath 8388608 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- try
- let caught = 0
- Xpath 16777216 " X: 16777216
-" endif " :endif without :if; throw error exception
-" if 1
- " Missing :endif
- catch /do_not_catch/ " ignore new error
- Xpath 33554432 " X: 0
- catch /^Vim(endif):/
- let caught = 1
- catch /^Vim(/
- Xpath 67108864 " X: 0
- finally
- Xpath 134217728 " X: 134217728
- if caught || $VIMNOERRTHROW
- Xpath 268435456 " X: 268435456
- endif
- endtry
- catch /.*/
- Xpath 536870912 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- Xpath 1073741824 " X: 1073741824
-
-endif
-
-Xcheck 1499645335
-
-
-"-------------------------------------------------------------------------------
-" Test 65: Errors in the /pattern/ argument of a :catch {{{1
-"
-" On an error in the /pattern/ argument of a :catch, the :catch does
-" not match. Any following :catches of the same :try/:endtry don't
-" match either. Finally clauses are executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! MSG(enr, emsg)
- let english = v:lang == "C" || v:lang =~ '^[Ee]n'
- if a:enr == ""
- Xout "TODO: Add message number for:" a:emsg
- let v:errmsg = ":" . v:errmsg
- endif
- let match = 1
- if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
- let match = 0
- if v:errmsg == ""
- Xout "Message missing."
- else
- let v:errmsg = escape(v:errmsg, '"')
- Xout "Unexpected message:" v:errmsg
- endif
- endif
- return match
-endfunction
-
-try
- try
- Xpath 1 " X: 1
- throw "oops"
- catch /^oops$/
- Xpath 2 " X: 2
- catch /\)/ " not checked; exception has already been caught
- Xpath 4 " X: 0
- endtry
- Xpath 8 " X: 8
-catch /.*/
- Xpath 16 " X: 0
- Xout v:exception "in" v:throwpoint
-endtry
-
-function! F()
- try
- let caught = 0
- try
- try
- Xpath 32 " X: 32
- throw "ab"
- catch /abc/ " does not catch
- Xpath 64 " X: 0
- catch /\)/ " error; discards exception
- Xpath 128 " X: 0
- catch /.*/ " not checked
- Xpath 256 " X: 0
- finally
- Xpath 512 " X: 512
- endtry
- Xpath 1024 " X: 0
- catch /^ab$/ " checked, but original exception is discarded
- Xpath 2048 " X: 0
- catch /^Vim(catch):/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim(catch):', '', "")
- finally
- Xpath 4096 " X: 4096
- if !caught && !$VIMNOERRTHROW
- Xpath 8192 " X: 0
- endif
- if !MSG('E475', "Invalid argument")
- Xpath 16384 " X: 0
- endif
- if !caught
- return | " discard error
- endif
- endtry
- catch /.*/
- Xpath 32768 " X: 0
- Xout v:exception "in" v:throwpoint
- endtry
-endfunction
-
-call F()
-Xpath 65536 " X: 65536
-
-delfunction MSG
-delfunction F
-unlet! caught
-
-Xcheck 70187
-
-
-"-------------------------------------------------------------------------------
-" Test 66: Stop range :call on error, interrupt, or :throw {{{1
-"
-" When a function which is multiply called for a range since it
-" doesn't handle the range itself has an error in a command
-" dynamically enclosed by :try/:endtry or gets an interrupt or
-" executes a :throw, no more calls for the remaining lines in the
-" range are made. On an error in a command not dynamically enclosed
-" by :try/:endtry, the function is executed again for the remaining
-" lines in the range.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- let file = tempname()
- exec "edit" file
-
- insert
-line 1
-line 2
-line 3
-.
-
- XloopINIT! 1 2
-
- let taken = ""
- let expected = "G1EF1E(1)F1E(2)F1E(3)G2EF2E(1)G3IF3I(1)G4TF4T(1)G5AF5A(1)"
-
- function! F(reason, n) abort
- let g:taken = g:taken . "F" . a:n .
- \ substitute(a:reason, '\(\l\).*', '\u\1', "") .
- \ "(" . line(".") . ")"
-
- if a:reason == "error"
- asdf
- elseif a:reason == "interrupt"
- "INTERRUPT
- let dummy = 0
- elseif a:reason == "throw"
- throw "xyz"
- elseif a:reason == "aborting error"
- XloopNEXT
- if g:taken != g:expected
- Xloop 1 " X: 0
- Xout "'taken' is" g:taken "instead of" g:expected
- endif
- try
- bwipeout!
- call delete(file)
- asdf
- endtry
- endif
- endfunction
-
- function! G(reason, n)
- let g:taken = g:taken . "G" . a:n .
- \ substitute(a:reason, '\(\l\).*', '\u\1', "")
- 1,3call F(a:reason, a:n)
- endfunction
-
- Xpath 8 " X: 8
- call G("error", 1)
- try
- Xpath 16 " X: 16
- try
- call G("error", 2)
- Xpath 32 " X: 0
- finally
- Xpath 64 " X: 64
- try
- call G("interrupt", 3)
- Xpath 128 " X: 0
- finally
- Xpath 256 " X: 256
- try
- call G("throw", 4)
- Xpath 512 " X: 0
- endtry
- endtry
- endtry
- catch /xyz/
- Xpath 1024 " X: 1024
- catch /.*/
- Xpath 2048 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- endtry
- Xpath 4096 " X: 4096
- call G("aborting error", 5)
- Xpath 8192 " X: 0
- Xout "'taken' is" taken "instead of" expected
-
-endif
-
-Xcheck 5464
-
-
-"-------------------------------------------------------------------------------
-" Test 67: :throw across :call command {{{1
-"
-" On a call command, an exception might be thrown when evaluating the
-" function name, during evaluation of the arguments, or when the
-" function is being executed. The exception can be caught by the
-" caller.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! THROW(x, n)
- if a:n == 1
- Xpath 1 " X: 1
- elseif a:n == 2
- Xpath 2 " X: 2
- elseif a:n == 3
- Xpath 4 " X: 4
- endif
- throw a:x
-endfunction
-
-function! NAME(x, n)
- if a:n == 1
- Xpath 8 " X: 0
- elseif a:n == 2
- Xpath 16 " X: 16
- elseif a:n == 3
- Xpath 32 " X: 32
- elseif a:n == 4
- Xpath 64 " X: 64
- endif
- return a:x
-endfunction
-
-function! ARG(x, n)
- if a:n == 1
- Xpath 128 " X: 0
- elseif a:n == 2
- Xpath 256 " X: 0
- elseif a:n == 3
- Xpath 512 " X: 512
- elseif a:n == 4
- Xpath 1024 " X: 1024
- endif
- return a:x
-endfunction
-
-function! F(x, n)
- if a:n == 2
- Xpath 2048 " X: 0
- elseif a:n == 4
- Xpath 4096 " X: 4096
- endif
-endfunction
-
-while 1
- try
- let error = 0
- let v:errmsg = ""
-
- while 1
- try
- Xpath 8192 " X: 8192
- call {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
- Xpath 16384 " X: 0
- catch /^name$/
- Xpath 32768 " X: 32768
- catch /.*/
- let error = 1
- Xout "1:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "1:" v:errmsg
- endif
- if error
- Xpath 65536 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- Xpath 131072 " X: 131072
- call {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
- Xpath 262144 " X: 0
- catch /^arg$/
- Xpath 524288 " X: 524288
- catch /.*/
- let error = 1
- Xout "2:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "2:" v:errmsg
- endif
- if error
- Xpath 1048576 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- Xpath 2097152 " X: 2097152
- call {NAME("THROW", 3)}(ARG("call", 3), 3)
- Xpath 4194304 " X: 0
- catch /^call$/
- Xpath 8388608 " X: 8388608
- catch /^0$/ " default return value
- Xpath 16777216 " X: 0
- Xout "3:" v:throwpoint
- catch /.*/
- let error = 1
- Xout "3:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "3:" v:errmsg
- endif
- if error
- Xpath 33554432 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- Xpath 67108864 " X: 67108864
- call {NAME("F", 4)}(ARG(4711, 4), 4)
- Xpath 134217728 " X: 134217728
- catch /.*/
- let error = 1
- Xout "4:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "4:" v:errmsg
- endif
- if error
- Xpath 268435456 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- catch /^0$/ " default return value
- Xpath 536870912 " X: 0
- Xout v:throwpoint
- catch /.*/
- let error = 1
- Xout v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout v:errmsg
- endif
- if error
- Xpath 1073741824 " X: 0
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-unlet error
-delfunction F
-
-Xcheck 212514423
-
-" Leave THROW(), NAME(), and ARG() for the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 68: :throw across function calls in expressions {{{1
-"
-" On a function call within an expression, an exception might be
-" thrown when evaluating the function name, during evaluation of the
-" arguments, or when the function is being executed. The exception
-" can be caught by the caller.
-"
-" This test reuses the functions THROW(), NAME(), and ARG() from the
-" previous test.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! F(x, n)
- if a:n == 2
- Xpath 2048 " X: 0
- elseif a:n == 4
- Xpath 4096 " X: 4096
- endif
- return a:x
-endfunction
-
-unlet! var1 var2 var3 var4
-
-while 1
- try
- let error = 0
- let v:errmsg = ""
-
- while 1
- try
- Xpath 8192 " X: 8192
- let var1 = {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
- Xpath 16384 " X: 0
- catch /^name$/
- Xpath 32768 " X: 32768
- catch /.*/
- let error = 1
- Xout "1:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "1:" v:errmsg
- endif
- if error
- Xpath 65536 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- Xpath 131072 " X: 131072
- let var2 = {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
- Xpath 262144 " X: 0
- catch /^arg$/
- Xpath 524288 " X: 524288
- catch /.*/
- let error = 1
- Xout "2:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "2:" v:errmsg
- endif
- if error
- Xpath 1048576 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- Xpath 2097152 " X: 2097152
- let var3 = {NAME("THROW", 3)}(ARG("call", 3), 3)
- Xpath 4194304 " X: 0
- catch /^call$/
- Xpath 8388608 " X: 8388608
- catch /^0$/ " default return value
- Xpath 16777216 " X: 0
- Xout "3:" v:throwpoint
- catch /.*/
- let error = 1
- Xout "3:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "3:" v:errmsg
- endif
- if error
- Xpath 33554432 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- while 1
- try
- Xpath 67108864 " X: 67108864
- let var4 = {NAME("F", 4)}(ARG(4711, 4), 4)
- Xpath 134217728 " X: 134217728
- catch /.*/
- let error = 1
- Xout "4:" v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout "4:" v:errmsg
- endif
- if error
- Xpath 268435456 " X: 0
- endif
- let error = 0
- let v:errmsg = ""
- break " discard error for $VIMNOERRTHROW
- endtry
- endwhile
-
- catch /^0$/ " default return value
- Xpath 536870912 " X: 0
- Xout v:throwpoint
- catch /.*/
- let error = 1
- Xout v:exception "in" v:throwpoint
- finally
- if !error && $VIMNOERRTHROW && v:errmsg != ""
- let error = 1
- Xout v:errmsg
- endif
- if error
- Xpath 1073741824 " X: 0
- endif
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-if exists("var1") || exists("var2") || exists("var3") ||
- \ !exists("var4") || var4 != 4711
- " The Xpath command does not accept 2^31 (negative); add explicitly:
- let Xpath = Xpath + 2147483648 " X: 0
- if exists("var1")
- Xout "var1 =" var1
- endif
- if exists("var2")
- Xout "var2 =" var2
- endif
- if exists("var3")
- Xout "var3 =" var3
- endif
- if !exists("var4")
- Xout "var4 unset"
- elseif var4 != 4711
- Xout "var4 =" var4
- endif
-endif
-
-unlet! error var1 var2 var3 var4
-delfunction THROW
-delfunction NAME
-delfunction ARG
-delfunction F
-
-Xcheck 212514423
-
-" Tests 69 to 75 were moved to test_trycatch.vim
-let Xtest = 76
-
-
-"-------------------------------------------------------------------------------
-" Test 76: Errors, interrupts, :throw during expression evaluation {{{1
-"
-" When a function call made during expression evaluation is aborted
-" due to an error inside a :try/:endtry region or due to an interrupt
-" or a :throw, the expression evaluation is aborted as well. No
-" message is displayed for the cancelled expression evaluation. On an
-" error not inside :try/:endtry, the expression evaluation continues.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- let taken = ""
-
- function! ERR(n)
- let g:taken = g:taken . "E" . a:n
- asdf
- endfunction
-
- function! ERRabort(n) abort
- let g:taken = g:taken . "A" . a:n
- asdf
- endfunction " returns -1; may cause follow-up msg for illegal var/func name
-
- function! WRAP(n, arg)
- let g:taken = g:taken . "W" . a:n
- let g:saved_errmsg = v:errmsg
- return arg
- endfunction
-
- function! INT(n)
- let g:taken = g:taken . "I" . a:n
- "INTERRUPT9
- let dummy = 0
- endfunction
-
- function! THR(n)
- let g:taken = g:taken . "T" . a:n
- throw "should not be caught"
- endfunction
-
- function! CONT(n)
- let g:taken = g:taken . "C" . a:n
- endfunction
-
- function! MSG(n)
- let g:taken = g:taken . "M" . a:n
- let errmsg = (a:n >= 37 && a:n <= 44) ? g:saved_errmsg : v:errmsg
- let msgptn = (a:n >= 10 && a:n <= 27) ? "^$" : "asdf"
- if errmsg !~ msgptn
- let g:taken = g:taken . "x"
- Xout "Expr" a:n.": Unexpected message:" v:errmsg
- endif
- let v:errmsg = ""
- let g:saved_errmsg = ""
- endfunction
-
- let v:errmsg = ""
-
- try
- let t = 1
- XloopINIT 1 2
- while t <= 9
- Xloop 1 " X: 511
- try
- if t == 1
- let v{ERR(t) + CONT(t)} = 0
- elseif t == 2
- let v{ERR(t) + CONT(t)}
- elseif t == 3
- let var = exists('v{ERR(t) + CONT(t)}')
- elseif t == 4
- unlet v{ERR(t) + CONT(t)}
- elseif t == 5
- function F{ERR(t) + CONT(t)}()
- endfunction
- elseif t == 6
- function F{ERR(t) + CONT(t)}
- elseif t == 7
- let var = exists('*F{ERR(t) + CONT(t)}')
- elseif t == 8
- delfunction F{ERR(t) + CONT(t)}
- elseif t == 9
- let var = ERR(t) + CONT(t)
- endif
- catch /asdf/
- " v:errmsg is not set when the error message is converted to an
- " exception. Set it to the original error message.
- let v:errmsg = substitute(v:exception, '^Vim:', '', "")
- catch /^Vim\((\a\+)\)\=:/
- " An error exception has been thrown after the original error.
- let v:errmsg = ""
- finally
- call MSG(t)
- let t = t + 1
- XloopNEXT
- continue " discard an aborting error
- endtry
- endwhile
- catch /.*/
- Xpath 512 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- endtry
-
- try
- let t = 10
- XloopINIT 1024 2
- while t <= 18
- Xloop 1 " X: 1024 * 511
- try
- if t == 10
- let v{INT(t) + CONT(t)} = 0
- elseif t == 11
- let v{INT(t) + CONT(t)}
- elseif t == 12
- let var = exists('v{INT(t) + CONT(t)}')
- elseif t == 13
- unlet v{INT(t) + CONT(t)}
- elseif t == 14
- function F{INT(t) + CONT(t)}()
- endfunction
- elseif t == 15
- function F{INT(t) + CONT(t)}
- elseif t == 16
- let var = exists('*F{INT(t) + CONT(t)}')
- elseif t == 17
- delfunction F{INT(t) + CONT(t)}
- elseif t == 18
- let var = INT(t) + CONT(t)
- endif
- catch /^Vim\((\a\+)\)\=:\(Interrupt\)\@!/
- " An error exception has been triggered after the interrupt.
- let v:errmsg = substitute(v:exception,
- \ '^Vim\((\a\+)\)\=:', '', "")
- finally
- call MSG(t)
- let t = t + 1
- XloopNEXT
- continue " discard interrupt
- endtry
- endwhile
- catch /.*/
- Xpath 524288 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- endtry
-
- try
- let t = 19
- XloopINIT 1048576 2
- while t <= 27
- Xloop 1 " X: 1048576 * 511
- try
- if t == 19
- let v{THR(t) + CONT(t)} = 0
- elseif t == 20
- let v{THR(t) + CONT(t)}
- elseif t == 21
- let var = exists('v{THR(t) + CONT(t)}')
- elseif t == 22
- unlet v{THR(t) + CONT(t)}
- elseif t == 23
- function F{THR(t) + CONT(t)}()
- endfunction
- elseif t == 24
- function F{THR(t) + CONT(t)}
- elseif t == 25
- let var = exists('*F{THR(t) + CONT(t)}')
- elseif t == 26
- delfunction F{THR(t) + CONT(t)}
- elseif t == 27
- let var = THR(t) + CONT(t)
- endif
- catch /^Vim\((\a\+)\)\=:/
- " An error exception has been triggered after the :throw.
- let v:errmsg = substitute(v:exception,
- \ '^Vim\((\a\+)\)\=:', '', "")
- finally
- call MSG(t)
- let t = t + 1
- XloopNEXT
- continue " discard exception
- endtry
- endwhile
- catch /.*/
- Xpath 536870912 " X: 0
- Xout v:exception "in" ExtraVimThrowpoint()
- endtry
-
- let v{ERR(28) + CONT(28)} = 0
- call MSG(28)
- let v{ERR(29) + CONT(29)}
- call MSG(29)
- let var = exists('v{ERR(30) + CONT(30)}')
- call MSG(30)
- unlet v{ERR(31) + CONT(31)}
- call MSG(31)
- function F{ERR(32) + CONT(32)}()
- endfunction
- call MSG(32)
- function F{ERR(33) + CONT(33)}
- call MSG(33)
- let var = exists('*F{ERR(34) + CONT(34)}')
- call MSG(34)
- delfunction F{ERR(35) + CONT(35)}
- call MSG(35)
- let var = ERR(36) + CONT(36)
- call MSG(36)
-
- let saved_errmsg = ""
-
- let v{WRAP(37, ERRabort(37)) + CONT(37)} = 0
- call MSG(37)
- let v{WRAP(38, ERRabort(38)) + CONT(38)}
- call MSG(38)
- let var = exists('v{WRAP(39, ERRabort(39)) + CONT(39)}')
- call MSG(39)
- unlet v{WRAP(40, ERRabort(40)) + CONT(40)}
- call MSG(40)
- function F{WRAP(41, ERRabort(41)) + CONT(41)}()
- endfunction
- call MSG(41)
- function F{WRAP(42, ERRabort(42)) + CONT(42)}
- call MSG(42)
- let var = exists('*F{WRAP(43, ERRabort(43)) + CONT(43)}')
- call MSG(43)
- delfunction F{WRAP(44, ERRabort(44)) + CONT(44)}
- call MSG(44)
- let var = ERRabort(45) + CONT(45)
- call MSG(45)
-
- Xpath 1073741824 " X: 1073741824
-
- let expected = ""
- \ . "E1M1E2M2E3M3E4M4E5M5E6M6E7M7E8M8E9M9"
- \ . "I10M10I11M11I12M12I13M13I14M14I15M15I16M16I17M17I18M18"
- \ . "T19M19T20M20T21M21T22M22T23M23T24M24T25M25T26M26T27M27"
- \ . "E28C28M28E29C29M29E30C30M30E31C31M31E32C32M32E33C33M33"
- \ . "E34C34M34E35C35M35E36C36M36"
- \ . "A37W37C37M37A38W38C38M38A39W39C39M39A40W40C40M40A41W41C41M41"
- \ . "A42W42C42M42A43W43C43M43A44W44C44M44A45C45M45"
-
- if taken != expected
- " The Xpath command does not accept 2^31 (negative); display explicitly:
- exec "!echo 2147483648 >>" . g:ExtraVimResult
- " X: 0
- Xout "'taken' is" taken "instead of" expected
- if substitute(taken,
- \ '\(.*\)E3C3M3x\(.*\)E30C30M30x\(.*\)A39C39M39x\(.*\)',
- \ '\1E3M3\2E30C30M30\3A39C39M39\4',
- \ "") == expected
- Xout "Is ++emsg_skip for var with expr_start non-NULL"
- \ "in f_exists ok?"
- endif
- endif
-
- unlet! v var saved_errmsg taken expected
- call delete(WA_t5)
- call delete(WA_t14)
- call delete(WA_t23)
- unlet! WA_t5 WA_t14 WA_t23
- delfunction WA_t5
- delfunction WA_t14
- delfunction WA_t23
-
-endif
-
-Xcheck 1610087935
-
-
-"-------------------------------------------------------------------------------
-" Test 77: Errors, interrupts, :throw in name{brace-expression} {{{1
-"
-" When a function call made during evaluation of an expression in
-" braces as part of a function name after ":function" is aborted due
-" to an error inside a :try/:endtry region or due to an interrupt or
-" a :throw, the expression evaluation is aborted as well, and the
-" function definition is ignored, skipping all commands to the
-" ":endfunction". On an error not inside :try/:endtry, the expression
-" evaluation continues and the function gets defined, and can be
-" called and deleted.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-XloopINIT 1 4
-
-function! ERR() abort
- Xloop 1 " X: 1 + 4 + 16 + 64
- asdf
-endfunction " returns -1
-
-function! OK()
- Xloop 2 " X: 2 * (1 + 4 + 16)
- let v:errmsg = ""
- return 0
-endfunction
-
-let v:errmsg = ""
-
-Xpath 4096 " X: 4096
-function! F{1 + ERR() + OK()}(arg)
- " F0 should be defined.
- if exists("a:arg") && a:arg == "calling"
- Xpath 8192 " X: 8192
- else
- Xpath 16384 " X: 0
- endif
-endfunction
-if v:errmsg != ""
- Xpath 32768 " X: 0
-endif
-XloopNEXT
-
-Xpath 65536 " X: 65536
-call F{1 + ERR() + OK()}("calling")
-if v:errmsg != ""
- Xpath 131072 " X: 0
-endif
-XloopNEXT
-
-Xpath 262144 " X: 262144
-delfunction F{1 + ERR() + OK()}
-if v:errmsg != ""
- Xpath 524288 " X: 0
-endif
-XloopNEXT
-
-try
- while 1
- let caught = 0
- try
- Xpath 1048576 " X: 1048576
- function! G{1 + ERR() + OK()}(arg)
- " G0 should not be defined, and the function body should be
- " skipped.
- if exists("a:arg") && a:arg == "calling"
- Xpath 2097152 " X: 0
- else
- Xpath 4194304 " X: 0
- endif
- " Use an unmatched ":finally" to check whether the body is
- " skipped when an error occurs in ERR(). This works whether or
- " not the exception is converted to an exception.
- finally
- Xpath 8388608 " X: 0
- Xout "Body of G{1 + ERR() + OK()}() not skipped"
- " Discard the aborting error or exception, and break the
- " while loop.
- break
- " End the try conditional and start a new one to avoid
- " ":catch after :finally" errors.
- endtry
- try
- Xpath 16777216 " X: 0
- endfunction
-
- " When the function was not defined, this won't be reached - whether
- " the body was skipped or not. When the function was defined, it
- " can be called and deleted here.
- Xpath 33554432 " X: 0
- Xout "G0() has been defined"
- XloopNEXT
- try
- call G{1 + ERR() + OK()}("calling")
- catch /.*/
- Xpath 67108864 " X: 0
- endtry
- Xpath 134217728 " X: 0
- XloopNEXT
- try
- delfunction G{1 + ERR() + OK()}
- catch /.*/
- Xpath 268435456 " X: 0
- endtry
- catch /asdf/
- " Jumped to when the function is not defined and the body is
- " skipped.
- let caught = 1
- catch /.*/
- Xpath 536870912 " X: 0
- finally
- if !caught && !$VIMNOERRTHROW
- Xpath 1073741824 " X: 0
- endif
- break " discard error for $VIMNOERRTHROW
- endtry " jumped to when the body is not skipped
- endwhile
-catch /.*/
- " The Xpath command does not accept 2^31 (negative); add explicitly:
- let Xpath = Xpath + 2147483648 " X: 0
- Xout "Body of G{1 + ERR() + OK()}() not skipped, exception caught"
- Xout v:exception "in" v:throwpoint
-endtry
-
-Xcheck 1388671
-
-
-"-------------------------------------------------------------------------------
-" Test 78: Messages on parsing errors in expression evaluation {{{1
-"
-" When an expression evaluation detects a parsing error, an error
-" message is given and converted to an exception, and the expression
-" evaluation is aborted.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- let taken = ""
-
- function! F(n)
- let g:taken = g:taken . "F" . a:n
- endfunction
-
- function! MSG(n, enr, emsg)
- let g:taken = g:taken . "M" . a:n
- let english = v:lang == "C" || v:lang =~ '^[Ee]n'
- if a:enr == ""
- Xout "TODO: Add message number for:" a:emsg
- let v:errmsg = ":" . v:errmsg
- endif
- if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
- if v:errmsg == ""
- Xout "Expr" a:n.": Message missing."
- let g:taken = g:taken . "x"
- else
- let v:errmsg = escape(v:errmsg, '"')
- Xout "Expr" a:n.": Unexpected message:" v:errmsg
- Xout "Expected: " . a:enr . ': ' . a:emsg
- let g:taken = g:taken . "X"
- endif
- endif
- endfunction
-
- function! CONT(n)
- let g:taken = g:taken . "C" . a:n
- endfunction
-
- let v:errmsg = ""
- XloopINIT 1 2
-
- try
- let t = 1
- while t <= 14
- let g:taken = g:taken . "T" . t
- let v:errmsg = ""
- try
- let caught = 0
- if t == 1
- let v{novar + CONT(t)} = 0
- elseif t == 2
- let v{novar + CONT(t)}
- elseif t == 3
- let var = exists('v{novar + CONT(t)}')
- elseif t == 4
- unlet v{novar + CONT(t)}
- elseif t == 5
- function F{novar + CONT(t)}()
- endfunction
- elseif t == 6
- function F{novar + CONT(t)}
- elseif t == 7
- let var = exists('*F{novar + CONT(t)}')
- elseif t == 8
- delfunction F{novar + CONT(t)}
- elseif t == 9
- echo novar + CONT(t)
- elseif t == 10
- echo v{novar + CONT(t)}
- elseif t == 11
- echo F{novar + CONT(t)}
- elseif t == 12
- let var = novar + CONT(t)
- elseif t == 13
- let var = v{novar + CONT(t)}
- elseif t == 14
- let var = F{novar + CONT(t)}()
- endif
- catch /^Vim\((\a\+)\)\=:/
- " v:errmsg is not set when the error message is converted to an
- " exception. Set it to the original error message.
- let v:errmsg = substitute(v:exception,
- \ '^Vim\((\a\+)\)\=:', '', "")
- let caught = 1
- finally
- if t <= 8 && t != 3 && t != 7
- call MSG(t, 'E475', 'Invalid argument\>')
- else
- if !caught " no error exceptions ($VIMNOERRTHROW set)
- call MSG(t, 'E15', "Invalid expression")
- else
- call MSG(t, 'E121', "Undefined variable")
- endif
- endif
- let t = t + 1
- XloopNEXT
- continue " discard an aborting error
- endtry
- endwhile
- catch /.*/
- Xloop 1 " X: 0
- Xout t.":" v:exception "in" ExtraVimThrowpoint()
- endtry
-
- function! T(n, expr, enr, emsg)
- try
- let g:taken = g:taken . "T" . a:n
- let v:errmsg = ""
- try
- let caught = 0
- execute "let var = " . a:expr
- catch /^Vim\((\a\+)\)\=:/
- " v:errmsg is not set when the error message is converted to an
- " exception. Set it to the original error message.
- let v:errmsg = substitute(v:exception,
- \ '^Vim\((\a\+)\)\=:', '', "")
- let caught = 1
- finally
- if !caught " no error exceptions ($VIMNOERRTHROW set)
- call MSG(a:n, 'E15', "Invalid expression")
- else
- call MSG(a:n, a:enr, a:emsg)
- endif
- XloopNEXT
- " Discard an aborting error:
- return
- endtry
- catch /.*/
- Xloop 1 " X: 0
- Xout a:n.":" v:exception "in" ExtraVimThrowpoint()
- endtry
- endfunction
-
- call T(15, 'Nofunc() + CONT(15)', 'E117', "Unknown function")
- call T(16, 'F(1 2 + CONT(16))', 'E116', "Invalid arguments")
- call T(17, 'F(1, 2) + CONT(17)', 'E118', "Too many arguments")
- call T(18, 'F() + CONT(18)', 'E119', "Not enough arguments")
- call T(19, '{(1} + CONT(19)', 'E110', "Missing ')'")
- call T(20, '("abc"[1) + CONT(20)', 'E111', "Missing ']'")
- call T(21, '(1 +) + CONT(21)', 'E15', "Invalid expression")
- call T(22, '1 2 + CONT(22)', 'E15', "Invalid expression")
- call T(23, '(1 ? 2) + CONT(23)', 'E109', "Missing ':' after '?'")
- call T(24, '("abc) + CONT(24)', 'E114', "Missing quote")
- call T(25, "('abc) + CONT(25)", 'E115', "Missing quote")
- call T(26, '& + CONT(26)', 'E112', "Option name missing")
- call T(27, '&asdf + CONT(27)', 'E113', "Unknown option")
-
- Xpath 134217728 " X: 134217728
-
- let expected = ""
- \ . "T1M1T2M2T3M3T4M4T5M5T6M6T7M7T8M8T9M9T10M10T11M11T12M12T13M13T14M14"
- \ . "T15M15T16M16T17M17T18M18T19M19T20M20T21M21T22M22T23M23T24M24T25M25"
- \ . "T26M26T27M27"
-
- if taken != expected
- Xpath 268435456 " X: 0
- Xout "'taken' is" taken "instead of" expected
- if substitute(taken, '\(.*\)T3M3x\(.*\)', '\1T3M3\2', "") == expected
- Xout "Is ++emsg_skip for var with expr_start non-NULL"
- \ "in f_exists ok?"
- endif
- endif
-
- unlet! var caught taken expected
- call delete(WA_t5)
- unlet! WA_t5
- delfunction WA_t5
-
-endif
-
-Xcheck 134217728
-
-
-"-------------------------------------------------------------------------------
-" Test 79: Throwing one of several errors for the same command {{{1
-"
-" When several errors appear in a row (for instance during expression
-" evaluation), the first as the most specific one is used when
-" throwing an error exception. If, however, a syntax error is
-" detected afterwards, this one is used for the error exception.
-" On a syntax error, the next command is not executed, on a normal
-" error, however, it is (relevant only in a function without the
-" "abort" flag). v:errmsg is not set.
-"
-" If throwing error exceptions is configured off, v:errmsg is always
-" set to the latest error message, that is, to the more general
-" message or the syntax error, respectively.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-XloopINIT 1 2
-
-function! NEXT(cmd)
- exec a:cmd . " | Xloop 1"
-endfunction
-
-call NEXT('echo novar') " X: 1 * 1 (checks nextcmd)
-XloopNEXT
-call NEXT('let novar #') " X: 0 * 2 (skips nextcmd)
-XloopNEXT
-call NEXT('unlet novar #') " X: 0 * 4 (skips nextcmd)
-XloopNEXT
-call NEXT('let {novar}') " X: 0 * 8 (skips nextcmd)
-XloopNEXT
-call NEXT('unlet{ novar}') " X: 0 * 16 (skips nextcmd)
-
-function! EXEC(cmd)
- exec a:cmd
-endfunction
-
-function! MATCH(expected, msg, enr, emsg)
- let msg = a:msg
- if a:enr == ""
- Xout "TODO: Add message number for:" a:emsg
- let msg = ":" . msg
- endif
- let english = v:lang == "C" || v:lang =~ '^[Ee]n'
- if msg !~ '^'.a:enr.':' || (english && msg !~ a:emsg)
- let match = 0
- if a:expected " no match although expected
- if a:msg == ""
- Xout "Message missing."
- else
- let msg = escape(msg, '"')
- Xout "Unexpected message:" msg
- Xout "Expected:" a:enr . ": " . a:emsg
- endif
- endif
- else
- let match = 1
- if !a:expected " match although not expected
- let msg = escape(msg, '"')
- Xout "Unexpected message:" msg
- Xout "Expected none."
- endif
- endif
- return match
-endfunction
-
-try
-
- while 1 " dummy loop
- try
- let v:errmsg = ""
- let caught = 0
- let thrmsg = ""
- call EXEC('echo novar') " normal error
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let thrmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xpath 32 " X: 32
- if !caught
- if !$VIMNOERRTHROW
- Xpath 64 " X: 0
- endif
- elseif !MATCH(1, thrmsg, 'E121', "Undefined variable")
- \ || v:errmsg != ""
- Xpath 128 " X: 0
- endif
- if !caught && !MATCH(1, v:errmsg, 'E15', "Invalid expression")
- Xpath 256 " X: 0
- endif
- break " discard error if $VIMNOERRTHROW == 1
- endtry
- endwhile
-
- Xpath 512 " X: 512
- let cmd = "let"
- XloopINIT 1024 32
- while cmd != ""
- try
- let v:errmsg = ""
- let caught = 0
- let thrmsg = ""
- call EXEC(cmd . ' novar #') " normal plus syntax error
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let thrmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xloop 1 " X: 1024 * (1 + 32)
- if !caught
- if !$VIMNOERRTHROW
- Xloop 2 " X: 0
- endif
- else
- if cmd == "let"
- let match = MATCH(0, thrmsg, 'E121', "Undefined variable")
- elseif cmd == "unlet"
- let match = MATCH(0, thrmsg, 'E108', "No such variable")
- endif
- if match " normal error
- Xloop 4 " X: 0
- endif
- if !MATCH(1, thrmsg, 'E488', "Trailing characters")
- \|| v:errmsg != ""
- " syntax error
- Xloop 8 " X: 0
- endif
- endif
- if !caught && !MATCH(1, v:errmsg, 'E488', "Trailing characters")
- " last error
- Xloop 16 " X: 0
- endif
- if cmd == "let"
- let cmd = "unlet"
- else
- let cmd = ""
- endif
- XloopNEXT
- continue " discard error if $VIMNOERRTHROW == 1
- endtry
- endwhile
-
- Xpath 1048576 " X: 1048576
- let cmd = "let"
- XloopINIT 2097152 32
- while cmd != ""
- try
- let v:errmsg = ""
- let caught = 0
- let thrmsg = ""
- call EXEC(cmd . ' {novar}') " normal plus syntax error
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let thrmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xloop 1 " X: 2097152 * (1 + 32)
- if !caught
- if !$VIMNOERRTHROW
- Xloop 2 " X: 0
- endif
- else
- if MATCH(0, thrmsg, 'E121', "Undefined variable") " normal error
- Xloop 4 " X: 0
- endif
- if !MATCH(1, thrmsg, 'E475', 'Invalid argument\>')
- \ || v:errmsg != "" " syntax error
- Xloop 8 " X: 0
- endif
- endif
- if !caught && !MATCH(1, v:errmsg, 'E475', 'Invalid argument\>')
- " last error
- Xloop 16 " X: 0
- endif
- if cmd == "let"
- let cmd = "unlet"
- else
- let cmd = ""
- endif
- XloopNEXT
- continue " discard error if $VIMNOERRTHROW == 1
- endtry
- endwhile
-
-catch /.*/
- " The Xpath command does not accept 2^31 (negative); add explicitly:
- let Xpath = Xpath + 2147483648 " X: 0
- Xout v:exception "in" v:throwpoint
-endtry
-
-unlet! next_command thrmsg match
-delfunction NEXT
-delfunction EXEC
-delfunction MATCH
-
-Xcheck 70288929
-
-
-"-------------------------------------------------------------------------------
-" Test 80: Syntax error in expression for illegal :elseif {{{1
-"
-" If there is a syntax error in the expression after an illegal
-" :elseif, an error message is given (or an error exception thrown)
-" for the illegal :elseif rather than the expression error.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! MSG(enr, emsg)
- let english = v:lang == "C" || v:lang =~ '^[Ee]n'
- if a:enr == ""
- Xout "TODO: Add message number for:" a:emsg
- let v:errmsg = ":" . v:errmsg
- endif
- let match = 1
- if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
- let match = 0
- if v:errmsg == ""
- Xout "Message missing."
- else
- let v:errmsg = escape(v:errmsg, '"')
- Xout "Unexpected message:" v:errmsg
- endif
- endif
- return match
-endfunction
-
-let v:errmsg = ""
-if 0
-else
-elseif 1 ||| 2
-endif
-Xpath 1 " X: 1
-if !MSG('E584', ":elseif after :else")
- Xpath 2 " X: 0
-endif
-
-let v:errmsg = ""
-if 1
-else
-elseif 1 ||| 2
-endif
-Xpath 4 " X: 4
-if !MSG('E584', ":elseif after :else")
- Xpath 8 " X: 0
-endif
-
-let v:errmsg = ""
-elseif 1 ||| 2
-Xpath 16 " X: 16
-if !MSG('E582', ":elseif without :if")
- Xpath 32 " X: 0
-endif
-
-let v:errmsg = ""
-while 1
- elseif 1 ||| 2
-endwhile
-Xpath 64 " X: 64
-if !MSG('E582', ":elseif without :if")
- Xpath 128 " X: 0
-endif
-
-while 1
- try
- try
- let v:errmsg = ""
- let caught = 0
- if 0
- else
- elseif 1 ||| 2
- endif
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xpath 256 " X: 256
- if !caught && !$VIMNOERRTHROW
- Xpath 512 " X: 0
- endif
- if !MSG('E584', ":elseif after :else")
- Xpath 1024 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 2048 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- try
- let v:errmsg = ""
- let caught = 0
- if 1
- else
- elseif 1 ||| 2
- endif
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xpath 4096 " X: 4096
- if !caught && !$VIMNOERRTHROW
- Xpath 8192 " X: 0
- endif
- if !MSG('E584', ":elseif after :else")
- Xpath 16384 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 32768 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- try
- let v:errmsg = ""
- let caught = 0
- elseif 1 ||| 2
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xpath 65536 " X: 65536
- if !caught && !$VIMNOERRTHROW
- Xpath 131072 " X: 0
- endif
- if !MSG('E582', ":elseif without :if")
- Xpath 262144 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 524288 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-while 1
- try
- try
- let v:errmsg = ""
- let caught = 0
- while 1
- elseif 1 ||| 2
- endwhile
- catch /^Vim\((\a\+)\)\=:/
- let caught = 1
- let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
- finally
- Xpath 1048576 " X: 1048576
- if !caught && !$VIMNOERRTHROW
- Xpath 2097152 " X: 0
- endif
- if !MSG('E582', ":elseif without :if")
- Xpath 4194304 " X: 0
- endif
- endtry
- catch /.*/
- Xpath 8388608 " X: 0
- Xout v:exception "in" v:throwpoint
- finally
- break " discard error for $VIMNOERRTHROW
- endtry
-endwhile
-
-Xpath 16777216 " X: 16777216
-
-unlet! caught
-delfunction MSG
-
-Xcheck 17895765
-
-
-"-------------------------------------------------------------------------------
-" Test 81: Discarding exceptions after an error or interrupt {{{1
-"
-" When an exception is thrown from inside a :try conditional without
-" :catch and :finally clauses and an error or interrupt occurs before
-" the :endtry is reached, the exception is discarded.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
- try
- Xpath 1 " X: 1
- try
- Xpath 2 " X: 2
- throw "arrgh"
- Xpath 4 " X: 0
-" if 1
- Xpath 8 " X: 0
- " error after :throw: missing :endif
- endtry
- Xpath 16 " X: 0
- catch /arrgh/
- Xpath 32 " X: 0
- endtry
- Xpath 64 " X: 0
-endif
-
-if ExtraVim()
- try
- Xpath 128 " X: 128
- try
- Xpath 256 " X: 256
- throw "arrgh"
- Xpath 512 " X: 0
- endtry " INTERRUPT
- Xpath 1024 " X: 0
- catch /arrgh/
- Xpath 2048 " X: 0
- endtry
- Xpath 4096 " X: 0
-endif
-
-Xcheck 387
-
+" Following tests were moved to test_vimscript.vim:
+" 1-24, 27-31, 34-40, 49-50, 52-68, 76-81, 87
+" Following tests were moved to test_trycatch.vim:
+" 25-26, 32-33, 41-48, 51, 69-75
+let Xtest = 82
"-------------------------------------------------------------------------------
" Test 82: Ignoring :catch clauses after an error or interrupt {{{1
diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
index 443a217143..cae71e10f3 100644
--- a/src/nvim/testdir/test_arglist.vim
+++ b/src/nvim/testdir/test_arglist.vim
@@ -1,5 +1,6 @@
" Test argument list commands
+source check.vim
source shared.vim
source term_util.vim
@@ -552,9 +553,7 @@ endfunc
" Test for quitting Vim with unedited files in the argument list
func Test_quit_with_arglist()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot run vim in terminal'
- endif
+ CheckRunVimInTerminal
let buf = RunVimInTerminal('', {'rows': 6})
call term_sendkeys(buf, ":set nomore\n")
call term_sendkeys(buf, ":args a b c\n")
diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim
index 8723a0a38d..431908e95c 100644
--- a/src/nvim/testdir/test_assert.vim
+++ b/src/nvim/testdir/test_assert.vim
@@ -48,6 +48,11 @@ func Test_assert_equal()
call assert_equal('XxxxxxxxxxxxxxxxxxxxxxX', 'XyyyyyyyyyyyyyyyyyyyyyyyyyX')
call assert_match("Expected 'X\\\\\\[x occurs 21 times]X' but got 'X\\\\\\[y occurs 25 times]X'", v:errors[0])
call remove(v:errors, 0)
+
+ " special characters are escaped
+ call assert_equal("\b\e\f\n\t\r\\\x01\x7f", 'x')
+ call assert_match('Expected ''\\b\\e\\f\\n\\t\\r\\\\\\x01\\x7f'' but got ''x''', v:errors[0])
+ call remove(v:errors, 0)
endfunc
func Test_assert_equal_dict()
@@ -146,6 +151,14 @@ func Test_assert_exception()
try
nocommand
catch
+ call assert_equal(1, assert_exception('E12345:'))
+ endtry
+ call assert_match("Expected 'E12345:' but got 'Vim:E492: ", v:errors[0])
+ call remove(v:errors, 0)
+
+ try
+ nocommand
+ catch
try
" illegal argument, get NULL for error
call assert_equal(1, assert_exception([]))
@@ -153,6 +166,10 @@ func Test_assert_exception()
call assert_equal(0, assert_exception('E730:'))
endtry
endtry
+
+ call assert_equal(1, assert_exception('E492:'))
+ call assert_match('v:exception is not set', v:errors[0])
+ call remove(v:errors, 0)
endfunc
func Test_wrong_error_type()
@@ -202,6 +219,14 @@ func Test_assert_fail_fails()
call assert_match("stupid: Expected 'E9876' but got 'E492:", v:errors[0])
call remove(v:errors, 0)
+ call assert_equal(1, assert_fails('xxx', ['E9876']))
+ call assert_match("Expected \\['E9876'\\] but got 'E492:", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, assert_fails('xxx', ['E492:', 'E9876']))
+ call assert_match("Expected \\['E492:', 'E9876'\\] but got 'E492:", v:errors[0])
+ call remove(v:errors, 0)
+
call assert_equal(1, assert_fails('echo', '', 'echo command'))
call assert_match("command did not fail: echo command", v:errors[0])
call remove(v:errors, 0)
@@ -209,6 +234,41 @@ func Test_assert_fail_fails()
call assert_equal(1, 'echo'->assert_fails('', 'echo command'))
call assert_match("command did not fail: echo command", v:errors[0])
call remove(v:errors, 0)
+
+ try
+ call assert_equal(1, assert_fails('xxx', []))
+ catch
+ let exp = v:exception
+ endtry
+ call assert_match("E856: assert_fails() second argument", exp)
+
+ try
+ call assert_equal(1, assert_fails('xxx', ['1', '2', '3']))
+ catch
+ let exp = v:exception
+ endtry
+ call assert_match("E856: assert_fails() second argument", exp)
+
+ try
+ call assert_equal(1, assert_fails('xxx', #{one: 1}))
+ catch
+ let exp = v:exception
+ endtry
+ call assert_match("E856: assert_fails() second argument", exp)
+
+ try
+ call assert_equal(1, assert_fails('xxx', 'E492', '', 'burp'))
+ catch
+ let exp = v:exception
+ endtry
+ call assert_match("E1115: assert_fails() fourth argument must be a number", exp)
+
+ try
+ call assert_equal(1, assert_fails('xxx', 'E492', '', 54, 123))
+ catch
+ let exp = v:exception
+ endtry
+ call assert_match("E1116: assert_fails() fifth argument must be a string", exp)
endfunc
func Test_assert_fails_in_try_block()
diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim
index 4229095f9f..a8810047a0 100644
--- a/src/nvim/testdir/test_autochdir.vim
+++ b/src/nvim/testdir/test_autochdir.vim
@@ -122,9 +122,7 @@ endfunc
func Test_multibyte()
" using an invalid character should not cause a crash
set wic
- " Except on Windows, E472 is also thrown last, but v8.1.1183 isn't ported yet
- " call assert_fails('tc *', has('win32') ? 'E480:' : 'E344:')
- call assert_fails('tc *', has('win32') ? 'E480:' : 'E472:')
+ call assert_fails('tc *', has('win32') ? 'E480:' : 'E344:')
set nowic
endfunc
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 8c15249f97..50904bab34 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -175,9 +175,7 @@ func Test_autocmd_bufunload_avoiding_SEGV_01()
exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!'
augroup END
- " Todo: check for E937 generated first
- " call assert_fails('edit bb.txt', 'E937:')
- call assert_fails('edit bb.txt', 'E517:')
+ call assert_fails('edit bb.txt', ['E937:', 'E517:'])
autocmd! test_autocmd_bufunload
augroup! test_autocmd_bufunload
@@ -533,6 +531,8 @@ func Test_augroup_warning()
redir END
call assert_notmatch("W19:", res)
au! VimEnter
+
+ call assert_fails('augroup!', 'E471:')
endfunc
func Test_BufReadCmdHelp()
@@ -2931,7 +2931,7 @@ func Test_BufDelete_changebuf()
augroup END
let save_cpo = &cpo
set cpo+=f
- call assert_fails('r Xfile', 'E484:')
+ call assert_fails('r Xfile', ['E812:', 'E484:'])
call assert_equal('somefile', @%)
let &cpo = save_cpo
augroup TestAuCmd
diff --git a/src/nvim/testdir/test_backup.vim b/src/nvim/testdir/test_backup.vim
index ce2bfe72bc..7eff818732 100644
--- a/src/nvim/testdir/test_backup.vim
+++ b/src/nvim/testdir/test_backup.vim
@@ -1,5 +1,7 @@
" Tests for the backup function
+source check.vim
+
func Test_backup()
set backup backupdir=. backupskip=
new
@@ -56,3 +58,19 @@ func Test_backup2_backupcopy()
call delete(f)
set backup&vim backupdir&vim backupcopy&vim backupskip&vim
endfunc
+
+" Test for using a non-existing directory as a backup directory
+func Test_non_existing_backupdir()
+ throw 'Skipped: Nvim auto-creates backup directory'
+ CheckNotBSD
+ let save_backup = &backupdir
+ set backupdir=./non_existing_dir
+ call writefile(['line1'], 'Xfile')
+ new Xfile
+ " TODO: write doesn't fail in Cirrus FreeBSD CI test
+ call assert_fails('write', 'E510:')
+ let &backupdir = save_backup
+ call delete('Xfile')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim
index 770b2d16ef..046acb81e1 100644
--- a/src/nvim/testdir/test_blob.vim
+++ b/src/nvim/testdir/test_blob.vim
@@ -252,6 +252,7 @@ func Test_blob_func_remove()
call assert_fails("call remove(b, 3, 2)", 'E979:')
call assert_fails("call remove(1, 0)", 'E896:')
call assert_fails("call remove(b, b)", 'E974:')
+ call assert_fails("call remove(b, 1, [])", 'E745:')
call assert_fails("call remove(v:_null_blob, 1, 2)", 'E979:')
" Translated from v8.2.3284
@@ -300,6 +301,9 @@ func Test_blob_index()
call assert_equal(3, 0z11110111->index(0x11, 2))
call assert_equal(2, index(0z11111111, 0x11, -2))
call assert_equal(3, index(0z11110111, 0x11, -2))
+ call assert_equal(0, index(0z11110111, 0x11, -10))
+ call assert_fails("echo index(0z11110111, 0x11, [])", 'E745:')
+ call assert_equal(-1, index(v:_null_blob, 1))
call assert_fails('call index("asdf", 0)', 'E897:')
endfunc
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
index ed4d886fd1..995683c68c 100644
--- a/src/nvim/testdir/test_breakindent.vim
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -8,6 +8,7 @@ source check.vim
CheckOption breakindent
source view_util.vim
+source screendump.vim
let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
@@ -421,6 +422,7 @@ func Test_breakindent11()
let width = strlen(text[1:]) + indent(2) + strlen(&sbr) * 3 " text wraps 3 times
call assert_equal(width, strdisplaywidth(text))
call s:close_windows('set sbr=')
+ call assert_equal(4, strdisplaywidth("\t", 4))
endfunc
func Test_breakindent11_vartabs()
@@ -875,17 +877,164 @@ endfunc
func Test_window_resize_with_linebreak()
new
53vnew
- set linebreak
- set showbreak=>>
- set breakindent
- set breakindentopt=shift:4
+ setl linebreak
+ setl showbreak=>>
+ setl breakindent
+ setl breakindentopt=shift:4
call setline(1, "\naaaaaaaaa\n\na\naaaaa\n¯aaaaaaaaaa\naaaaaaaaaaaa\naaa\n\"a:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaa\"\naaaaaaaa\n\"a")
redraw!
call assert_equal([" >>aa^@\"a: "], ScreenLines(2, 14))
vertical resize 52
redraw!
call assert_equal([" >>aaa^@\"a:"], ScreenLines(2, 14))
+ set linebreak& showbreak& breakindent& breakindentopt&
%bw!
endfunc
+func Test_cursor_position_with_showbreak()
+ CheckScreendump
+
+ let lines =<< trim END
+ vim9script
+ &signcolumn = 'yes'
+ &showbreak = '+ '
+ var leftcol: number = win_getid()->getwininfo()->get(0, {})->get('textoff')
+ repeat('x', &columns - leftcol - 1)->setline(1)
+ 'second line'->setline(2)
+ END
+ call writefile(lines, 'XscriptShowbreak')
+ let buf = RunVimInTerminal('-S XscriptShowbreak', #{rows: 6})
+
+ call term_sendkeys(buf, "AX")
+ call VerifyScreenDump(buf, 'Test_cursor_position_with_showbreak', {})
+
+ call StopVimInTerminal(buf)
+ call delete('XscriptShowbreak')
+endfunc
+
+func Test_no_spurious_match()
+ let s:input = printf('- y %s y %s', repeat('x', 50), repeat('x', 50))
+ call s:test_windows('setl breakindent breakindentopt=list:-1 formatlistpat=^- hls')
+ let @/ = '\%>3v[y]'
+ redraw!
+ call searchcount().total->assert_equal(1)
+ " cleanup
+ set hls&vim
+ let s:input = "\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
+ bwipeout!
+endfunc
+
+func Test_no_extra_indent()
+ call s:test_windows('setl breakindent breakindentopt=list:-1,min:10')
+ %d
+ let &l:formatlistpat='^\s*\d\+\.\s\+'
+ let text = 'word '
+ let len = text->strcharlen()
+ let line1 = text->repeat((winwidth(0) / len) * 2)
+ let line2 = repeat(' ', 2) .. '1. ' .. line1
+ call setline(1, [line2])
+ redraw!
+ " 1) matches formatlist pattern, so indent
+ let expect = [
+ \ " 1. word word word ",
+ \ " word word word ",
+ \ " word word ",
+ \ "~ ",
+ \ ]
+ let lines = s:screen_lines2(1, 4, 20)
+ call s:compare_lines(expect, lines)
+ " 2) change formatlist pattern
+ " -> indent adjusted
+ let &l:formatlistpat='^\s*\d\+\.'
+ let expect = [
+ \ " 1. word word word ",
+ \ " word word word ",
+ \ " word word ",
+ \ "~ ",
+ \ ]
+ let lines = s:screen_lines2(1, 4, 20)
+ " 3) no local formatlist pattern,
+ " so use global one -> indent
+ let g_flp = &g:flp
+ let &g:formatlistpat='^\s*\d\+\.\s\+'
+ let &l:formatlistpat=''
+ let expect = [
+ \ " 1. word word word ",
+ \ " word word word ",
+ \ " word word ",
+ \ "~ ",
+ \ ]
+ let lines = s:screen_lines2(1, 4, 20)
+ call s:compare_lines(expect, lines)
+ let &g:flp = g_flp
+ let &l:formatlistpat='^\s*\d\+\.'
+ " 4) add something in front, no additional indent
+ norm! gg0
+ exe ":norm! 5iword \<esc>"
+ redraw!
+ let expect = [
+ \ "word word word word ",
+ \ "word 1. word word ",
+ \ "word word word word ",
+ \ "word word ",
+ \ "~ ",
+ \ ]
+ let lines = s:screen_lines2(1, 5, 20)
+ call s:compare_lines(expect, lines)
+ bwipeout!
+endfunc
+
+func Test_breakindent_column()
+ " restore original
+ let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
+ call s:test_windows('setl breakindent breakindentopt=column:10')
+ redraw!
+ " 1) default: does not indent, too wide :(
+ let expect = [
+ \ " ",
+ \ " abcdefghijklmnop",
+ \ "qrstuvwxyzABCDEFGHIJ",
+ \ "KLMNOP "
+ \ ]
+ let lines = s:screen_lines2(1, 4, 20)
+ call s:compare_lines(expect, lines)
+ " 2) lower min value, so that breakindent works
+ setl breakindentopt+=min:5
+ redraw!
+ let expect = [
+ \ " ",
+ \ " abcdefghijklmnop",
+ \ " qrstuvwxyz",
+ \ " ABCDEFGHIJ",
+ \ " KLMNOP "
+ \ ]
+ let lines = s:screen_lines2(1, 5, 20)
+ " 3) set shift option -> no influence
+ setl breakindentopt+=shift:5
+ redraw!
+ let expect = [
+ \ " ",
+ \ " abcdefghijklmnop",
+ \ " qrstuvwxyz",
+ \ " ABCDEFGHIJ",
+ \ " KLMNOP "
+ \ ]
+ let lines = s:screen_lines2(1, 5, 20)
+ call s:compare_lines(expect, lines)
+ " 4) add showbreak value
+ setl showbreak=++
+ redraw!
+ let expect = [
+ \ " ",
+ \ " abcdefghijklmnop",
+ \ " ++qrstuvwx",
+ \ " ++yzABCDEF",
+ \ " ++GHIJKLMN",
+ \ " ++OP "
+ \ ]
+ let lines = s:screen_lines2(1, 6, 20)
+ call s:compare_lines(expect, lines)
+ bwipeout!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim
index 4def3b5df9..27c2d5d442 100644
--- a/src/nvim/testdir/test_buffer.vim
+++ b/src/nvim/testdir/test_buffer.vim
@@ -76,7 +76,7 @@ func Test_bunload_with_offset()
let caught_E90 = 1
endtry
call assert_equal(1, caught_E90)
- call assert_fails('$bunload', 'E515:')
+ call assert_fails('$bunload', 'E90:')
endfunc
" Test for :buffer, :bnext, :bprevious, :brewind, :blast and :bmodified
@@ -282,7 +282,7 @@ func Test_goto_buf_with_confirm()
call assert_equal(1, &modified)
call assert_equal('', @%)
call feedkeys('y', 'L')
- call assert_fails('confirm b Xfile', 'E37:')
+ call assert_fails('confirm b Xfile', ['', 'E37:'])
call assert_equal(1, &modified)
call assert_equal('', @%)
call feedkeys('n', 'L')
diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim
index 8f853fe44e..2867f13cbc 100644
--- a/src/nvim/testdir/test_bufline.vim
+++ b/src/nvim/testdir/test_bufline.vim
@@ -19,13 +19,25 @@ func Test_setbufline_getbufline()
call setline(1, ['a', 'b', 'c'])
let b = bufnr('%')
wincmd w
+
+ call assert_equal(1, setbufline(b, 5, 'x'))
call assert_equal(1, setbufline(b, 5, ['x']))
+ call assert_equal(1, setbufline(b, 5, []))
+ call assert_equal(1, setbufline(b, 5, v:_null_list))
+
+ call assert_equal(1, 'x'->setbufline(bufnr('$') + 1, 1))
call assert_equal(1, ['x']->setbufline(bufnr('$') + 1, 1))
+ call assert_equal(1, []->setbufline(bufnr('$') + 1, 1))
+ call assert_equal(1, v:_null_list->setbufline(bufnr('$') + 1, 1))
+
+ call assert_equal(['a', 'b', 'c'], getbufline(b, 1, '$'))
+
call assert_equal(0, setbufline(b, 4, ['d', 'e']))
call assert_equal(['c'], b->getbufline(3))
call assert_equal(['d'], getbufline(b, 4))
call assert_equal(['e'], getbufline(b, 5))
call assert_equal([], getbufline(b, 6))
+ call assert_equal([], getbufline(b, 2, 1))
exe "bwipe! " . b
endfunc
@@ -83,9 +95,29 @@ func Test_appendbufline()
call setline(1, ['a', 'b', 'c'])
let b = bufnr('%')
wincmd w
+
+ call assert_equal(1, appendbufline(b, -1, 'x'))
call assert_equal(1, appendbufline(b, -1, ['x']))
+ call assert_equal(1, appendbufline(b, -1, []))
+ call assert_equal(1, appendbufline(b, -1, v:_null_list))
+
+ call assert_equal(1, appendbufline(b, 4, 'x'))
call assert_equal(1, appendbufline(b, 4, ['x']))
+ call assert_equal(1, appendbufline(b, 4, []))
+ call assert_equal(1, appendbufline(b, 4, v:_null_list))
+
+ call assert_equal(1, appendbufline(1234, 1, 'x'))
call assert_equal(1, appendbufline(1234, 1, ['x']))
+ call assert_equal(1, appendbufline(1234, 1, []))
+ call assert_equal(1, appendbufline(1234, 1, v:_null_list))
+
+ call assert_equal(0, appendbufline(b, 1, []))
+ call assert_equal(0, appendbufline(b, 1, v:_null_list))
+ call assert_equal(1, appendbufline(b, 3, []))
+ call assert_equal(1, appendbufline(b, 3, v:_null_list))
+
+ call assert_equal(['a', 'b', 'c'], getbufline(b, 1, '$'))
+
call assert_equal(0, appendbufline(b, 3, ['d', 'e']))
call assert_equal(['c'], getbufline(b, 3))
call assert_equal(['d'], getbufline(b, 4))
diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim
index d6d44d1901..2a2437f542 100644
--- a/src/nvim/testdir/test_cd.vim
+++ b/src/nvim/testdir/test_cd.vim
@@ -5,7 +5,7 @@ source check.vim
func Test_cd_large_path()
" This used to crash with a heap write overflow.
- call assert_fails('cd ' . repeat('x', 5000), 'E472:')
+ call assert_fails('cd ' . repeat('x', 5000), 'E344:')
endfunc
func Test_cd_up_and_down()
@@ -45,9 +45,7 @@ func Test_cd_minus()
call assert_equal(path, getcwd())
" Test for :cd - after a failed :cd
- " v8.2.1183 is not ported yet
- " call assert_fails('cd /nonexistent', 'E344:')
- call assert_fails('cd /nonexistent', 'E472:')
+ call assert_fails('cd /nonexistent', 'E344:')
call assert_equal(path, getcwd())
cd -
call assert_equal(path_dotdot, getcwd())
@@ -68,30 +66,6 @@ func Test_cd_minus()
call delete('Xresult')
endfunc
-func Test_cd_with_cpo_chdir()
- e Xfoo
- call setline(1, 'foo')
- let path = getcwd()
- " set cpo+=.
-
- " :cd should fail when buffer is modified and 'cpo' contains dot.
- " call assert_fails('cd ..', 'E747:')
- call assert_equal(path, getcwd())
-
- " :cd with exclamation mark should succeed.
- cd! ..
- call assert_notequal(path, getcwd())
-
- " :cd should succeed when buffer has been written.
- w!
- exe 'cd ' .. fnameescape(path)
- call assert_equal(path, getcwd())
-
- call delete('Xfoo')
- set cpo&
- bw!
-endfunc
-
" Test for chdir()
func Test_chdir_func()
let topdir = getcwd()
@@ -127,7 +101,7 @@ func Test_chdir_func()
call assert_equal('testdir', fnamemodify(getcwd(1, 1), ':t'))
" Error case
- call assert_fails("call chdir('dir-abcd')", 'E472:')
+ call assert_fails("call chdir('dir-abcd')", 'E344:')
silent! let d = chdir("dir_abcd")
call assert_equal("", d)
" Should not crash
diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim
index d386d74f8d..54e0a62ce5 100644
--- a/src/nvim/testdir/test_charsearch.vim
+++ b/src/nvim/testdir/test_charsearch.vim
@@ -43,36 +43,6 @@ func Test_charsearch()
enew!
endfunc
-" Test for t,f,F,T movement commands and 'cpo-;' setting
-func Test_search_cmds()
- enew!
- call append(0, ["aaa two three four", " zzz", "yyy ",
- \ "bbb yee yoo four", "ccc two three four",
- \ "ddd yee yoo four"])
- set cpo-=;
- 1
- normal! 0tt;D
- 2
- normal! 0fz;D
- 3
- normal! $Fy;D
- 4
- normal! $Ty;D
- set cpo+=;
- 5
- normal! 0tt;;D
- 6
- normal! $Ty;;D
-
- call assert_equal('aaa two', getline(1))
- call assert_equal(' z', getline(2))
- call assert_equal('y', getline(3))
- call assert_equal('bbb y', getline(4))
- call assert_equal('ccc', getline(5))
- call assert_equal('ddd yee y', getline(6))
- enew!
-endfunc
-
" Test for character search in virtual edit mode with <Tab>
func Test_csearch_virtualedit()
new
@@ -81,7 +51,7 @@ func Test_csearch_virtualedit()
normal! tb
call assert_equal([0, 1, 2, 6], getpos('.'))
set virtualedit&
- close!
+ bw!
endfunc
" Test for character search failure in latin1 encoding
@@ -95,7 +65,34 @@ func Test_charsearch_latin1()
call assert_beeps('normal $Fz')
call assert_beeps('normal $Tx')
let &encoding = save_enc
- close!
+ bw!
+endfunc
+
+" Test for using character search to find a multibyte character with composing
+" characters.
+func Test_charsearch_composing_char()
+ new
+ call setline(1, "one two thq\u0328\u0301r\u0328\u0301ree")
+ call feedkeys("fr\u0328\u0301", 'xt')
+ call assert_equal([0, 1, 16, 0, 12], getcurpos())
+
+ " use character search with a multi-byte character followed by a
+ " non-composing character
+ call setline(1, "abc deȉf ghi")
+ call feedkeys("ggcf\u0209\u0210", 'xt')
+ call assert_equal("\u0210f ghi", getline(1))
+ bw!
+endfunc
+
+" Test for character search with 'hkmap'
+func Test_charsearch_hkmap()
+ new
+ set hkmap
+ call setline(1, "ùðáâ÷ëòéïçìêöî")
+ call feedkeys("fë", 'xt')
+ call assert_equal([0, 1, 11, 0, 6], getcurpos())
+ set hkmap&
+ bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim
index 943f79d98f..19a92dce3c 100644
--- a/src/nvim/testdir/test_clientserver.vim
+++ b/src/nvim/testdir/test_clientserver.vim
@@ -102,7 +102,7 @@ func Test_client_server()
call remote_send(v:servername, ":let g:testvar2 = 75\<CR>")
call feedkeys('', 'x')
call assert_equal(75, g:testvar2)
- call assert_fails('let v = remote_expr(v:servername, "/2")', 'E449:')
+ call assert_fails('let v = remote_expr(v:servername, "/2")', ['E15:.*/2'])
call remote_send(name, ":call server2client(expand('<client>'), 'got it')\<CR>", 'g:myserverid')
call assert_equal('got it', g:myserverid->remote_read(2))
@@ -182,9 +182,10 @@ func Test_client_server()
endif
endtry
+ call assert_fails('call remote_startserver([])', 'E730:')
call assert_fails("let x = remote_peek([])", 'E730:')
- call assert_fails("let x = remote_read('vim10')", 'E277:')
- call assert_fails("call server2client('abc', 'xyz')", 'E258:')
+ call assert_fails("let x = remote_read('vim10')", ['E573:.*vim10'])
+ call assert_fails("call server2client('abc', 'xyz')", ['E573:.*abc'])
endfunc
" Uncomment this line to get a debugging log
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 00bfadec93..c00172ed68 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -89,6 +89,13 @@ func Test_complete_wildmenu()
call assert_equal('"e Xtestfile3 Xtestfile4', @:)
cd -
+ cnoremap <expr> <F2> wildmenumode()
+ call feedkeys(":cd Xdir\<Tab>\<F2>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"cd Xdir1/0', @:)
+ call feedkeys(":e Xdir1/\<Tab>\<F2>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"e Xdir1/Xdir2/1', @:)
+ cunmap <F2>
+
" cleanup
%bwipe
call delete('Xdir1/Xdir2/Xtestfile4')
@@ -1147,7 +1154,7 @@ func Test_cmdline_search_range()
call assert_equal('B', getline(2))
let @/ = 'apple'
- call assert_fails('\/print', 'E486:')
+ call assert_fails('\/print', ['E486:.*apple'])
bwipe!
endfunc
@@ -1263,6 +1270,11 @@ func Test_verbosefile()
let log = readfile('Xlog')
call assert_match("foo\nbar", join(log, "\n"))
call delete('Xlog')
+ call mkdir('Xdir')
+ if !has('win32') " FIXME: no error on Windows, libuv bug?
+ call assert_fails('set verbosefile=Xdir', ['E484:.*Xdir', 'E474:'])
+ endif
+ call delete('Xdir', 'd')
endfunc
func Test_verbose_option()
@@ -1513,7 +1525,7 @@ func Test_cmdwin_jump_to_win()
call assert_fails('call feedkeys("q:\<C-W>\<C-W>\<CR>", "xt")', 'E11:')
new
set modified
- call assert_fails('call feedkeys("q/:qall\<CR>", "xt")', 'E162:')
+ call assert_fails('call feedkeys("q/:qall\<CR>", "xt")', ['E37:', 'E162:'])
close!
call feedkeys("q/:close\<CR>", "xt")
call assert_equal(1, winnr('$'))
@@ -1527,13 +1539,7 @@ endfunc
func Test_cmdwin_tabpage()
tabedit
- " v8.2.1919 isn't ported yet, so E492 is thrown after E11 here.
- " v8.2.1183 also isn't ported yet, so we also can't assert E11 directly.
- " For now, assert E11 and E492 separately. When v8.2.1183 is ported, the
- " assert for E492 will fail and this workaround should be removed.
- " call assert_fails("silent norm q/g :I\<Esc>", 'E11:')
- call assert_fails("silent norm q/g ", 'E11:')
- call assert_fails("silent norm q/g :I\<Esc>", 'E492:')
+ call assert_fails("silent norm q/g :I\<Esc>", 'E11:')
tabclose!
endfunc
diff --git a/src/nvim/testdir/test_cpoptions.vim b/src/nvim/testdir/test_cpoptions.vim
new file mode 100644
index 0000000000..ef51d955f1
--- /dev/null
+++ b/src/nvim/testdir/test_cpoptions.vim
@@ -0,0 +1,927 @@
+" Test for the various 'cpoptions' (cpo) flags
+
+source check.vim
+source shared.vim
+source view_util.vim
+
+" Test for the 'a' flag in 'cpo'. Reading a file should set the alternate
+" file name.
+func Test_cpo_a()
+ let save_cpo = &cpo
+ call writefile(['one'], 'Xfile')
+ " Wipe out all the buffers, so that the alternate file is empty
+ edit Xfoo | %bw
+ set cpo-=a
+ new
+ read Xfile
+ call assert_equal('', @#)
+ %d
+ set cpo+=a
+ read Xfile
+ call assert_equal('Xfile', @#)
+ close!
+ call delete('Xfile')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'A' flag in 'cpo'. Writing a file should set the alternate
+" file name.
+func Test_cpo_A()
+ let save_cpo = &cpo
+ " Wipe out all the buffers, so that the alternate file is empty
+ edit Xfoo | %bw
+ set cpo-=A
+ new Xfile1
+ write Xfile2
+ call assert_equal('', @#)
+ %bw
+ call delete('Xfile2')
+ new Xfile1
+ set cpo+=A
+ write Xfile2
+ call assert_equal('Xfile2', @#)
+ close!
+ call delete('Xfile2')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'b' flag in 'cpo'. "\|" at the end of a map command is
+" recognized as the end of the map.
+func Test_cpo_b()
+ let save_cpo = &cpo
+ set cpo+=b
+ nnoremap <F5> :pwd\<CR>\|let i = 1
+ call assert_equal(':pwd\<CR>\', maparg('<F5>'))
+ nunmap <F5>
+ exe "nnoremap <F5> :pwd\<C-V>|let i = 1"
+ call assert_equal(':pwd|let i = 1', maparg('<F5>'))
+ nunmap <F5>
+ set cpo-=b
+ nnoremap <F5> :pwd\<CR>\|let i = 1
+ call assert_equal(':pwd\<CR>|let i = 1', maparg('<F5>'))
+ let &cpo = save_cpo
+ nunmap <F5>
+endfunc
+
+" Test for the 'B' flag in 'cpo'. A backslash in mappings, abbreviations, user
+" commands and menu commands has no special meaning.
+func Test_cpo_B()
+ let save_cpo = &cpo
+ new
+ set cpo-=B
+ iabbr <buffer> abc ab\<BS>d
+ exe "normal iabc "
+ call assert_equal('ab<BS>d ', getline(1))
+ %d
+ set cpo+=B
+ iabbr <buffer> abc ab\<BS>d
+ exe "normal iabc "
+ call assert_equal('abd ', getline(1))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'c' flag in 'cpo'.
+func Test_cpo_c()
+ let save_cpo = &cpo
+ set cpo+=c
+ new
+ call setline(1, ' abababababab')
+ exe "normal gg/abab\<CR>"
+ call assert_equal(3, searchcount().total)
+ set cpo-=c
+ exe "normal gg/abab\<CR>"
+ call assert_equal(5, searchcount().total)
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'C' flag in 'cpo' (line continuation)
+func Test_cpo_C()
+ let save_cpo = &cpo
+ call writefile(['let l = [', '\ 1,', '\ 2]'], 'Xfile')
+ set cpo-=C
+ source Xfile
+ call assert_equal([1, 2], g:l)
+ set cpo+=C
+ call assert_fails('source Xfile', ['E697:', 'E10:'])
+ call delete('Xfile')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'd' flag in 'cpo' (tags relative to the current file)
+func Test_cpo_d()
+ let save_cpo = &cpo
+ call mkdir('Xdir')
+ call writefile(["one\tXfile1\t/^one$/"], 'tags')
+ call writefile(["two\tXfile2\t/^two$/"], 'Xdir/tags')
+ set tags=./tags
+ set cpo-=d
+ edit Xdir/Xfile
+ call assert_equal('two', taglist('.*')[0].name)
+ set cpo+=d
+ call assert_equal('one', taglist('.*')[0].name)
+ %bw!
+ call delete('tags')
+ call delete('Xdir', 'rf')
+ set tags&
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'D' flag in 'cpo' (digraph after a r, f or t)
+func Test_cpo_D()
+ CheckFeature digraphs
+ let save_cpo = &cpo
+ new
+ set cpo-=D
+ call setline(1, 'abcdefgh|')
+ exe "norm! 1gg0f\<c-k>!!"
+ call assert_equal(9, col('.'))
+ set cpo+=D
+ exe "norm! 1gg0f\<c-k>!!"
+ call assert_equal(1, col('.'))
+ set cpo-=D
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'e' flag in 'cpo'
+func Test_cpo_e()
+ let save_cpo = &cpo
+ let @a='let i = 45'
+ set cpo+=e
+ call feedkeys(":@a\<CR>", 'xt')
+ call assert_equal(45, i)
+ set cpo-=e
+ call feedkeys(":@a\<CR>6\<CR>", 'xt')
+ call assert_equal(456, i)
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'E' flag in 'cpo' with yank, change, delete, etc. operators
+func Test_cpo_E()
+ new
+ call setline(1, '')
+ set cpo+=E
+ " yank an empty line
+ call assert_beeps('normal "ayl')
+ " change an empty line
+ call assert_beeps('normal lcTa')
+ call assert_beeps('normal 0c0')
+ " delete an empty line
+ call assert_beeps('normal D')
+ call assert_beeps('normal dl')
+ call assert_equal('', getline(1))
+ " change case of an empty line
+ call assert_beeps('normal gul')
+ call assert_beeps('normal gUl')
+ " replace a character
+ call assert_beeps('normal vrx')
+ " increment and decrement
+ call assert_beeps('exe "normal v\<C-A>"')
+ call assert_beeps('exe "normal v\<C-X>"')
+ set cpo-=E
+ close!
+endfunc
+
+" Test for the 'f' flag in 'cpo' (read in an empty buffer sets the file name)
+func Test_cpo_f()
+ let save_cpo = &cpo
+ new
+ set cpo-=f
+ read test_cpoptions.vim
+ call assert_equal('', @%)
+ %d
+ set cpo+=f
+ read test_cpoptions.vim
+ call assert_equal('test_cpoptions.vim', @%)
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'F' flag in 'cpo' (write in an empty buffer sets the file name)
+func Test_cpo_F()
+ let save_cpo = &cpo
+ new
+ set cpo-=F
+ write Xfile
+ call assert_equal('', @%)
+ call delete('Xfile')
+ set cpo+=F
+ write Xfile
+ call assert_equal('Xfile', @%)
+ close!
+ call delete('Xfile')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'g' flag in 'cpo' (jump to line 1 when re-editing a file)
+func Test_cpo_g()
+ throw 'Skipped: Nvim does not support cpoptions flag "g"'
+ let save_cpo = &cpo
+ new test_cpoptions.vim
+ set cpo-=g
+ normal 20G
+ edit
+ call assert_equal(20, line('.'))
+ set cpo+=g
+ edit
+ call assert_equal(1, line('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for inserting text in a line with only spaces ('H' flag in 'cpoptions')
+func Test_cpo_H()
+ throw 'Skipped: Nvim does not support cpoptions flag "H"'
+ let save_cpo = &cpo
+ new
+ set cpo-=H
+ call setline(1, ' ')
+ normal! Ia
+ call assert_equal(' a', getline(1))
+ set cpo+=H
+ call setline(1, ' ')
+ normal! Ia
+ call assert_equal(' a ', getline(1))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" TODO: Add a test for the 'i' flag in 'cpo'
+" Interrupting the reading of a file will leave it modified.
+
+" Test for the 'I' flag in 'cpo' (deleting autoindent when using arrow keys)
+func Test_cpo_I()
+ let save_cpo = &cpo
+ new
+ setlocal autoindent
+ set cpo+=I
+ exe "normal i one\<CR>\<Up>"
+ call assert_equal(' ', getline(2))
+ set cpo-=I
+ %d
+ exe "normal i one\<CR>\<Up>"
+ call assert_equal('', getline(2))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'j' flag in 'cpo' is in the test_join.vim file.
+
+" Test for the 'J' flag in 'cpo' (two spaces after a sentence)
+func Test_cpo_J()
+ let save_cpo = &cpo
+ new
+ set cpo-=J
+ call setline(1, 'one. two! three? four."'' five.)]')
+ normal 0
+ for colnr in [6, 12, 19, 28, 34]
+ normal )
+ call assert_equal(colnr, col('.'))
+ endfor
+ for colnr in [28, 19, 12, 6, 1]
+ normal (
+ call assert_equal(colnr, col('.'))
+ endfor
+ set cpo+=J
+ normal 0
+ for colnr in [12, 28, 34]
+ normal )
+ call assert_equal(colnr, col('.'))
+ endfor
+ for colnr in [28, 12, 1]
+ normal (
+ call assert_equal(colnr, col('.'))
+ endfor
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" TODO: Add a test for the 'k' flag in 'cpo'.
+" Disable the recognition of raw key codes in mappings, abbreviations, and the
+" "to" part of menu commands.
+
+" TODO: Add a test for the 'K' flag in 'cpo'.
+" Don't wait for a key code to complete when it is halfway a mapping.
+
+" Test for the 'l' flag in 'cpo' (backslash in a [] range)
+func Test_cpo_l()
+ let save_cpo = &cpo
+ new
+ call setline(1, ['', "a\tc" .. '\t'])
+ set cpo-=l
+ exe 'normal gg/[\t]' .. "\<CR>"
+ call assert_equal([2, 8], [col('.'), virtcol('.')])
+ set cpo+=l
+ exe 'normal gg/[\t]' .. "\<CR>"
+ call assert_equal([4, 10], [col('.'), virtcol('.')])
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for inserting tab in virtual replace mode ('L' flag in 'cpoptions')
+func Test_cpo_L()
+ let save_cpo = &cpo
+ new
+ set cpo-=L
+ call setline(1, 'abcdefghijklmnopqr')
+ exe "normal 0gR\<Tab>"
+ call assert_equal("\<Tab>ijklmnopqr", getline(1))
+ set cpo+=L
+ set list
+ call setline(1, 'abcdefghijklmnopqr')
+ exe "normal 0gR\<Tab>"
+ call assert_equal("\<Tab>cdefghijklmnopqr", getline(1))
+ set nolist
+ call setline(1, 'abcdefghijklmnopqr')
+ exe "normal 0gR\<Tab>"
+ call assert_equal("\<Tab>ijklmnopqr", getline(1))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" TODO: Add a test for the 'm' flag in 'cpo'.
+" When included, a showmatch will always wait half a second. When not
+" included, a showmatch will wait half a second or until a character is typed.
+
+" Test for the 'M' flag in 'cpo' (% with escape parenthesis)
+func Test_cpo_M()
+ let save_cpo = &cpo
+ new
+ call setline(1, ['( \( )', '\( ( \)'])
+
+ set cpo-=M
+ call cursor(1, 1)
+ normal %
+ call assert_equal(6, col('.'))
+ call cursor(1, 4)
+ call assert_beeps('normal %')
+ call cursor(2, 2)
+ normal %
+ call assert_equal(7, col('.'))
+ call cursor(2, 4)
+ call assert_beeps('normal %')
+
+ set cpo+=M
+ call cursor(1, 4)
+ normal %
+ call assert_equal(6, col('.'))
+ call cursor(1, 1)
+ call assert_beeps('normal %')
+ call cursor(2, 4)
+ normal %
+ call assert_equal(7, col('.'))
+ call cursor(2, 1)
+ call assert_beeps('normal %')
+
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'n' flag in 'cpo' (using number column for wrapped lines)
+func Test_cpo_n()
+ let save_cpo = &cpo
+ new
+ call setline(1, repeat('a', &columns))
+ setlocal number
+ set cpo-=n
+ redraw!
+ call assert_equal(' aaaa', Screenline(2))
+ set cpo+=n
+ redraw!
+ call assert_equal('aaaa', Screenline(2))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'o' flag in 'cpo' (line offset to search command)
+func Test_cpo_o()
+ let save_cpo = &cpo
+ new
+ call setline(1, ['', 'one', 'two', 'three', 'one', 'two', 'three'])
+ set cpo-=o
+ exe "normal /one/+2\<CR>"
+ normal n
+ call assert_equal(7, line('.'))
+ set cpo+=o
+ exe "normal /one/+2\<CR>"
+ normal n
+ call assert_equal(5, line('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'O' flag in 'cpo' (overwriting an existing file)
+func Test_cpo_O()
+ let save_cpo = &cpo
+ new Xfile
+ call setline(1, 'one')
+ call writefile(['two'], 'Xfile')
+ set cpo-=O
+ call assert_fails('write', 'E13:')
+ set cpo+=O
+ write
+ call assert_equal(['one'], readfile('Xfile'))
+ close!
+ call delete('Xfile')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'p' flag in 'cpo' is in the test_lispwords.vim file.
+
+" Test for the 'P' flag in 'cpo' (appending to a file sets the current file
+" name)
+func Test_cpo_P()
+ let save_cpo = &cpo
+ call writefile([], 'Xfile')
+ new
+ call setline(1, 'one')
+ set cpo+=F
+ set cpo-=P
+ write >> Xfile
+ call assert_equal('', @%)
+ set cpo+=P
+ write >> Xfile
+ call assert_equal('Xfile', @%)
+ close!
+ call delete('Xfile')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'q' flag in 'cpo' (joining multiple lines)
+func Test_cpo_q()
+ let save_cpo = &cpo
+ new
+ call setline(1, ['one', 'two', 'three', 'four', 'five'])
+ set cpo-=q
+ normal gg4J
+ call assert_equal(14, col('.'))
+ %d
+ call setline(1, ['one', 'two', 'three', 'four', 'five'])
+ set cpo+=q
+ normal gg4J
+ call assert_equal(4, col('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'r' flag in 'cpo' (redo command with a search motion)
+func Test_cpo_r()
+ let save_cpo = &cpo
+ new
+ call setline(1, repeat(['one two three four'], 2))
+ set cpo-=r
+ exe "normal ggc/two\<CR>abc "
+ let @/ = 'three'
+ normal 2G.
+ call assert_equal('abc two three four', getline(2))
+ %d
+ call setline(1, repeat(['one two three four'], 2))
+ set cpo+=r
+ exe "normal ggc/two\<CR>abc "
+ let @/ = 'three'
+ normal 2G.
+ call assert_equal('abc three four', getline(2))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'R' flag in 'cpo' (clear marks after a filter command)
+func Test_cpo_R()
+ CheckUnix
+ let save_cpo = &cpo
+ new
+ call setline(1, ['three', 'one', 'two'])
+ set cpo-=R
+ 3mark r
+ %!sort
+ call assert_equal(3, line("'r"))
+ %d
+ call setline(1, ['three', 'one', 'two'])
+ set cpo+=R
+ 3mark r
+ %!sort
+ call assert_equal(0, line("'r"))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" TODO: Add a test for the 's' flag in 'cpo'.
+" Set buffer options when entering the buffer for the first time. If not
+" present the options are set when the buffer is created.
+
+" Test for the 'S' flag in 'cpo' (copying buffer options)
+func Test_cpo_S()
+ let save_cpo = &cpo
+ new Xfile1
+ set noautoindent
+ new Xfile2
+ set cpo-=S
+ set autoindent
+ wincmd p
+ call assert_equal(0, &autoindent)
+ wincmd p
+ call assert_equal(1, &autoindent)
+ set cpo+=S
+ wincmd p
+ call assert_equal(1, &autoindent)
+ set noautoindent
+ wincmd p
+ call assert_equal(0, &autoindent)
+ wincmd t
+ close!
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 't' flag in 'cpo' is in the test_tagjump.vim file.
+
+" Test for the 'u' flag in 'cpo' (Vi-compatible undo)
+func Test_cpo_u()
+ let save_cpo = &cpo
+ new
+ set cpo-=u
+ exe "normal iabc\<C-G>udef\<C-G>ughi"
+ normal uu
+ call assert_equal('abc', getline(1))
+ %d
+ set cpo+=u
+ exe "normal iabc\<C-G>udef\<C-G>ughi"
+ normal uu
+ call assert_equal('abcdefghi', getline(1))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" TODO: Add a test for the 'v' flag in 'cpo'.
+" Backspaced characters remain visible on the screen in Insert mode.
+
+" Test for the 'w' flag in 'cpo' ('cw' on a blank character changes only one
+" character)
+func Test_cpo_w()
+ throw 'Skipped: Nvim does not support cpoptions flag "w"'
+ let save_cpo = &cpo
+ new
+ set cpo+=w
+ call setline(1, 'here are some words')
+ norm! 1gg0elcwZZZ
+ call assert_equal('hereZZZ are some words', getline('.'))
+ norm! 1gg2elcWYYY
+ call assert_equal('hereZZZ areYYY some words', getline('.'))
+ set cpo-=w
+ call setline(1, 'here are some words')
+ norm! 1gg0elcwZZZ
+ call assert_equal('hereZZZare some words', getline('.'))
+ norm! 1gg2elcWYYY
+ call assert_equal('hereZZZare someYYYwords', getline('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'W' flag in 'cpo' is in the test_writefile.vim file
+
+" Test for the 'x' flag in 'cpo' (Esc on command-line executes command)
+func Test_cpo_x()
+ let save_cpo = &cpo
+ set cpo-=x
+ let i = 1
+ call feedkeys(":let i=10\<Esc>", 'xt')
+ call assert_equal(1, i)
+ set cpo+=x
+ call feedkeys(":let i=10\<Esc>", 'xt')
+ call assert_equal(10, i)
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'X' flag in 'cpo' ('R' with a count)
+func Test_cpo_X()
+ let save_cpo = &cpo
+ new
+ call setline(1, 'aaaaaa')
+ set cpo-=X
+ normal gg4Rx
+ call assert_equal('xxxxaa', getline(1))
+ normal ggRy
+ normal 4.
+ call assert_equal('yyyyaa', getline(1))
+ call setline(1, 'aaaaaa')
+ set cpo+=X
+ normal gg4Rx
+ call assert_equal('xxxxaaaaa', getline(1))
+ normal ggRy
+ normal 4.
+ call assert_equal('yyyyxxxaaaaa', getline(1))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'y' flag in 'cpo' (repeating a yank command)
+func Test_cpo_y()
+ let save_cpo = &cpo
+ new
+ call setline(1, ['one', 'two'])
+ set cpo-=y
+ normal ggyy
+ normal 2G.
+ call assert_equal("one\n", @")
+ %d
+ call setline(1, ['one', 'two'])
+ set cpo+=y
+ normal ggyy
+ normal 2G.
+ call assert_equal("two\n", @")
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the 'Z' flag in 'cpo' (write! resets 'readonly')
+func Test_cpo_Z()
+ let save_cpo = &cpo
+ call writefile([], 'Xfile')
+ new Xfile
+ setlocal readonly
+ set cpo-=Z
+ write!
+ call assert_equal(0, &readonly)
+ set cpo+=Z
+ setlocal readonly
+ write!
+ call assert_equal(1, &readonly)
+ close!
+ call delete('Xfile')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '!' flag in 'cpo' is in the test_normal.vim file
+
+" Test for displaying dollar when changing text ('$' flag in 'cpoptions')
+func Test_cpo_dollar()
+ throw 'Skipped: use test/functional/legacy/cpoptions_spec.lua'
+ new
+ let g:Line = ''
+ func SaveFirstLine()
+ let g:Line = Screenline(1)
+ return ''
+ endfunc
+ inoremap <expr> <buffer> <F2> SaveFirstLine()
+ call test_override('redraw_flag', 1)
+ set cpo+=$
+ call setline(1, 'one two three')
+ redraw!
+ exe "normal c2w\<F2>vim"
+ call assert_equal('one tw$ three', g:Line)
+ call assert_equal('vim three', getline(1))
+ set cpo-=$
+ call test_override('ALL', 0)
+ delfunc SaveFirstLine
+ %bw!
+endfunc
+
+" Test for the '%' flag in 'cpo' (parenthesis matching inside strings)
+func Test_cpo_percent()
+ let save_cpo = &cpo
+ new
+ call setline(1, ' if (strcmp("ab)cd(", s))')
+ set cpo-=%
+ normal 8|%
+ call assert_equal(28, col('.'))
+ normal 15|%
+ call assert_equal(27, col('.'))
+ normal 27|%
+ call assert_equal(15, col('.'))
+ call assert_beeps("normal 19|%")
+ call assert_beeps("normal 22|%")
+ set cpo+=%
+ normal 8|%
+ call assert_equal(28, col('.'))
+ normal 15|%
+ call assert_equal(19, col('.'))
+ normal 27|%
+ call assert_equal(22, col('.'))
+ normal 19|%
+ call assert_equal(15, col('.'))
+ normal 22|%
+ call assert_equal(27, col('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for cursor movement with '-' in 'cpoptions'
+func Test_cpo_minus()
+ throw 'Skipped: Nvim does not support cpoptions flag "-"'
+ new
+ call setline(1, ['foo', 'bar', 'baz'])
+ let save_cpo = &cpo
+ set cpo+=-
+ call assert_beeps('normal 10j')
+ call assert_equal(1, line('.'))
+ normal G
+ call assert_beeps('normal 10k')
+ call assert_equal(3, line('.'))
+ call assert_fails(10, 'E16:')
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '+' flag in 'cpo' ('write file' command resets the 'modified'
+" flag)
+func Test_cpo_plus()
+ let save_cpo = &cpo
+ call writefile([], 'Xfile')
+ new Xfile
+ call setline(1, 'foo')
+ write X1
+ call assert_equal(1, &modified)
+ set cpo+=+
+ write X2
+ call assert_equal(0, &modified)
+ close!
+ call delete('Xfile')
+ call delete('X1')
+ call delete('X2')
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '*' flag in 'cpo' (':*' is same as ':@')
+func Test_cpo_star()
+ throw 'Skipped: Nvim does not support cpoptions flag "*"'
+ let save_cpo = &cpo
+ let x = 0
+ new
+ set cpo-=*
+ let @a = 'let x += 1'
+ call assert_fails('*a', 'E20:')
+ set cpo+=*
+ *a
+ call assert_equal(1, x)
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '<' flag in 'cpo' is in the test_mapping.vim file
+
+" Test for the '>' flag in 'cpo' (use a new line when appending to a register)
+func Test_cpo_gt()
+ let save_cpo = &cpo
+ new
+ call setline(1, 'one two')
+ set cpo-=>
+ let @r = ''
+ normal gg"Rye
+ normal "Rye
+ call assert_equal("oneone", @r)
+ set cpo+=>
+ let @r = ''
+ normal gg"Rye
+ normal "Rye
+ call assert_equal("\none\none", @r)
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the ';' flag in 'cpo'
+" Test for t,f,F,T movement commands and 'cpo-;' setting
+func Test_cpo_semicolon()
+ let save_cpo = &cpo
+ new
+ call append(0, ["aaa two three four", " zzz", "yyy ",
+ \ "bbb yee yoo four", "ccc two three four",
+ \ "ddd yee yoo four"])
+ set cpo-=;
+ 1
+ normal! 0tt;D
+ 2
+ normal! 0fz;D
+ 3
+ normal! $Fy;D
+ 4
+ normal! $Ty;D
+ set cpo+=;
+ 5
+ normal! 0tt;;D
+ 6
+ normal! $Ty;;D
+
+ call assert_equal('aaa two', getline(1))
+ call assert_equal(' z', getline(2))
+ call assert_equal('y', getline(3))
+ call assert_equal('bbb y', getline(4))
+ call assert_equal('ccc', getline(5))
+ call assert_equal('ddd yee y', getline(6))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '#' flag in 'cpo' (count before 'D', 'o' and 'O' operators)
+func Test_cpo_hash()
+ throw 'Skipped: Nvim does not support cpoptions flag "#"'
+ let save_cpo = &cpo
+ new
+ set cpo-=#
+ call setline(1, ['one', 'two', 'three'])
+ normal gg2D
+ call assert_equal(['three'], getline(1, '$'))
+ normal gg2ofour
+ call assert_equal(['three', 'four', 'four'], getline(1, '$'))
+ normal gg2Otwo
+ call assert_equal(['two', 'two', 'three', 'four', 'four'], getline(1, '$'))
+ %d
+ set cpo+=#
+ call setline(1, ['one', 'two', 'three'])
+ normal gg2D
+ call assert_equal(['', 'two', 'three'], getline(1, '$'))
+ normal gg2oone
+ call assert_equal(['', 'one', 'two', 'three'], getline(1, '$'))
+ normal gg2Ozero
+ call assert_equal(['zero', '', 'one', 'two', 'three'], getline(1, '$'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '&' flag in 'cpo'. The swap file is kept when a buffer is still
+" loaded and ':preserve' is used.
+func Test_cpo_ampersand()
+ throw 'Skipped: Nvim does not support cpoptions flag "&"'
+ call writefile(['one'], 'Xfile')
+ let after =<< trim [CODE]
+ set cpo+=&
+ preserve
+ qall
+ [CODE]
+ if RunVim([], after, 'Xfile')
+ call assert_equal(1, filereadable('.Xfile.swp'))
+ call delete('.Xfile.swp')
+ endif
+ call delete('Xfile')
+endfunc
+
+" Test for the '\' flag in 'cpo' (backslash in a [] range in a search pattern)
+func Test_cpo_backslash()
+ throw 'Skipped: Nvim does not support cpoptions flag "\"'
+ let save_cpo = &cpo
+ new
+ call setline(1, ['', " \\-string"])
+ set cpo-=\
+ exe 'normal gg/[ \-]' .. "\<CR>n"
+ call assert_equal(3, col('.'))
+ set cpo+=\
+ exe 'normal gg/[ \-]' .. "\<CR>n"
+ call assert_equal(2, col('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '/' flag in 'cpo' is in the test_substitute.vim file
+
+" Test for the '{' flag in 'cpo' (the "{" and "}" commands stop at a {
+" character at the start of a line)
+func Test_cpo_brace()
+ throw 'Skipped: Nvim does not support cpoptions flag "{"'
+ let save_cpo = &cpo
+ new
+ call setline(1, ['', '{', ' int i;', '}', ''])
+ set cpo-={
+ normal gg}
+ call assert_equal(5, line('.'))
+ normal G{
+ call assert_equal(1, line('.'))
+ set cpo+={
+ normal gg}
+ call assert_equal(2, line('.'))
+ normal G{
+ call assert_equal(2, line('.'))
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" Test for the '.' flag in 'cpo' (:cd command fails if the current buffer is
+" modified)
+func Test_cpo_dot()
+ throw 'Skipped: Nvim does not support cpoptions flag "."'
+ let save_cpo = &cpo
+ new Xfoo
+ call setline(1, 'foo')
+ let save_dir = getcwd()
+ set cpo+=.
+
+ " :cd should fail when buffer is modified and 'cpo' contains dot.
+ call assert_fails('cd ..', 'E747:')
+ call assert_equal(save_dir, getcwd())
+
+ " :cd with exclamation mark should succeed.
+ cd! ..
+ call assert_notequal(save_dir, getcwd())
+
+ " :cd should succeed when buffer has been written.
+ w!
+ exe 'cd ' .. fnameescape(save_dir)
+ call assert_equal(save_dir, getcwd())
+
+ call delete('Xfoo')
+ set cpo&
+ close!
+ let &cpo = save_cpo
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim
index 13796449ab..679fe89628 100644
--- a/src/nvim/testdir/test_display.vim
+++ b/src/nvim/testdir/test_display.vim
@@ -195,8 +195,6 @@ func Test_edit_long_file_name()
call VerifyScreenDump(buf, 'Test_long_file_name_1', {})
- call term_sendkeys(buf, ":q\<cr>")
-
" clean up
call StopVimInTerminal(buf)
call delete(longName)
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index 9783ed19a7..89fd73351d 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1433,9 +1433,7 @@ endfunc
func Test_edit_rightleft()
" Cursor in rightleft mode moves differently
- if !exists("+rightleft")
- return
- endif
+ CheckFeature rightleft
call NewWindow(10, 20)
call setline(1, ['abc', 'def', 'ghi'])
call cursor(1, 2)
@@ -1480,6 +1478,13 @@ func Test_edit_rightleft()
\" ihg",
\" ~"]
call assert_equal(join(expect, "\n"), join(lines, "\n"))
+ %d _
+ " call test_override('redraw_flag', 1)
+ " call test_override('char_avail', 1)
+ call feedkeys("a\<C-V>x41", "xt")
+ redraw!
+ call assert_equal(repeat(' ', 19) .. 'A', Screenline(1))
+ " call test_override('ALL', 0)
set norightleft
bw!
endfunc
@@ -1724,40 +1729,6 @@ func Test_edit_illegal_filename()
close!
endfunc
-" Test for inserting text in a line with only spaces ('H' flag in 'cpoptions')
-func Test_edit_cpo_H()
- throw 'Skipped: Nvim does not support cpoptions flag "H"'
- new
- call setline(1, ' ')
- normal! Ia
- call assert_equal(' a', getline(1))
- set cpo+=H
- call setline(1, ' ')
- normal! Ia
- call assert_equal(' a ', getline(1))
- set cpo-=H
- close!
-endfunc
-
-" Test for inserting tab in virtual replace mode ('L' flag in 'cpoptions')
-func Test_edit_cpo_L()
- new
- call setline(1, 'abcdefghijklmnopqr')
- exe "normal 0gR\<Tab>"
- call assert_equal("\<Tab>ijklmnopqr", getline(1))
- set cpo+=L
- set list
- call setline(1, 'abcdefghijklmnopqr')
- exe "normal 0gR\<Tab>"
- call assert_equal("\<Tab>cdefghijklmnopqr", getline(1))
- set nolist
- call setline(1, 'abcdefghijklmnopqr')
- exe "normal 0gR\<Tab>"
- call assert_equal("\<Tab>ijklmnopqr", getline(1))
- set cpo-=L
- %bw!
-endfunc
-
" Test for editing a directory
func Test_edit_is_a_directory()
CheckEnglish
@@ -1902,6 +1873,107 @@ func Test_edit_insertmode_ex_edit()
call delete('Xtest_edit_insertmode_ex_edit')
endfunc
+" Pressing escape in 'insertmode' should beep
+func Test_edit_insertmode_esc_beeps()
+ throw "Skipped: Nvim does not support 'insertmode'"
+ new
+ set insertmode
+ call assert_beeps("call feedkeys(\"one\<Esc>\", 'xt')")
+ set insertmode&
+ " unsupported CTRL-G command should beep in insert mode.
+ call assert_beeps("normal i\<C-G>l")
+ close!
+endfunc
+
+" Test for 'hkmap' and 'hkmapp'
+func Test_edit_hkmap()
+ CheckFeature rightleft
+ if has('win32') && !has('gui')
+ " Test fails on the MS-Windows terminal version
+ return
+ endif
+ new
+
+ set revins hkmap
+ let str = 'abcdefghijklmnopqrstuvwxyz'
+ let str ..= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ let str ..= '`/'',.;'
+ call feedkeys('i' .. str, 'xt')
+ let expected = "óõú,.;"
+ let expected ..= "ZYXWVUTSRQPONMLKJIHGFEDCBA"
+ let expected ..= "æèñ'äåàãø/ôíîöêìçïéòë÷âáðù"
+ call assert_equal(expected, getline(1))
+
+ %d
+ set revins hkmap hkmapp
+ let str = 'abcdefghijklmnopqrstuvwxyz'
+ let str ..= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ call feedkeys('i' .. str, 'xt')
+ let expected = "õYXWVUTSRQóOïíLKJIHGFEDêBA"
+ let expected ..= "öòXùåèúæø'ôñðîì÷çéäâóǟãëáà"
+ call assert_equal(expected, getline(1))
+
+ set revins& hkmap& hkmapp&
+ close!
+endfunc
+
+" Test for 'allowrevins' and using CTRL-_ in insert mode
+func Test_edit_allowrevins()
+ CheckFeature rightleft
+ new
+ set allowrevins
+ call feedkeys("iABC\<C-_>DEF\<C-_>GHI", 'xt')
+ call assert_equal('ABCFEDGHI', getline(1))
+ set allowrevins&
+ close!
+endfunc
+
+" Test for inserting a register in insert mode using CTRL-R
+func Test_edit_insert_reg()
+ throw 'Skipped: use test/functional/legacy/edit_spec.lua'
+ new
+ let g:Line = ''
+ func SaveFirstLine()
+ let g:Line = Screenline(1)
+ return 'r'
+ endfunc
+ inoremap <expr> <buffer> <F2> SaveFirstLine()
+ call test_override('redraw_flag', 1)
+ call test_override('char_avail', 1)
+ let @r = 'sample'
+ call feedkeys("a\<C-R>=SaveFirstLine()\<CR>", "xt")
+ call assert_equal('"', g:Line)
+ call test_override('ALL', 0)
+ close!
+endfunc
+
+" When a character is inserted at the last position of the last line in a
+" window, the window contents should be scrolled one line up. If the top line
+" is part of a fold, then the entire fold should be scrolled up.
+func Test_edit_lastline_scroll()
+ new
+ let h = winheight(0)
+ let lines = ['one', 'two', 'three']
+ let lines += repeat(['vim'], h - 4)
+ call setline(1, lines)
+ call setline(h, repeat('x', winwidth(0) - 1))
+ call feedkeys("GAx", 'xt')
+ redraw!
+ call assert_equal(h - 1, winline())
+ call assert_equal(2, line('w0'))
+
+ " scroll with a fold
+ 1,2fold
+ normal gg
+ call setline(h + 1, repeat('x', winwidth(0) - 1))
+ call feedkeys("GAx", 'xt')
+ redraw!
+ call assert_equal(h - 1, winline())
+ call assert_equal(3, line('w0'))
+
+ close!
+endfunc
+
func Test_edit_browse()
" in the GUI this opens a file picker, we only test the terminal behavior
CheckNotGui
diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim
index 851048ec5b..46482c34a1 100644
--- a/src/nvim/testdir/test_eval_stuff.vim
+++ b/src/nvim/testdir/test_eval_stuff.vim
@@ -20,13 +20,8 @@ func Test_nocatch_restore_silent_emsg()
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)
+ echoerr 'wrong again'
+ call assert_equal('wrong again', ScreenLine(&lines))
endfunc
func Test_mkdir_p()
diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim
index 2f734cba26..93100732ed 100644
--- a/src/nvim/testdir/test_ex_mode.vim
+++ b/src/nvim/testdir/test_ex_mode.vim
@@ -97,7 +97,6 @@ func Test_Ex_substitute()
call term_sendkeys(buf, ":vi\<CR>")
call WaitForAssert({-> assert_match('foo bar', term_getline(buf, 1))}, 1000)
- call term_sendkeys(buf, ":q!\n")
call StopVimInTerminal(buf)
endfunc
diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim
index 04ab8e288f..582dcaac2c 100644
--- a/src/nvim/testdir/test_excmd.vim
+++ b/src/nvim/testdir/test_excmd.vim
@@ -481,8 +481,8 @@ func Test_redir_cmd()
call assert_fails('redir abc', 'E475:')
call assert_fails('redir => 1abc', 'E474:')
call assert_fails('redir => a b', 'E488:')
- call assert_fails('redir => abc[1]', 'E475:')
- let b=0zFF
+ call assert_fails('redir => abc[1]', 'E121:')
+ let b = 0zFF
call assert_fails('redir =>> b', 'E734:')
unlet b
diff --git a/src/nvim/testdir/test_exists.vim b/src/nvim/testdir/test_exists.vim
index 471c77853d..62c66192ef 100644
--- a/src/nvim/testdir/test_exists.vim
+++ b/src/nvim/testdir/test_exists.vim
@@ -68,6 +68,10 @@ func Test_exists()
" Existing environment variable
let $EDITOR_NAME = 'Vim Editor'
call assert_equal(1, exists('$EDITOR_NAME'))
+ if has('unix')
+ " ${name} environment variables are supported only on Unix-like systems
+ call assert_equal(1, exists('${VIM}'))
+ endif
" Non-existing environment variable
call assert_equal(0, exists('$NON_ENV_VAR'))
@@ -323,3 +327,5 @@ endfunc
func Test_exists_funcarg()
call FuncArg_Tests("arg1", "arg2")
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_expand.vim b/src/nvim/testdir/test_expand.vim
index aa131a49ff..4f5bb67d21 100644
--- a/src/nvim/testdir/test_expand.vim
+++ b/src/nvim/testdir/test_expand.vim
@@ -90,14 +90,26 @@ func Test_expandcmd()
" Test for expression expansion `=
let $FOO= "blue"
call assert_equal("blue sky", expandcmd("`=$FOO .. ' sky'`"))
+ let x = expandcmd("`=axbycz`")
+ call assert_equal('`=axbycz`', x)
+ call assert_fails('let x = expandcmd("`=axbycz`", #{errmsg: 1})', 'E121:')
+ let x = expandcmd("`=axbycz`", #{abc: []})
+ call assert_equal('`=axbycz`', x)
" Test for env variable with spaces
let $FOO= "foo bar baz"
call assert_equal("e foo bar baz", expandcmd("e $FOO"))
- if has('unix')
- " test for using the shell to expand a command argument
- call assert_equal('{1..4}', expandcmd('{1..4}'))
+ if has('unix') && executable('bash')
+ " test for using the shell to expand a command argument.
+ " only bash supports the {..} syntax
+ set shell=bash
+ let x = expandcmd('{1..4}')
+ call assert_equal('{1..4}', x)
+ call assert_fails("let x = expandcmd('{1..4}', #{errmsg: v:true})", 'E77:')
+ let x = expandcmd('{1..4}', #{error: v:true})
+ call assert_equal('{1..4}', x)
+ set shell&
endif
unlet $FOO
diff --git a/src/nvim/testdir/test_expand_func.vim b/src/nvim/testdir/test_expand_func.vim
index 80bfdb8553..454d76f0aa 100644
--- a/src/nvim/testdir/test_expand_func.vim
+++ b/src/nvim/testdir/test_expand_func.vim
@@ -139,6 +139,7 @@ func Test_expand_wildignore()
call assert_equal('test_expand_func.vim', expand('test_expand_func.vim', 1))
call assert_equal(['test_expand_func.vim'],
\ expand('test_expand_func.vim', 1, 1))
+ call assert_fails("call expand('*', [])", 'E745:')
set wildignore&
endfunc
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 66660ab75e..ea874cc398 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -65,6 +65,8 @@ func Test_strgetchar()
call assert_equal(-1, strgetchar('axb', -1))
call assert_equal(-1, strgetchar('axb', 3))
call assert_equal(-1, strgetchar('', 0))
+ call assert_fails("let c=strgetchar([], 1)", 'E730:')
+ call assert_fails("let c=strgetchar('axb', [])", 'E745:')
endfunc
func Test_strcharpart()
@@ -485,49 +487,6 @@ function Test_max_min_errors()
call assert_fails('call min(v:true)', 'min()')
endfunc
-func Test_substitute_expr()
- let g:val = 'XXX'
- call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', ''))
- call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, ''))
- call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
- \ '\=nr2char("0x" . submatch(1))', 'g'))
- call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
- \ {-> nr2char("0x" . submatch(1))}, 'g'))
-
- call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)',
- \ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
-
- func Recurse()
- return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '')
- endfunc
- " recursive call works
- call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, ''))
-endfunc
-
-func Test_invalid_submatch()
- " This was causing invalid memory access in Vim-7.4.2232 and older
- call assert_fails("call substitute('x', '.', {-> submatch(10)}, '')", 'E935:')
-endfunc
-
-func Test_substitute_expr_arg()
- call assert_equal('123456789-123456789=', substitute('123456789',
- \ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
- \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
-
- call assert_equal('123456-123456=789', substitute('123456789',
- \ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
- \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
-
- call assert_equal('123456789-123456789x=', substitute('123456789',
- \ '\(.\)\(.\)\(.*\)',
- \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
-
- call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
- call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
- call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
- call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
-endfunc
-
func Test_function_with_funcref()
let s:f = function('type')
let s:fref = function(s:f)
@@ -569,6 +528,7 @@ func Test_setmatches()
endif
eval set->setmatches()
call assert_equal(exp, getmatches())
+ call assert_fails('let m = setmatches([], [])', 'E745:')
endfunc
func Test_empty_concatenate()
diff --git a/src/nvim/testdir/test_fixeol.vim b/src/nvim/testdir/test_fixeol.vim
index 9d6c900bdb..41d47d6a06 100644
--- a/src/nvim/testdir/test_fixeol.vim
+++ b/src/nvim/testdir/test_fixeol.vim
@@ -48,4 +48,71 @@ func Test_fixeol()
enew!
endfunc
+func Test_eof()
+ let data = 0z68656c6c6f.0d0a.776f726c64 " "hello\r\nworld"
+
+ " 1. Eol, Eof
+ " read
+ call writefile(data + 0z0d0a.1a, 'XXEolEof')
+ e! XXEolEof
+ call assert_equal(['hello', 'world'], getline(1, 2))
+ call assert_equal([1, 1], [&eol, &eof])
+ " write
+ set fixeol
+ w!
+ call assert_equal(data + 0z0d0a, readblob('XXEolEof'))
+ set nofixeol
+ w!
+ call assert_equal(data + 0z0d0a.1a, readblob('XXEolEof'))
+
+ " 2. NoEol, Eof
+ " read
+ call writefile(data + 0z1a, 'XXNoEolEof')
+ e! XXNoEolEof
+ call assert_equal(['hello', 'world'], getline(1, 2))
+ call assert_equal([0, 1], [&eol, &eof])
+ " write
+ set fixeol
+ w!
+ call assert_equal(data + 0z0d0a, readblob('XXNoEolEof'))
+ set nofixeol
+ w!
+ call assert_equal(data + 0z1a, readblob('XXNoEolEof'))
+
+ " 3. Eol, NoEof
+ " read
+ call writefile(data + 0z0d0a, 'XXEolNoEof')
+ e! XXEolNoEof
+ call assert_equal(['hello', 'world'], getline(1, 2))
+ call assert_equal([1, 0], [&eol, &eof])
+ " write
+ set fixeol
+ w!
+ call assert_equal(data + 0z0d0a, readblob('XXEolNoEof'))
+ set nofixeol
+ w!
+ call assert_equal(data + 0z0d0a, readblob('XXEolNoEof'))
+
+ " 4. NoEol, NoEof
+ " read
+ call writefile(data, 'XXNoEolNoEof')
+ e! XXNoEolNoEof
+ call assert_equal(['hello', 'world'], getline(1, 2))
+ call assert_equal([0, 0], [&eol, &eof])
+ " write
+ set fixeol
+ w!
+ call assert_equal(data + 0z0d0a, readblob('XXNoEolNoEof'))
+ set nofixeol
+ w!
+ call assert_equal(data, readblob('XXNoEolNoEof'))
+
+ call delete('XXEolEof')
+ call delete('XXNoEolEof')
+ call delete('XXEolNoEof')
+ call delete('XXNoEolNoEof')
+ set ff& fixeol& eof& eol&
+ enew!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index fa79aaf6d7..d2603809b9 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -19,6 +19,27 @@ func Test_00_bufexists()
call assert_equal(0, bufexists('Xfoo'))
endfunc
+func Test_has()
+ throw 'Skipped: Nvim has removed some features'
+ call assert_equal(1, has('eval'))
+ call assert_equal(1, has('eval', 1))
+
+ if has('unix')
+ call assert_equal(1, or(has('ttyin'), 1))
+ call assert_equal(0, and(has('ttyout'), 0))
+ call assert_equal(1, has('multi_byte_encoding'))
+ endif
+ call assert_equal(1, has('vcon', 1))
+ call assert_equal(1, has('mouse_gpm_enabled', 1))
+
+ call assert_equal(0, has('nonexistent'))
+ call assert_equal(0, has('nonexistent', 1))
+
+ " Will we ever have patch 9999?
+ let ver = 'patch-' .. v:version / 100 .. '.' .. v:version % 100 .. '.9999'
+ call assert_equal(0, has(ver))
+endfunc
+
func Test_empty()
call assert_equal(1, empty(''))
call assert_equal(0, empty('a'))
@@ -190,7 +211,7 @@ func Test_str2nr()
if has('float')
call assert_fails('call str2nr(1.2)', 'E806:')
endif
- call assert_fails('call str2nr(10, [])', 'E474:')
+ call assert_fails('call str2nr(10, [])', 'E745:')
endfunc
func Test_strftime()
@@ -383,6 +404,8 @@ func Test_strpart()
call assert_equal('abcdefg', 'abcdefg'->strpart(-2))
call assert_equal('fg', strpart('abcdefg', 5, 4))
call assert_equal('defg', strpart('abcdefg', 3))
+ call assert_equal('', strpart('abcdefg', 10))
+ call assert_fails("let s=strpart('abcdef', [])", 'E745:')
call assert_equal('lép', strpart('éléphant', 2, 4))
call assert_equal('léphant', strpart('éléphant', 2))
@@ -552,6 +575,15 @@ endfunc
func Test_tr()
call assert_equal('foo', tr('bar', 'bar', 'foo'))
call assert_equal('zxy', 'cab'->tr('abc', 'xyz'))
+ call assert_fails("let s=tr([], 'abc', 'def')", 'E730:')
+ call assert_fails("let s=tr('abc', [], 'def')", 'E730:')
+ call assert_fails("let s=tr('abc', 'abc', [])", 'E730:')
+ call assert_fails("let s=tr('abcd', 'abcd', 'def')", 'E475:')
+ " set encoding=latin1
+ call assert_fails("let s=tr('abcd', 'abcd', 'def')", 'E475:')
+ call assert_equal('hEllO', tr('hello', 'eo', 'EO'))
+ call assert_equal('hello', tr('hello', 'xy', 'ab'))
+ set encoding=utf8
endfunc
" Tests for the mode() function
@@ -766,18 +798,41 @@ func Test_mode()
delfunction OperatorFunc
endfunc
+" Test for append()
func Test_append()
enew!
split
call append(0, ["foo"])
+ call append(1, [])
+ call append(1, v:_null_list)
+ call assert_equal(['foo', ''], getline(1, '$'))
split
only
undo
+ undo
" Using $ instead of '$' must give an error
call assert_fails("call append($, 'foobar')", 'E116:')
endfunc
+" Test for setline()
+func Test_setline()
+ new
+ call setline(0, ["foo"])
+ call setline(0, [])
+ call setline(0, v:_null_list)
+ call setline(1, ["bar"])
+ call setline(1, [])
+ call setline(1, v:_null_list)
+ call setline(2, [])
+ call setline(2, v:_null_list)
+ call setline(3, [])
+ call setline(3, v:_null_list)
+ call setline(2, ["baz"])
+ call assert_equal(['bar', 'baz'], getline(1, '$'))
+ close!
+endfunc
+
func Test_getbufvar()
let bnr = bufnr('%')
let b:var_num = '1234'
@@ -851,6 +906,8 @@ func Test_stridx()
call assert_equal(-1, stridx('hello', 'l', 10))
call assert_equal(2, stridx('hello', 'll'))
call assert_equal(-1, stridx('hello', 'hello world'))
+ call assert_fails("let n=stridx('hello', [])", 'E730:')
+ call assert_fails("let n=stridx([], 'l')", 'E730:')
endfunc
func Test_strridx()
@@ -867,6 +924,8 @@ func Test_strridx()
call assert_equal(-1, strridx('hello', 'l', -1))
call assert_equal(2, strridx('hello', 'll'))
call assert_equal(-1, strridx('hello', 'hello world'))
+ call assert_fails("let n=strridx('hello', [])", 'E730:')
+ call assert_fails("let n=strridx([], 'l')", 'E730:')
endfunc
func Test_match_func()
@@ -876,6 +935,12 @@ func Test_match_func()
call assert_equal(-1, match('testing', 'ing', 8))
call assert_equal(1, match(['vim', 'testing', 'execute'], 'ing'))
call assert_equal(-1, match(['vim', 'testing', 'execute'], 'img'))
+ call assert_fails("let x=match('vim', [])", 'E730:')
+ call assert_equal(3, match(['a', 'b', 'c', 'a'], 'a', 1))
+ call assert_equal(-1, match(['a', 'b', 'c', 'a'], 'a', 5))
+ call assert_equal(4, match('testing', 'ing', -1))
+ call assert_fails("let x=match('testing', 'ing', 0, [])", 'E745:')
+ call assert_equal(-1, match(v:_null_list, 2))
endfunc
func Test_matchend()
@@ -982,6 +1047,7 @@ func Test_byte2line_line2byte()
bw!
endfunc
+" Test for byteidx() and byteidxcomp() functions
func Test_byteidx()
let a = '.é.' " one char of two bytes
call assert_equal(0, byteidx(a, 0))
@@ -1001,6 +1067,7 @@ func Test_byteidx()
call assert_equal(4, b->byteidx(2))
call assert_equal(5, b->byteidx(3))
call assert_equal(-1, b->byteidx(4))
+ call assert_fails("call byteidx([], 0)", 'E730:')
call assert_equal(0, b->byteidxcomp(0))
call assert_equal(1, b->byteidxcomp(1))
@@ -1008,6 +1075,7 @@ func Test_byteidx()
call assert_equal(4, b->byteidxcomp(3))
call assert_equal(5, b->byteidxcomp(4))
call assert_equal(-1, b->byteidxcomp(5))
+ call assert_fails("call byteidxcomp([], 0)", 'E730:')
endfunc
" Test for charidx()
@@ -1033,8 +1101,8 @@ func Test_charidx()
call assert_fails('let x = charidx([], 1)', 'E474:')
call assert_fails('let x = charidx("abc", [])', 'E474:')
call assert_fails('let x = charidx("abc", 1, [])', 'E474:')
- call assert_fails('let x = charidx("abc", 1, -1)', 'E474:')
- call assert_fails('let x = charidx("abc", 1, 2)', 'E474:')
+ call assert_fails('let x = charidx("abc", 1, -1)', 'E1023:')
+ call assert_fails('let x = charidx("abc", 1, 2)', 'E1023:')
endfunc
func Test_count()
@@ -1230,6 +1298,22 @@ func Test_col()
xunmap <F2>
delfunc T
+ " Test for the visual line start and end marks '< and '>
+ call setline(1, ['one', 'one two', 'one two three'])
+ "normal! ggVG
+ call feedkeys("ggVG\<Esc>", 'xt')
+ call assert_equal(1, col("'<"))
+ call assert_equal(14, col("'>"))
+ " Delete the last line of the visually selected region
+ $d
+ call assert_notequal(14, col("'>"))
+
+ " Test with 'virtualedit'
+ set virtualedit=all
+ call cursor(1, 10)
+ call assert_equal(4, col('.'))
+ set virtualedit&
+
bw!
endfunc
@@ -1273,12 +1357,15 @@ endfunc
" Test for the inputdialog() function
func Test_inputdialog()
- CheckNotGui
-
- call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<CR>", 'xt')
- call assert_equal('xx', v)
- call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<Esc>", 'xt')
- call assert_equal('yy', v)
+ if has('gui_running')
+ call assert_fails('let v=inputdialog([], "xx")', 'E730:')
+ call assert_fails('let v=inputdialog("Q", [])', 'E730:')
+ else
+ call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<CR>", 'xt')
+ call assert_equal('xx', v)
+ call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<Esc>", 'xt')
+ call assert_equal('yy', v)
+ endif
endfunc
" Test for inputlist()
@@ -1329,6 +1416,7 @@ func Test_balloon_show()
call balloon_show('hi!')
if !has('gui_running')
call balloon_show(range(3))
+ call balloon_show([])
endif
endfunc
@@ -1640,11 +1728,11 @@ func Test_libcall_libcallnr()
call assert_equal(4, 'abcd'->libcallnr(libc, 'strlen'))
call assert_equal(char2nr('A'), char2nr('a')->libcallnr(libc, 'toupper'))
- call assert_fails("call libcall(libc, 'Xdoesnotexist_', '')", 'E364:')
- call assert_fails("call libcallnr(libc, 'Xdoesnotexist_', '')", 'E364:')
+ call assert_fails("call libcall(libc, 'Xdoesnotexist_', '')", ['', 'E364:'])
+ call assert_fails("call libcallnr(libc, 'Xdoesnotexist_', '')", ['', 'E364:'])
- call assert_fails("call libcall('Xdoesnotexist_', 'getenv', 'HOME')", 'E364:')
- call assert_fails("call libcallnr('Xdoesnotexist_', 'strlen', 'abcd')", 'E364:')
+ call assert_fails("call libcall('Xdoesnotexist_', 'getenv', 'HOME')", ['', 'E364:'])
+ call assert_fails("call libcallnr('Xdoesnotexist_', 'strlen', 'abcd')", ['', 'E364:'])
endfunc
sandbox function Fsandbox()
@@ -1763,7 +1851,7 @@ func Test_confirm()
call assert_equal(2, a)
" confirm() should return 0 when pressing CTRL-C.
- call feedkeys("\<C-c>", 'L')
+ call feedkeys("\<C-C>", 'L')
let a = confirm('Are you sure?', "&Yes\n&No")
call assert_equal(0, a)
@@ -1858,6 +1946,7 @@ func Test_call()
call assert_equal(3, 'len'->call([123]))
call assert_fails("call call('len', 123)", 'E714:')
call assert_equal(0, call('', []))
+ call assert_equal(0, call('len', v:_null_list))
function Mylen() dict
return len(self.data)
@@ -1871,6 +1960,9 @@ endfunc
func Test_char2nr()
call assert_equal(12354, char2nr('あ', 1))
call assert_equal(120, 'x'->char2nr())
+ " set encoding=latin1
+ call assert_equal(120, 'x'->char2nr())
+ set encoding=utf-8
endfunc
func Test_charclass()
@@ -2043,6 +2135,7 @@ func Test_range()
" index()
call assert_equal(1, index(range(1, 5), 2))
+ call assert_fails("echo index([1, 2], 1, [])", 'E745:')
" inputlist()
call feedkeys(":let result = inputlist(range(10))\<CR>1\<CR>", 'x')
@@ -2204,6 +2297,14 @@ func Test_range()
" uniq()
call assert_equal([0, 1, 2, 3, 4], uniq(range(5)))
+
+ " errors
+ call assert_fails('let x=range(2, 8, 0)', 'E726:')
+ call assert_fails('let x=range(3, 1)', 'E727:')
+ call assert_fails('let x=range(1, 3, -2)', 'E727:')
+ call assert_fails('let x=range([])', 'E745:')
+ call assert_fails('let x=range(1, [])', 'E745:')
+ call assert_fails('let x=range(1, 4, [])', 'E745:')
endfunc
func Test_garbagecollect_now_fails()
@@ -2255,6 +2356,13 @@ func Test_nr2char()
call assert_equal("\x80\xfc\b" .. nr2char(0x40000000), eval('"\<M-' .. nr2char(0x40000000) .. '>"'))
endfunc
+" Test for screenattr(), screenchar() and screenchars() functions
+func Test_screen_functions()
+ call assert_equal(-1, screenattr(-1, -1))
+ call assert_equal(-1, screenchar(-1, -1))
+ call assert_equal([], screenchars(-1, -1))
+endfunc
+
" Test for getcurpos() and setpos()
func Test_getcurpos_setpos()
new
diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim
index cb6851250c..bbfe374f51 100644
--- a/src/nvim/testdir/test_global.vim
+++ b/src/nvim/testdir/test_global.vim
@@ -39,7 +39,7 @@ endfunc
func Test_global_error()
call assert_fails('g\\a', 'E10:')
call assert_fails('g', 'E148:')
- call assert_fails('g/\(/y', 'E476:')
+ call assert_fails('g/\(/y', 'E54:')
endfunc
" Test for printing lines using :g with different search patterns
diff --git a/src/nvim/testdir/test_increment.vim b/src/nvim/testdir/test_increment.vim
index 2559654f25..3c2b88ef9f 100644
--- a/src/nvim/testdir/test_increment.vim
+++ b/src/nvim/testdir/test_increment.vim
@@ -476,6 +476,10 @@ func Test_visual_increment_20()
exec "norm! \<C-A>"
call assert_equal(["b"], getline(1, '$'))
call assert_equal([0, 1, 1, 0], getpos('.'))
+ " decrement a and A and increment z and Z
+ call setline(1, ['a', 'A', 'z', 'Z'])
+ exe "normal 1G\<C-X>2G\<C-X>3G\<C-A>4G\<C-A>"
+ call assert_equal(['a', 'A', 'z', 'Z'], getline(1, '$'))
endfunc
" 21) block-wise increment on part of hexadecimal
@@ -566,12 +570,14 @@ endfunc
" 1) <ctrl-a>
" 0b11111111111111111111111111111111
func Test_visual_increment_26()
- set nrformats+=alpha
+ set nrformats+=bin
call setline(1, ["0b11111111111111111111111111111110"])
exec "norm! \<C-V>$\<C-A>"
call assert_equal(["0b11111111111111111111111111111111"], getline(1, '$'))
call assert_equal([0, 1, 1, 0], getpos('.'))
- set nrformats-=alpha
+ exec "norm! \<C-V>$\<C-X>"
+ call assert_equal(["0b11111111111111111111111111111110"], getline(1, '$'))
+ set nrformats-=bin
endfunc
" 27) increment with 'rightreft', if supported
@@ -772,7 +778,6 @@ func Test_normal_increment_03()
endfunc
func Test_increment_empty_line()
- new
call setline(1, ['0', '0', '0', '0', '0', '0', ''])
exe "normal Gvgg\<C-A>"
call assert_equal(['1', '1', '1', '1', '1', '1', ''], getline(1, 7))
@@ -783,8 +788,13 @@ func Test_increment_empty_line()
exe "normal! c\<C-A>l"
exe "normal! c\<C-X>l"
call assert_equal('one two', getline(1))
+endfunc
- bwipe!
+" Try incrementing/decrementing a non-digit/alpha character
+func Test_increment_special_char()
+ call setline(1, '!')
+ call assert_beeps("normal \<C-A>")
+ call assert_beeps("normal \<C-X>")
endfunc
" Try incrementing/decrementing a number when nrformats contains unsigned
@@ -867,4 +877,21 @@ func Test_normal_increment_with_virtualedit()
set virtualedit&
endfunc
+" Test for incrementing a signed hexadecimal and octal number
+func Test_normal_increment_signed_hexoct_nr()
+ new
+ " negative sign before a hex number should be ignored
+ call setline(1, ["-0x9"])
+ exe "norm \<C-A>"
+ call assert_equal(["-0xa"], getline(1, '$'))
+ exe "norm \<C-X>"
+ call assert_equal(["-0x9"], getline(1, '$'))
+ call setline(1, ["-007"])
+ exe "norm \<C-A>"
+ call assert_equal(["-010"], getline(1, '$'))
+ exe "norm \<C-X>"
+ call assert_equal(["-007"], getline(1, '$'))
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim
index 937076aa2a..f05e06f774 100644
--- a/src/nvim/testdir/test_let.vim
+++ b/src/nvim/testdir/test_let.vim
@@ -262,7 +262,7 @@ func Test_let_errors()
let l = [1, 2, 3]
call assert_fails('let l[:] = 5', 'E709:')
- call assert_fails('let x:lnum=5', 'E488:')
+ call assert_fails('let x:lnum=5', ['E121:', 'E488:'])
call assert_fails('let v:=5', 'E461:')
call assert_fails('let [a]', 'E474:')
call assert_fails('let [a, b] = [', 'E697:')
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index f7261b2055..ecf95ba8c0 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -336,11 +336,12 @@ func Test_dict_deepcopy()
let l = [4, d, 6]
let d[3] = l
let dc = deepcopy(d)
- call assert_fails('call deepcopy(d, 1)', 'E698')
+ call assert_fails('call deepcopy(d, 1)', 'E698:')
let l2 = [0, l, l, 3]
let l[1] = l2
let l3 = deepcopy(l2)
call assert_true(l3[1] is l3[2])
+ call assert_fails("call deepcopy([1, 2], 2)", 'E1023:')
endfunc
" Locked variables
@@ -420,6 +421,11 @@ func Test_list_locked_var()
call assert_equal(expected[depth][u][1], ps)
endfor
endfor
+ call assert_fails("let x=islocked('a b')", 'E488:')
+ let mylist = [1, 2, 3]
+ call assert_fails("let x = islocked('mylist[1:2]')", 'E786:')
+ let mydict = {'k' : 'v'}
+ call assert_fails("let x = islocked('mydict.a')", 'E716:')
endfunc
" Unletting locked variables
@@ -674,10 +680,10 @@ func Test_reverse_sort_uniq()
endif
call assert_fails('call reverse("")', 'E899:')
- call assert_fails('call uniq([1, 2], {x, y -> []})', 'E882:')
+ call assert_fails('call uniq([1, 2], {x, y -> []})', 'E745:')
call assert_fails("call sort([1, 2], function('min'), 1)", "E715:")
call assert_fails("call sort([1, 2], function('invalid_func'))", "E700:")
- call assert_fails("call sort([1, 2], function('min'))", "E702:")
+ call assert_fails("call sort([1, 2], function('min'))", "E118:")
endfunc
" reduce a list or a blob
@@ -736,6 +742,8 @@ func Test_str_split()
call assert_equal(['aa', '', 'bb', 'cc', ''], split('aa,,bb, cc,', ',\s*', 1))
call assert_equal(['a', 'b', 'c'], split('abc', '\zs'))
call assert_equal(['', 'a', '', 'b', '', 'c', ''], split('abc', '\zs', 1))
+ call assert_fails("call split('abc', [])", 'E730:')
+ call assert_fails("call split('abc', 'b', [])", 'E745:')
endfunc
" compare recursively linked list and dict
@@ -975,7 +983,7 @@ func Test_listdict_index()
call assert_fails('echo d[1:2]', 'E719:')
call assert_fails("let v = [4, 6][{-> 1}]", 'E729:')
call assert_fails("let v = range(5)[2:[]]", 'E730:')
- call assert_fails("let v = range(5)[2:{-> 2}(]", 'E116:')
+ call assert_fails("let v = range(5)[2:{-> 2}(]", ['E15:', 'E116:'])
call assert_fails("let v = range(5)[2:3", 'E111:')
call assert_fails("let l = insert([1,2,3], 4, 10)", 'E684:')
call assert_fails("let l = insert([1,2,3], 4, -10)", 'E684:')
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index 2d8c45210b..560883ba5d 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -1050,6 +1050,30 @@ func Test_mouse_drag_mapped_start_select()
set mouse&
endfunc
+func Test_mouse_drag_statusline()
+ set laststatus=2
+ set mouse=a
+ func ClickExpr()
+ call Ntest_setmouse(&lines - 1, 1)
+ return "\<LeftMouse>"
+ endfunc
+ func DragExpr()
+ call Ntest_setmouse(&lines - 2, 1)
+ return "\<LeftDrag>"
+ endfunc
+ nnoremap <expr> <F2> ClickExpr()
+ nnoremap <expr> <F3> DragExpr()
+
+ " this was causing a crash in win_drag_status_line()
+ call feedkeys("\<F2>:tabnew\<CR>\<F3>", 'tx')
+
+ nunmap <F2>
+ nunmap <F3>
+ delfunc ClickExpr
+ delfunc DragExpr
+ set laststatus& mouse&
+endfunc
+
" Test for mapping <LeftDrag> in Insert mode
func Test_mouse_drag_insert_map()
set mouse=a
diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim
index 054ebf1218..b432b7bbbc 100644
--- a/src/nvim/testdir/test_marks.vim
+++ b/src/nvim/testdir/test_marks.vim
@@ -100,6 +100,8 @@ func Test_setpos()
call setpos('.', [0, 1, -1, 0])
call assert_equal([2, 2], [line('.'), col('.')])
+ call assert_fails("call setpos('ab', [0, 1, 1, 0])", 'E474:')
+
bwipe!
call win_gotoid(twowin)
bwipe!
diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim
index fe931fefb2..5d9be99444 100644
--- a/src/nvim/testdir/test_match.vim
+++ b/src/nvim/testdir/test_match.vim
@@ -160,12 +160,14 @@ endfunc
func Test_matchadd_error()
call clearmatches()
" Nvim: not an error anymore:
+ " call assert_fails("call matchadd('GroupDoesNotExist', 'X')", 'E28:')
call matchadd('GroupDoesNotExist', 'X')
call assert_equal([{'group': 'GroupDoesNotExist', 'pattern': 'X', 'priority': 10, 'id': 1206}], getmatches())
- call assert_fails("call matchadd('Search', '\\(')", 'E475:')
+ call assert_fails("call matchadd('Search', '\\(')", 'E54:')
call assert_fails("call matchadd('Search', 'XXX', 1, 123, 1)", 'E715:')
call assert_fails("call matchadd('Error', 'XXX', 1, 3)", 'E798:')
call assert_fails("call matchadd('Error', 'XXX', 1, 0)", 'E799:')
+ call assert_fails("call matchadd('Error', 'XXX', [], 0)", 'E745:')
endfunc
func Test_matchaddpos()
@@ -305,7 +307,10 @@ func Test_matchaddpos_error()
call assert_fails("call matchaddpos('Error', [1], 1, 123, 1)", 'E715:')
call assert_fails("call matchaddpos('Error', [1], 1, 5, {'window':12345})", 'E957:')
" Why doesn't the following error have an error code E...?
+ " call assert_fails("call matchaddpos('Error', [{}])", 'E290:')
call assert_fails("call matchaddpos('Error', [{}])", 'E5031:')
+ call assert_equal(-1, matchaddpos('Error', v:_null_list))
+ call assert_fails("call matchaddpos('Error', [1], [], 1)", 'E745:')
endfunc
func OtherWindowCommon()
@@ -338,9 +343,7 @@ func Test_matchdelete_error()
endfunc
func Test_matchclear_other_window()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckRunVimInTerminal
let buf = OtherWindowCommon()
call term_sendkeys(buf, ":call clearmatches(winid)\<CR>")
call VerifyScreenDump(buf, 'Test_matchclear_1', {})
@@ -350,9 +353,7 @@ func Test_matchclear_other_window()
endfunc
func Test_matchadd_other_window()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckRunVimInTerminal
let buf = OtherWindowCommon()
call term_sendkeys(buf, ":call matchadd('Search', 'Hello', 1, -1, #{window: winid})\<CR>")
call term_sendkeys(buf, ":\<CR>")
@@ -362,5 +363,4 @@ func Test_matchadd_other_window()
call delete('XscriptMatchCommon')
endfunc
-
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_matchfuzzy.vim b/src/nvim/testdir/test_matchfuzzy.vim
index c836bc87aa..b46550fbc3 100644
--- a/src/nvim/testdir/test_matchfuzzy.vim
+++ b/src/nvim/testdir/test_matchfuzzy.vim
@@ -6,9 +6,7 @@ source check.vim
" Test for matchfuzzy()
func Test_matchfuzzy()
call assert_fails('call matchfuzzy(10, "abc")', 'E686:')
- " Needs v8.2.1183; match the final error that's thrown for now
- " call assert_fails('call matchfuzzy(["abc"], [])', 'E730:')
- call assert_fails('call matchfuzzy(["abc"], [])', 'E475:')
+ call assert_fails('call matchfuzzy(["abc"], [])', 'E730:')
call assert_fails("let x = matchfuzzy(v:_null_list, 'foo')", 'E686:')
call assert_fails('call matchfuzzy(["abc"], v:_null_string)', 'E475:')
call assert_equal([], matchfuzzy([], 'abc'))
@@ -75,12 +73,9 @@ func Test_matchfuzzy()
call assert_fails("let x = matchfuzzy(l, 'day', {'text_cb' : {a, b -> 1}})", 'E119:')
call assert_equal([], matchfuzzy(l, 'cam'))
" Nvim's callback implementation is different, so E6000 is expected instead,
- " but we need v8.2.1183 to assert it
" call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E921:')
- " call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E6000:')
- call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E475:')
- " call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E730:')
- call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E475:')
+ call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E6000:')
+ call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E730:')
call assert_fails("let x = matchfuzzy(l, 'cam', v:_null_dict)", 'E715:')
call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : v:_null_string})", 'E475:')
" Nvim doesn't have null functions
@@ -155,12 +150,9 @@ func Test_matchfuzzypos()
call assert_fails("let x = matchfuzzypos(l, 'day', {'text_cb' : {a, b -> 1}})", 'E119:')
call assert_equal([[], [], []], matchfuzzypos(l, 'cam'))
" Nvim's callback implementation is different, so E6000 is expected instead,
- " but we need v8.2.1183 to assert it
" call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E921:')
- " call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E6000:')
- call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E475:')
- " call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E730:')
- call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E475:')
+ call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E6000:')
+ call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E730:')
call assert_fails("let x = matchfuzzypos(l, 'cam', v:_null_dict)", 'E715:')
call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : v:_null_string})", 'E475:')
" Nvim doesn't have null functions
diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim
index 2e149ad5a5..a1121632e6 100644
--- a/src/nvim/testdir/test_menu.vim
+++ b/src/nvim/testdir/test_menu.vim
@@ -153,7 +153,7 @@ func Test_menu_errors()
call assert_fails('menu Test.Foo.Bar', 'E327:')
call assert_fails('cmenu Test.Foo', 'E328:')
call assert_fails('emenu x Test.Foo', 'E475:')
- call assert_fails('emenu Test.Foo.Bar', 'E334:')
+ call assert_fails('emenu Test.Foo.Bar', 'E327:')
call assert_fails('menutranslate Test', 'E474:')
silent! unmenu Foo
@@ -252,6 +252,7 @@ func Test_menu_info()
nmenu Test.abc <Nop>
call assert_equal('<Nop>', menu_info('Test.abc').rhs)
call assert_fails('call menu_info([])', 'E730:')
+ call assert_fails('call menu_info("", [])', 'E730:')
nunmenu Test
" Test for defining menus in different modes
diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim
index e035b3ef50..057f4a1bea 100644
--- a/src/nvim/testdir/test_method.vim
+++ b/src/nvim/testdir/test_method.vim
@@ -131,9 +131,9 @@ func Test_method_syntax()
eval [1, 2, 3]
\ ->sort(
\ )
- call assert_fails('eval [1, 2, 3]-> sort()', 'E260:')
+ call assert_fails('eval [1, 2, 3]-> sort()', 'E15:')
call assert_fails('eval [1, 2, 3]->sort ()', 'E274:')
- call assert_fails('eval [1, 2, 3]-> sort ()', 'E260:')
+ call assert_fails('eval [1, 2, 3]-> sort ()', 'E15:')
endfunc
func Test_method_lambda()
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 5730085c78..84929e2be3 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -120,6 +120,39 @@ func Test_normal01_keymodel()
call feedkeys("Vkk\<Up>yy", 'tx')
call assert_equal(['47', '48', '49', '50'], getreg(0, 0, 1))
+ " Test for using special keys to start visual selection
+ %d
+ call setline(1, ['red fox tail', 'red fox tail', 'red fox tail'])
+ set keymodel=startsel
+ " Test for <S-PageUp> and <S-PageDown>
+ call cursor(1, 1)
+ call feedkeys("\<S-PageDown>y", 'xt')
+ call assert_equal([0, 1, 1, 0], getpos("'<"))
+ call assert_equal([0, 3, 1, 0], getpos("'>"))
+ call feedkeys("Gz\<CR>8|\<S-PageUp>y", 'xt')
+ call assert_equal([0, 2, 1, 0], getpos("'<"))
+ call assert_equal([0, 3, 8, 0], getpos("'>"))
+ " Test for <S-C-Home> and <S-C-End>
+ call cursor(2, 12)
+ call feedkeys("\<S-C-Home>y", 'xt')
+ call assert_equal([0, 1, 1, 0], getpos("'<"))
+ call assert_equal([0, 2, 12, 0], getpos("'>"))
+ call cursor(1, 4)
+ call feedkeys("\<S-C-End>y", 'xt')
+ call assert_equal([0, 1, 4, 0], getpos("'<"))
+ call assert_equal([0, 3, 13, 0], getpos("'>"))
+ " Test for <S-C-Left> and <S-C-Right>
+ call cursor(2, 5)
+ call feedkeys("\<S-C-Right>y", 'xt')
+ call assert_equal([0, 2, 5, 0], getpos("'<"))
+ call assert_equal([0, 2, 9, 0], getpos("'>"))
+ call cursor(2, 9)
+ call feedkeys("\<S-C-Left>y", 'xt')
+ call assert_equal([0, 2, 5, 0], getpos("'<"))
+ call assert_equal([0, 2, 9, 0], getpos("'>"))
+
+ set keymodel&
+
" clean up
bw!
endfunc
@@ -140,16 +173,15 @@ func Test_normal03_join()
$
:j 10
call assert_equal('100', getline('.'))
+ call assert_beeps('normal GVJ')
" clean up
bw!
endfunc
+" basic filter test
func Test_normal04_filter()
- " basic filter test
" only test on non windows platform
- if has('win32')
- return
- endif
+ CheckNotMSWindows
call Setup_NewWindow()
1
call feedkeys("!!sed -e 's/^/| /'\n", 'tx')
@@ -222,12 +254,10 @@ func Test_normal_formatexpr_returns_nonzero()
close!
endfunc
+" basic test for formatprg
func Test_normal06_formatprg()
- " basic test for formatprg
" only test on non windows platform
- if has('win32')
- return
- endif
+ CheckNotMSWindows
" uses sed to number non-empty lines
call writefile(['#!/bin/sh', 'sed ''/./=''|sed ''/./{', 'N', 's/\n/ /', '}'''], 'Xsed_format.sh')
@@ -240,16 +270,24 @@ func Test_normal06_formatprg()
set formatprg=./Xsed_format.sh
norm! gggqG
call assert_equal(expected, getline(1, '$'))
- bw!
+ %d
- 10new
call setline(1, text)
set formatprg=donothing
setlocal formatprg=./Xsed_format.sh
norm! gggqG
call assert_equal(expected, getline(1, '$'))
- bw!
+ %d
+ " Check for the command-line ranges added to 'formatprg'
+ set formatprg=cat
+ call setline(1, ['one', 'two', 'three', 'four', 'five'])
+ call feedkeys('gggqG', 'xt')
+ call assert_equal('.,$!cat', @:)
+ call feedkeys('2Ggq2j', 'xt')
+ call assert_equal('.,.+2!cat', @:)
+
+ bw!
" clean up
set formatprg=
setlocal formatprg=
@@ -263,18 +301,16 @@ func Test_normal07_internalfmt()
10new
call setline(1, list)
set tw=12
- norm! gggqG
+ norm! ggVGgq
call assert_equal(['1 2 3', '4 5 6', '7 8 9', '10 11 '], getline(1, '$'))
" clean up
set tw=0
bw!
endfunc
+" basic tests for foldopen/folddelete
func Test_normal08_fold()
- " basic tests for foldopen/folddelete
- if !has("folding")
- return
- endif
+ CheckFeature folding
call Setup_NewWindow()
50
setl foldenable fdm=marker
@@ -506,6 +542,14 @@ func Test_normal10_expand()
call assert_equal(expected[i], expand('<cexpr>'), 'i == ' . i)
endfor
+ " Test for <cexpr> in state.val and ptr->val
+ call setline(1, 'x = state.val;')
+ call cursor(1, 10)
+ call assert_equal('state.val', expand('<cexpr>'))
+ call setline(1, 'x = ptr->val;')
+ call cursor(1, 9)
+ call assert_equal('ptr->val', expand('<cexpr>'))
+
if executable('echo')
" Test expand(`...`) i.e. backticks command expansion.
" MS-Windows has a trailing space.
@@ -520,6 +564,19 @@ func Test_normal10_expand()
bw!
endfunc
+" Test for expand() in latin1 encoding
+func Test_normal_expand_latin1()
+ new
+ let save_enc = &encoding
+ " set encoding=latin1
+ call setline(1, 'val = item->color;')
+ call cursor(1, 11)
+ call assert_equal('color', expand("<cword>"))
+ call assert_equal('item->color', expand("<cexpr>"))
+ let &encoding = save_enc
+ bw!
+endfunc
+
func Test_normal11_showcmd()
" test for 'showcmd'
10new
@@ -544,6 +601,13 @@ func Test_normal11_showcmd()
redraw!
call assert_match('1-3$', Screenline(&lines))
call feedkeys("v", 'xt')
+ " test for visually selecting the end of line
+ call setline(1, ["foobar"])
+ call feedkeys("$vl", 'xt')
+ redraw!
+ call assert_match('2$', Screenline(&lines))
+ call feedkeys("y", 'xt')
+ call assert_equal("r\n", @")
bw!
endfunc
@@ -1432,10 +1496,8 @@ func Test_normal18_z_fold()
endfunc
func Test_normal20_exmode()
- if !has("unix")
- " Reading from redirected file doesn't work on MS-Windows
- return
- endif
+ " Reading from redirected file doesn't work on MS-Windows
+ CheckNotMSWindows
call writefile(['1a', 'foo', 'bar', '.', 'w! Xfile2', 'q!'], 'Xscript')
call writefile(['1', '2'], 'Xfile')
call system(GetVimCommand() .. ' -e -s < Xscript Xfile')
@@ -1588,7 +1650,7 @@ func Test_normal23_K()
call setline(1, '---')
call assert_fails('normal! ggv2lK', 'E349:')
call setline(1, ['abc', 'xyz'])
- call assert_fails("normal! gg2lv2h\<C-]>", 'E426:')
+ call assert_fails("normal! gg2lv2h\<C-]>", 'E433:')
call assert_beeps("normal! ggVjK")
" clean up
@@ -2063,6 +2125,16 @@ func Test_normal30_changecase()
call assert_equal(['aaaaaa', 'AAAAaa'], getline(1, 2))
set whichwrap&
+ " try changing the case with a double byte encoding (DBCS)
+ %bw!
+ let enc = &enc
+ " set encoding=cp932
+ call setline(1, "\u8470")
+ normal ~
+ normal gU$gu$gUgUg~g~gugu
+ call assert_equal("\u8470", getline(1))
+ let &encoding = enc
+
" clean up
bw!
endfunc
@@ -2154,6 +2226,19 @@ func Test_normal31_r_cmd()
" r command should fail in operator pending mode
call assert_beeps('normal! cr')
+ " replace a tab character in visual mode
+ %d
+ call setline(1, ["a\tb", "c\td", "e\tf"])
+ normal gglvjjrx
+ call assert_equal(['axx', 'xxx', 'xxf'], getline(1, '$'))
+
+ " replace with a multibyte character (with multiple composing characters)
+ %d
+ new
+ call setline(1, 'aaa')
+ exe "normal $ra\u0328\u0301"
+ call assert_equal("aaa\u0328\u0301", getline(1))
+
" clean up
set noautoindent
bw!
@@ -2178,9 +2263,7 @@ endfunc
" Test for g`, g;, g,, g&, gv, gk, gj, gJ, g0, g^, g_, gm, g$, gM, g CTRL-G,
" gi and gI commands
func Test_normal33_g_cmd2()
- if !has("jumplist")
- return
- endif
+ CheckFeature jumplist
call Setup_NewWindow()
" Test for g`
clearjumps
@@ -2639,7 +2722,6 @@ endfunc
" Test for cw cW ce
func Test_normal39_cw()
" Test for cw and cW on whitespace
- " and cpo+=w setting
new
set tw=0
call append(0, 'here are some words')
@@ -2647,14 +2729,6 @@ func Test_normal39_cw()
call assert_equal('hereZZZare some words', getline('.'))
norm! 1gg0elcWYYY
call assert_equal('hereZZZareYYYsome words', getline('.'))
- " Nvim: no "w" flag in 'cpoptions'.
- " set cpo+=w
- " call setline(1, 'here are some words')
- " norm! 1gg0elcwZZZ
- " call assert_equal('hereZZZ are some words', getline('.'))
- " norm! 1gg2elcWYYY
- " call assert_equal('hereZZZ areYYY some words', getline('.'))
- set cpo-=w
norm! 2gg0cwfoo
call assert_equal('foo', getline('.'))
@@ -2849,9 +2923,8 @@ func Test_normal49_counts()
endfunc
func Test_normal50_commandline()
- if !has("timers") || !has("cmdline_hist")
- return
- endif
+ CheckFeature timers
+ CheckFeature cmdline_hist
func! DoTimerWork(id)
call assert_equal('[Command Line]', bufname(''))
" should fail, with E11, but does fail with E23?
@@ -2880,9 +2953,7 @@ func Test_normal50_commandline()
endfunc
func Test_normal51_FileChangedRO()
- if !has("autocmd")
- return
- endif
+ CheckFeature autocmd
call writefile(['foo'], 'Xreadonly.log')
new Xreadonly.log
setl ro
@@ -2897,9 +2968,7 @@ func Test_normal51_FileChangedRO()
endfunc
func Test_normal52_rl()
- if !has("rightleft")
- return
- endif
+ CheckFeature rightleft
new
call setline(1, 'abcde fghij klmnopq')
norm! 1gg$
@@ -2931,22 +3000,6 @@ func Test_normal52_rl()
bw!
endfunc
-func Test_normal53_digraph()
- if !has('digraphs')
- return
- endif
- new
- call setline(1, 'abcdefgh|')
- exe "norm! 1gg0f\<c-k>!!"
- call assert_equal(9, col('.'))
- set cpo+=D
- exe "norm! 1gg0f\<c-k>!!"
- call assert_equal(1, col('.'))
-
- set cpo-=D
- bw!
-endfunc
-
func Test_normal54_Ctrl_bsl()
new
call setline(1, 'abcdefghijklmn')
@@ -3267,46 +3320,6 @@ func Test_normal_gk_gj()
set cpoptions& number& numberwidth& wrap&
endfunc
-" Test for cursor movement with '-' in 'cpoptions'
-func Test_normal_cpo_minus()
- throw 'Skipped: Nvim does not support cpoptions flag "-"'
- new
- call setline(1, ['foo', 'bar', 'baz'])
- let save_cpo = &cpo
- set cpo+=-
- call assert_beeps('normal 10j')
- call assert_equal(1, line('.'))
- normal G
- call assert_beeps('normal 10k')
- call assert_equal(3, line('.'))
- call assert_fails(10, 'E16:')
- let &cpo = save_cpo
- close!
-endfunc
-
-" Test for displaying dollar when changing text ('$' flag in 'cpoptions')
-func Test_normal_cpo_dollar()
- throw 'Skipped: use test/functional/legacy/cpoptions_spec.lua'
- new
- let g:Line = ''
- func SaveFirstLine()
- let g:Line = Screenline(1)
- return ''
- endfunc
- inoremap <expr> <buffer> <F2> SaveFirstLine()
- call test_override('redraw_flag', 1)
- set cpo+=$
- call setline(1, 'one two three')
- redraw!
- exe "normal c2w\<F2>vim"
- call assert_equal('one tw$ three', g:Line)
- call assert_equal('vim three', getline(1))
- set cpo-=$
- call test_override('ALL', 0)
- delfunc SaveFirstLine
- %bw!
-endfunc
-
" Test for using : to run a multi-line Ex command in operator pending mode
func Test_normal_yank_with_excmd()
new
@@ -3371,6 +3384,24 @@ func Test_normal_colon_op()
close!
endfunc
+" Test for d and D commands
+func Test_normal_delete_cmd()
+ new
+ " D in an empty line
+ call setline(1, '')
+ normal D
+ call assert_equal('', getline(1))
+ " D in an empty line in virtualedit mode
+ set virtualedit=all
+ normal D
+ call assert_equal('', getline(1))
+ set virtualedit&
+ " delete to a readonly register
+ call setline(1, ['abcd'])
+ call assert_beeps('normal ":d2l')
+ close!
+endfunc
+
" Test for deleting or changing characters across lines with 'whichwrap'
" containing 's'. Should count <EOL> as one character.
func Test_normal_op_across_lines()
@@ -3478,6 +3509,27 @@ func Test_normal_percent_jump()
close!
endfunc
+" Test for << and >> commands to shift text by 'shiftwidth'
+func Test_normal_shift_rightleft()
+ new
+ call setline(1, ['one', '', "\t", ' two', "\tthree", ' four'])
+ set shiftwidth=2 tabstop=8
+ normal gg6>>
+ call assert_equal([' one', '', "\t ", ' two', "\t three", "\tfour"],
+ \ getline(1, '$'))
+ normal ggVG2>>
+ call assert_equal([' one', '', "\t ", "\ttwo",
+ \ "\t three", "\t four"], getline(1, '$'))
+ normal gg6<<
+ call assert_equal([' one', '', "\t ", ' two', "\t three",
+ \ "\t four"], getline(1, '$'))
+ normal ggVG2<<
+ call assert_equal(['one', '', "\t", ' two', "\tthree", ' four'],
+ \ getline(1, '$'))
+ set shiftwidth& tabstop&
+ bw!
+endfunc
+
" Some commands like yy, cc, dd, >>, << and !! accept a count after
" typing the first letter of the command.
func Test_normal_count_after_operator()
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 2836e81c4a..3916d7c554 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -263,7 +263,7 @@ func Test_set_completion()
call feedkeys(":setglobal di\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"setglobal dictionary diff diffexpr diffopt digraph directory display', @:)
- " Expand boolan options. When doing :set no<Tab>
+ " Expand boolean options. When doing :set no<Tab>
" vim displays the options names without "no" but completion uses "no...".
call feedkeys(":set nodi\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"set nodiff digraph', @:)
diff --git a/src/nvim/testdir/test_partial.vim b/src/nvim/testdir/test_partial.vim
index 8c90f21600..3020668f1b 100644
--- a/src/nvim/testdir/test_partial.vim
+++ b/src/nvim/testdir/test_partial.vim
@@ -82,6 +82,9 @@ func Test_partial_dict()
let dict = {"tr": function('tr', ['hello', 'h', 'H'])}
call assert_equal("Hello", dict.tr())
+
+ call assert_fails("let F=function('setloclist', 10)", "E923:")
+ call assert_fails("let F=function('setloclist', [], [])", "E922:")
endfunc
func Test_partial_implicit()
@@ -354,3 +357,5 @@ func Test_compare_partials()
call assert_true(F1 isnot# F1d1) " Partial /= non-partial
call assert_true(d1.f1 isnot# d1.f1) " handle_subscript creates new partial each time
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index dcedfe26a2..9d9fc5e77b 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -740,7 +740,7 @@ func s:test_xhelpgrep(cchar)
" Search for non existing help string
call assert_fails('Xhelpgrep a1b2c3', 'E480:')
" Invalid regular expression
- call assert_fails('Xhelpgrep \@<!', 'E480:')
+ call assert_fails('Xhelpgrep \@<!', 'E866:')
endfunc
func Test_helpgrep()
@@ -4101,8 +4101,8 @@ endfunc
func Test_lvimgrep_crash2()
au BufNewFile x sfind
- call assert_fails('lvimgrep x x', 'E480:')
- call assert_fails('lvimgrep x x x', 'E480:')
+ call assert_fails('lvimgrep x x', 'E471:')
+ call assert_fails('lvimgrep x x x', 'E471:')
au! BufNewFile
endfunc
@@ -5801,6 +5801,21 @@ func Test_win_gettype()
lclose
endfunc
+fun Test_vimgrep_nomatch()
+ call XexprTests('c')
+ call g:Xsetlist([{'lnum':10,'text':'Line1'}])
+ copen
+ if has("win32")
+ call assert_fails('vimgrep foo *.zzz', 'E479:')
+ let expected = [{'lnum': 10, 'bufnr': 0, 'end_lnum': 0, 'pattern': '', 'valid': 0, 'vcol': 0, 'nr': 0, 'module': '', 'type': '', 'end_col': 0, 'col': 0, 'text': 'Line1'}]
+ else
+ call assert_fails('vimgrep foo *.zzz', 'E480:')
+ let expected = []
+ endif
+ call assert_equal(expected, getqflist())
+ cclose
+endfunc
+
" Test for opening the quickfix window in two tab pages and then closing one
" of the quickfix windows. This should not make the quickfix buffer unlisted.
" (github issue #9300).
diff --git a/src/nvim/testdir/test_random.vim b/src/nvim/testdir/test_random.vim
index 6d3f7dcfd9..fb1c9ab6cd 100644
--- a/src/nvim/testdir/test_random.vim
+++ b/src/nvim/testdir/test_random.vim
@@ -33,11 +33,11 @@ func Test_Rand()
endif
call assert_fails('echo srand([1])', 'E745:')
call assert_fails('echo rand("burp")', 'E475:')
- call assert_fails('echo rand([1, 2, 3])', 'E475:')
- call assert_fails('echo rand([[1], 2, 3, 4])', 'E475:')
- call assert_fails('echo rand([1, [2], 3, 4])', 'E475:')
- call assert_fails('echo rand([1, 2, [3], 4])', 'E475:')
- call assert_fails('echo rand([1, 2, 3, [4]])', 'E475:')
+ call assert_fails('echo rand([1, 2, 3])', 'E730:')
+ call assert_fails('echo rand([[1], 2, 3, 4])', 'E730:')
+ call assert_fails('echo rand([1, [2], 3, 4])', 'E730:')
+ call assert_fails('echo rand([1, 2, [3], 4])', 'E730:')
+ call assert_fails('echo rand([1, 2, 3, [4]])', 'E730:')
" call test_settime(0)
endfunc
diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim
index d08a980787..ece6ae518e 100644
--- a/src/nvim/testdir/test_regexp_latin.vim
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -101,8 +101,33 @@ func Test_multi_failure()
set re=2
call assert_fails('/a**', 'E871:')
call assert_fails('/a*\+', 'E871:')
- call assert_fails('/a\{a}', 'E870:')
+ call assert_fails('/a\{a}', 'E554:')
+ set re=0
+endfunc
+
+func Test_column_success_failure()
+ new
+ call setline(1, 'xbar')
+
+ set re=1
+ %s/\%>0v./A/
+ call assert_equal('Abar', getline(1))
+ call assert_fails('/\%v', 'E71:')
+ call assert_fails('/\%>v', 'E71:')
+ call assert_fails('/\%c', 'E71:')
+ call assert_fails('/\%<c', 'E71:')
+ call assert_fails('/\%l', 'E71:')
+ set re=2
+ %s/\%>0v./B/
+ call assert_equal('Bbar', getline(1))
+ call assert_fails('/\%v', 'E1273:')
+ call assert_fails('/\%>v', 'E1273:')
+ call assert_fails('/\%c', 'E1273:')
+ call assert_fails('/\%<c', 'E1273:')
+ call assert_fails('/\%l', 'E1273:')
+
set re=0
+ bwipe!
endfunc
func Test_recursive_addstate()
@@ -146,6 +171,10 @@ func Test_regexp_single_line_pat()
call add(tl, [2, 'c*', 'abdef', ''])
call add(tl, [2, 'bc\+', 'abccccdef', 'bcccc'])
call add(tl, [2, 'bc\+', 'abdef']) " no match
+ " match escape character in a string
+ call add(tl, [2, '.\e.', "one\<Esc>two", "e\<Esc>t"])
+ " match backspace character in a string
+ call add(tl, [2, '.\b.', "one\<C-H>two", "e\<C-H>t"])
" match newline character in a string
call add(tl, [2, 'o\nb', "foo\nbar", "o\nb"])
@@ -895,6 +924,8 @@ func Test_regexp_error()
call assert_fails("call matchlist('x x', '\\%#=2 \\zs*')", 'E888:')
call assert_fails("call matchlist('x x', '\\%#=2 \\ze*')", 'E888:')
call assert_fails('exe "normal /\\%#=1\\%[x\\%[x]]\<CR>"', 'E369:')
+ call assert_fails("call matchstr('abcd', '\\%o841\\%o142')", 'E678:')
+ call assert_equal('', matchstr('abcd', '\%o181\%o142'))
endfunc
" Test for using the last substitute string pattern (~)
@@ -1027,6 +1058,28 @@ func Test_using_invalid_visual_position()
bwipe!
endfunc
+func Test_using_two_engines_pattern()
+ new
+ call setline(1, ['foobar=0', 'foobar=1', 'foobar=2'])
+ " \%#= at the end of the pattern
+ for i in range(0, 2)
+ for j in range(0, 2)
+ exe "set re=" .. i
+ call cursor(j + 1, 7)
+ call assert_fails("%s/foobar\\%#=" .. j, 'E1281:')
+ endfor
+ endfor
+ set re=0
+
+ " \%#= at the start of the pattern
+ for i in range(0, 2)
+ call cursor(i + 1, 7)
+ exe ":%s/\\%#=" .. i .. "foobar=" .. i .. "/xx"
+ endfor
+ call assert_equal(['xx', 'xx', 'xx'], getline(1, '$'))
+ bwipe!
+endfunc
+
func Test_recursive_substitute_expr()
new
func Repl()
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index 11dd3badb6..5bdbbe7a22 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -263,8 +263,16 @@ func Test_get_register()
call assert_equal('', getreg("\<C-F>"))
call assert_equal('', getreg("\<C-W>"))
call assert_equal('', getreg("\<C-L>"))
+ " Change the last used register to '"' for the next test
+ normal! ""yy
+ let @" = 'happy'
+ call assert_equal('happy', getreg())
+ call assert_equal('happy', getreg(''))
call assert_equal('', getregtype('!'))
+ call assert_fails('echo getregtype([])', 'E730:')
+ call assert_equal('v', getregtype())
+ call assert_equal('v', getregtype(''))
" Test for inserting an invalid register content
call assert_beeps('exe "normal i\<C-R>!"')
@@ -277,7 +285,9 @@ func Test_get_register()
" Test for inserting a multi-line register in the command line
call feedkeys(":\<C-R>r\<Esc>", 'xt')
- call assert_equal("a\rb", histget(':', -1)) " Modified because of #6137
+ " Nvim: no trailing CR because of #6137
+ " call assert_equal("a\rb\r", histget(':', -1))
+ call assert_equal("a\rb", histget(':', -1))
call assert_fails('let r = getreg("=", [])', 'E745:')
call assert_fails('let r = getreg("=", 1, [])', 'E745:')
@@ -349,6 +359,12 @@ func Test_set_register()
normal 0".gP
call assert_equal('abcabcabc', getline(1))
+ let @"=''
+ call setreg('', '1')
+ call assert_equal('1', @")
+ call setreg('@', '2')
+ call assert_equal('2', @")
+
enew!
endfunc
diff --git a/src/nvim/testdir/test_reltime.vim b/src/nvim/testdir/test_reltime.vim
index b381f1ddbb..f4ce5de118 100644
--- a/src/nvim/testdir/test_reltime.vim
+++ b/src/nvim/testdir/test_reltime.vim
@@ -24,4 +24,8 @@ func Test_reltime()
call assert_true(reltimefloat(differs) < 0.1)
call assert_true(reltimefloat(differs) > 0.0)
+ call assert_equal(0, reltime({}))
+ call assert_equal(0, reltime({}, {}))
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index 0cf55c7d0b..96ed35718f 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -294,6 +294,9 @@ func Test_searchpair()
new
call setline(1, ['other code', 'here [', ' [', ' " cursor here', ' ]]'])
+ " should not give an error for using "42"
+ call assert_equal(0, searchpair('a', 'b', 'c', '', 42))
+
4
call assert_equal(3, searchpair('\[', '', ']', 'bW'))
call assert_equal([0, 3, 2, 0], getpos('.'))
@@ -897,9 +900,7 @@ func Test_incsearch_cmdline_modifier()
endfunc
func Test_incsearch_scrolling()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckRunVimInTerminal
call assert_equal(0, &scrolloff)
call writefile([
\ 'let dots = repeat(".", 120)',
@@ -1649,8 +1650,8 @@ func Test_search_with_no_last_pat()
call assert_fails(";//p", 'E35:')
call assert_fails("??p", 'E35:')
call assert_fails(";??p", 'E35:')
- call assert_fails('g//p', 'E476:')
- call assert_fails('v//p', 'E476:')
+ call assert_fails('g//p', ['E35:', 'E476:'])
+ call assert_fails('v//p', ['E35:', 'E476:'])
call writefile(v:errors, 'Xresult')
qall!
[SCRIPT]
@@ -1671,8 +1672,8 @@ func Test_search_tilde_pat()
call assert_fails('exe "normal /~\<CR>"', 'E33:')
call assert_fails('exe "normal ?~\<CR>"', 'E33:')
set regexpengine=2
- call assert_fails('exe "normal /~\<CR>"', 'E383:')
- call assert_fails('exe "normal ?~\<CR>"', 'E383:')
+ call assert_fails('exe "normal /~\<CR>"', ['E33:', 'E383:'])
+ call assert_fails('exe "normal ?~\<CR>"', ['E33:', 'E383:'])
set regexpengine&
call writefile(v:errors, 'Xresult')
qall!
@@ -1752,6 +1753,25 @@ func Test_invalid_regexp()
call assert_fails("call search('\\%#=3ab')", 'E864:')
endfunc
+" Test for searching a very complex pattern in a string. Should switch the
+" regexp engine from NFA to the old engine.
+func Test_regexp_switch_engine()
+ let l = readfile('samples/re.freeze.txt')
+ let v = substitute(l[4], '..\@<!', '', '')
+ call assert_equal(l[4], v)
+endfunc
+
+" Test for the \%V atom to search within visually selected text
+func Test_search_in_visual_area()
+ new
+ call setline(1, ['foo bar1', 'foo bar2', 'foo bar3', 'foo bar4'])
+ exe "normal 2GVjo/\\%Vbar\<CR>\<Esc>"
+ call assert_equal([2, 5], [line('.'), col('.')])
+ exe "normal 2GVj$?\\%Vbar\<CR>\<Esc>"
+ call assert_equal([3, 5], [line('.'), col('.')])
+ close!
+endfunc
+
" Test for searching with 'smartcase' and 'ignorecase'
func Test_search_smartcase()
new
diff --git a/src/nvim/testdir/test_selectmode.vim b/src/nvim/testdir/test_selectmode.vim
index f2cab45450..041f0592f1 100644
--- a/src/nvim/testdir/test_selectmode.vim
+++ b/src/nvim/testdir/test_selectmode.vim
@@ -34,6 +34,9 @@ func Test_selectmode_start()
set selectmode=cmd
call feedkeys('gvabc', 'xt')
call assert_equal('abctdef', getline(1))
+ " arrow keys without shift should not start selection
+ call feedkeys("A\<Home>\<Right>\<Left>ro", 'xt')
+ call assert_equal('roabctdef', getline(1))
set selectmode= keymodel=
bw!
endfunc
diff --git a/src/nvim/testdir/test_signals.vim b/src/nvim/testdir/test_signals.vim
index e1c6e5d11f..c291c68e0d 100644
--- a/src/nvim/testdir/test_signals.vim
+++ b/src/nvim/testdir/test_signals.vim
@@ -53,7 +53,7 @@ endfunc
" Test signal PWR, which should update the swap file.
func Test_signal_PWR()
if !HasSignal('PWR')
- return
+ throw 'Skipped: PWR signal not supported'
endif
" Set a very large 'updatetime' and 'updatecount', so that we can be sure
@@ -79,6 +79,33 @@ func Test_signal_PWR()
set updatetime& updatecount&
endfunc
+" Test signal INT. Handler sets got_int. It should be like typing CTRL-C.
+func Test_signal_INT()
+ CheckRunVimInTerminal
+ if !HasSignal('INT')
+ throw 'Skipped: INT signal not supported'
+ endif
+
+ " Skip the rest of the test when running with valgrind as signal INT is not
+ " received somehow by Vim when running with valgrind.
+ let cmd = GetVimCommand()
+ if cmd =~ 'valgrind'
+ throw 'Skipped: cannot test signal INT with valgrind'
+ endif
+
+ let buf = RunVimInTerminal('', {'rows': 6})
+ let pid_vim = term_getjob(buf)->job_info().process
+
+ " Check that an endless loop in Vim is interrupted by signal INT.
+ call term_sendkeys(buf, ":while 1 | endwhile\n")
+ call WaitForAssert({-> assert_equal(':while 1 | endwhile', term_getline(buf, 6))})
+ exe 'silent !kill -s INT ' .. pid_vim
+ call term_sendkeys(buf, ":call setline(1, 'INTERUPTED')\n")
+ call WaitForAssert({-> assert_equal('INTERUPTED', term_getline(buf, 1))})
+
+ call StopVimInTerminal(buf)
+endfunc
+
" Test a deadly signal.
"
" There are several deadly signals: SISEGV, SIBUS, SIGTERM...
@@ -92,9 +119,7 @@ func Test_deadly_signal_TERM()
if !HasSignal('TERM')
throw 'Skipped: TERM signal not supported'
endif
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot run vim in terminal'
- endif
+ CheckRunVimInTerminal
let cmd = GetVimCommand()
if cmd =~ 'valgrind'
throw 'Skipped: cannot test signal TERM with valgrind'
diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim
index ff9ba3d8ed..1f1b3097b1 100644
--- a/src/nvim/testdir/test_signs.vim
+++ b/src/nvim/testdir/test_signs.vim
@@ -483,13 +483,13 @@ func Test_sign_funcs()
call assert_fails('call sign_place(5, "", "sign1", "@", {"lnum" : 10})',
\ 'E158:')
call assert_fails('call sign_place(5, "", "sign1", [], {"lnum" : 10})',
- \ 'E158:')
+ \ 'E730:')
call assert_fails('call sign_place(21, "", "sign1", "Xsign",
\ {"lnum" : -1})', 'E474:')
call assert_fails('call sign_place(22, "", "sign1", "Xsign",
\ {"lnum" : 0})', 'E474:')
call assert_fails('call sign_place(22, "", "sign1", "Xsign",
- \ {"lnum" : []})', 'E474:')
+ \ {"lnum" : []})', 'E745:')
call assert_equal(-1, sign_place(1, "*", "sign1", "Xsign", {"lnum" : 10}))
" Tests for sign_getplaced()
@@ -1731,7 +1731,7 @@ func Test_sign_jump_func()
call assert_fails("call sign_jump(5, 'g5', 'foo')", 'E157:')
call assert_fails('call sign_jump([], "", "foo")', 'E745:')
call assert_fails('call sign_jump(2, [], "foo")', 'E730:')
- call assert_fails('call sign_jump(2, "", {})', 'E158:')
+ call assert_fails('call sign_jump(2, "", {})', 'E731:')
call assert_fails('call sign_jump(2, "", "baz")', 'E158:')
sign unplace * group=*
@@ -1741,9 +1741,7 @@ endfunc
" Test for correct cursor position after the sign column appears or disappears.
func Test_sign_cursor_position()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckRunVimInTerminal
let lines =<< trim END
call setline(1, [repeat('x', 75), 'mmmm', 'yyyy'])
diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim
index c3e7788164..f9cbcbb55f 100644
--- a/src/nvim/testdir/test_sort.vim
+++ b/src/nvim/testdir/test_sort.vim
@@ -1360,7 +1360,7 @@ func Test_sort_cmd()
call setline(1, ['line1', 'line2'])
call assert_fails('sort no', 'E474:')
call assert_fails('sort c', 'E475:')
- call assert_fails('sort #pat%', 'E682:')
+ call assert_fails('sort #pat%', 'E654:')
enew!
endfunc
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index 4af02d3d31..d8495fdb9b 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -495,7 +495,7 @@ func Test_spellsuggest_expr_errors()
return [[{}, {}]]
endfunc
set spellsuggest=expr:MySuggest3()
- call assert_fails("call spellsuggest('baord')", 'E728:')
+ call assert_fails("call spellsuggest('baord')", 'E731:')
set nospell spellsuggest&
delfunc MySuggest
@@ -1430,3 +1430,5 @@ let g:test_data_aff_sal = [
\"SAL ZZ- _",
\"SAL Z S",
\ ]
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index b8aad1bc46..30bbd355df 100644
--- a/src/nvim/testdir/test_startup.vim
+++ b/src/nvim/testdir/test_startup.vim
@@ -809,9 +809,7 @@ func Test_issue_3969()
endfunc
func Test_start_with_tabs()
- if !CanRunVimInTerminal()
- return
- endif
+ CheckRunVimInTerminal
let buf = RunVimInTerminal('-p a b c', {})
call VerifyScreenDump(buf, 'Test_start_with_tabs', {})
@@ -968,9 +966,7 @@ endfunc
" Test for specifying a non-existing vimrc file using "-u"
func Test_missing_vimrc()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot run vim in terminal'
- endif
+ CheckRunVimInTerminal
let after =<< trim [CODE]
call assert_match('^E282:', v:errmsg)
call writefile(v:errors, 'Xtestout')
diff --git a/src/nvim/testdir/test_startup_utf8.vim b/src/nvim/testdir/test_startup_utf8.vim
index bb4304396e..2ee6ecc41d 100644
--- a/src/nvim/testdir/test_startup_utf8.vim
+++ b/src/nvim/testdir/test_startup_utf8.vim
@@ -63,9 +63,7 @@ func Test_read_fifo_utf8()
endfunc
func Test_detect_ambiwidth()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot run Vim in a terminal window'
- endif
+ CheckRunVimInTerminal
" Use the title termcap entries to output the escape sequence.
call writefile([
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index 88a3c13d65..5b476ddd7f 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -446,13 +446,18 @@ func Test_substitute_errors()
call assert_fails('s/FOO/bar/', 'E486:')
call assert_fails('s/foo/bar/@', 'E488:')
- call assert_fails('s/\(/bar/', 'E476:')
+ call assert_fails('s/\(/bar/', 'E54:')
call assert_fails('s afooabara', 'E146:')
call assert_fails('s\\a', 'E10:')
setl nomodifiable
call assert_fails('s/foo/bar/', 'E21:')
+ call assert_fails("let s=substitute([], 'a', 'A', 'g')", 'E730:')
+ call assert_fails("let s=substitute('abcda', [], 'A', 'g')", 'E730:')
+ call assert_fails("let s=substitute('abcda', 'a', [], 'g')", 'E730:')
+ call assert_fails("let s=substitute('abcda', 'a', 'A', [])", 'E730:')
+
bwipe!
endfunc
@@ -488,6 +493,9 @@ func Test_sub_replace_1()
call assert_equal("x\<C-M>x", substitute('xXx', 'X', "\r", ''))
call assert_equal("YyyY", substitute('Y', 'Y', '\L\uyYy\l\EY', ''))
call assert_equal("zZZz", substitute('Z', 'Z', '\U\lZzZ\u\Ez', ''))
+ " \v or \V after $
+ call assert_equal('abxx', substitute('abcd', 'xy$\v|cd$', 'xx', ''))
+ call assert_equal('abxx', substitute('abcd', 'xy$\V\|cd\$', 'xx', ''))
endfunc
func Test_sub_replace_2()
@@ -833,9 +841,9 @@ endfunc
func Test_sub_with_no_last_pat()
let lines =<< trim [SCRIPT]
call assert_fails('~', 'E33:')
- call assert_fails('s//abc/g', 'E476:')
- call assert_fails('s\/bar', 'E476:')
- call assert_fails('s\&bar&', 'E476:')
+ call assert_fails('s//abc/g', 'E35:')
+ call assert_fails('s\/bar', 'E35:')
+ call assert_fails('s\&bar&', 'E33:')
call writefile(v:errors, 'Xresult')
qall!
[SCRIPT]
@@ -862,6 +870,40 @@ endfunc
func Test_substitute()
call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g'))
+ " Substitute with special keys
+ call assert_equal("a\<End>c", substitute('abc', "a.c", "a\<End>c", ''))
+endfunc
+
+func Test_substitute_expr()
+ let g:val = 'XXX'
+ call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', ''))
+ call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, ''))
+ call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
+ \ '\=nr2char("0x" . submatch(1))', 'g'))
+ call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
+ \ {-> nr2char("0x" . submatch(1))}, 'g'))
+
+ call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)',
+ \ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
+
+ func Recurse()
+ return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '')
+ endfunc
+ " recursive call works
+ call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, ''))
+
+ call assert_fails("let s=submatch([])", 'E745:')
+ call assert_fails("let s=submatch(2, [])", 'E745:')
+endfunc
+
+func Test_invalid_submatch()
+ " This was causing invalid memory access in Vim-7.4.2232 and older
+ call assert_fails("call substitute('x', '.', {-> submatch(10)}, '')", 'E935:')
+ call assert_fails('eval submatch(-1)', 'E935:')
+ call assert_equal('', submatch(0))
+ call assert_equal('', submatch(1))
+ call assert_equal([], submatch(0, 1))
+ call assert_equal([], submatch(1, 1))
endfunc
func Test_submatch_list_concatenate()
@@ -870,6 +912,44 @@ func Test_submatch_list_concatenate()
call substitute('A1', pat, Rep, '')->assert_equal("[['A1'], ['1']]")
endfunc
+func Test_substitute_expr_arg()
+ call assert_equal('123456789-123456789=', substitute('123456789',
+ \ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
+ \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+ call assert_equal('123456-123456=789', substitute('123456789',
+ \ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
+ \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+ call assert_equal('123456789-123456789x=', substitute('123456789',
+ \ '\(.\)\(.\)\(.*\)',
+ \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+ call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
+ call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
+ call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
+ call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
+endfunc
+
+" Test for using a function to supply the substitute string
+func Test_substitute_using_func()
+ func Xfunc()
+ return '1234'
+ endfunc
+ call assert_equal('a1234f', substitute('abcdef', 'b..e',
+ \ function("Xfunc"), ''))
+ delfunc Xfunc
+endfunc
+
+" Test for using submatch() with a multiline match
+func Test_substitute_multiline_submatch()
+ new
+ call setline(1, ['line1', 'line2', 'line3', 'line4'])
+ %s/^line1\(\_.\+\)line4$/\=submatch(1)/
+ call assert_equal(['', 'line2', 'line3', ''], getline(1, '$'))
+ close!
+endfunc
+
func Test_substitute_skipped_range()
new
if 0
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index d686ad7e96..9d108c6ca2 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -378,7 +378,7 @@ func Test_syntax_invalid_arg()
call assert_fails('syntax sync x', 'E404:')
call assert_fails('syntax keyword Abc a[', 'E789:')
call assert_fails('syntax keyword Abc a[bc]d', 'E890:')
- call assert_fails('syntax cluster Abc add=A add=', 'E475:')
+ call assert_fails('syntax cluster Abc add=A add=', 'E406:')
" Test for too many \z\( and unmatched \z\(
" Not able to use assert_fails() here because both E50:/E879: and E475:
@@ -623,15 +623,15 @@ func Test_synstack_synIDtrans()
call assert_equal(['cComment', 'cTodo'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")'))
call assert_equal(['Comment', 'Todo'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")'))
+ call assert_fails("let n=synIDtrans([])", 'E745:')
+
syn clear
bw!
endfunc
" Check highlighting for a small piece of C code with a screen dump.
func Test_syntax_c()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckRunVimInTerminal
call writefile([
\ '/* comment line at the top */',
\ 'int main(int argc, char **argv) { // another comment',
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index 6d468ec9de..b97aa409d8 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -591,9 +591,7 @@ func Test_tabs()
endfunc
func Test_tabpage_cmdheight()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot make screendumps'
- endif
+ CheckRunVimInTerminal
call writefile([
\ 'set laststatus=2',
\ 'set cmdheight=2',
@@ -623,6 +621,17 @@ func Test_tabpage_close_cmdwin()
tabonly
endfunc
+" Pressing <C-PageUp> in insert mode should go to the previous tab page
+" and <C-PageDown> should go to the next tab page
+func Test_tabpage_Ctrl_Pageup()
+ tabnew
+ call feedkeys("i\<C-PageUp>", 'xt')
+ call assert_equal(1, tabpagenr())
+ call feedkeys("i\<C-PageDown>", 'xt')
+ call assert_equal(2, tabpagenr())
+ %bw!
+endfunc
+
" Return the terminal key code for selecting a tab page from the tabline. This
" sequence contains the following codes: a CSI (0x9b), KS_TABLINE (0xf0),
" KS_FILLER (0x58) and then the tab page number.
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index ffc1d63b90..bdf5afa5b2 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -85,7 +85,7 @@ func Test_tagfunc()
return v:null
endfunc
set tags= tfu=NullTagFunc
- call assert_fails('tag nothing', 'E426')
+ call assert_fails('tag nothing', 'E433')
delf NullTagFunc
bwipe!
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index 04c0218f74..61bf9e6d0d 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -8,7 +8,7 @@ func Test_ptag_with_notagstack()
CheckFeature quickfix
set notagstack
- call assert_fails('ptag does_not_exist_tag_name', 'E426')
+ call assert_fails('ptag does_not_exist_tag_name', 'E433')
set tagstack&vim
endfunc
@@ -282,6 +282,7 @@ func Test_tag_file_encoding()
call delete('Xtags1')
endfunc
+" Test for emacs-style tags file (TAGS)
func Test_tagjump_etags()
if !has('emacs_tags')
return
@@ -345,7 +346,7 @@ func Test_tagjump_etags()
\ "Xmain.c,64",
\ ";;;;\x7f1,0",
\ ], 'Xtags')
- call assert_fails('tag foo', 'E426:')
+ call assert_fails('tag foo', 'E431:')
call delete('Xtags')
call delete('Xtags2')
@@ -371,6 +372,7 @@ func Test_getsettagstack()
call assert_fails("call settagstack(1, {'items' : 10})", 'E714')
call assert_fails("call settagstack(1, {'items' : []}, 10)", 'E928')
call assert_fails("call settagstack(1, {'items' : []}, 'b')", 'E962')
+ call assert_equal(-1, settagstack(0, v:_null_dict))
set tags=Xtags
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
@@ -1099,7 +1101,7 @@ func Test_tselect_listing()
call writefile([
\ "!_TAG_FILE_ENCODING\tutf-8\t//",
\ "first\tXfoo\t1" .. ';"' .. "\tv\ttyperef:typename:int\tfile:",
- \ "first\tXfoo\t2" .. ';"' .. "\tv\ttyperef:typename:char\tfile:"],
+ \ "first\tXfoo\t2" .. ';"' .. "\tkind:v\ttyperef:typename:char\tfile:"],
\ 'Xtags')
set tags=Xtags
@@ -1422,4 +1424,56 @@ func Test_tag_length()
set tags& taglength&
endfunc
+" Tests for errors in a tags file
+func Test_tagfile_errors()
+ set tags=Xtags
+
+ " missing search pattern or line number for a tag
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "foo\tXfile\t"], 'Xtags', 'b')
+ call writefile(['foo'], 'Xfile')
+
+ enew
+ tag foo
+ call assert_equal('', @%)
+ let caught_431 = v:false
+ try
+ eval taglist('.*')
+ catch /:E431:/
+ let caught_431 = v:true
+ endtry
+ call assert_equal(v:true, caught_431)
+
+ call delete('Xtags')
+ call delete('Xfile')
+ set tags&
+endfunc
+
+" When :stag fails to open the file, should close the new window
+func Test_stag_close_window_on_error()
+ new | only
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "foo\tXfile\t1"], 'Xtags')
+ call writefile(['foo'], 'Xfile')
+ call writefile([], '.Xfile.swp')
+ " Remove the catch-all that runtest.vim adds
+ au! SwapExists
+ augroup StagTest
+ au!
+ autocmd SwapExists Xfile let v:swapchoice='q'
+ augroup END
+
+ stag foo
+ call assert_equal(1, winnr('$'))
+ call assert_equal('', @%)
+
+ augroup StagTest
+ au!
+ augroup END
+ call delete('Xfile')
+ call delete('.Xfile.swp')
+ set tags&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim
index be46773256..658485582c 100644
--- a/src/nvim/testdir/test_taglist.vim
+++ b/src/nvim/testdir/test_taglist.vim
@@ -36,6 +36,8 @@ func Test_taglist()
call assert_equal('d', cmd[0]['kind'])
call assert_equal('call cursor(3, 4)', cmd[0]['cmd'])
+ call assert_fails("let l=taglist([])", 'E730:')
+
call delete('Xtags')
set tags&
bwipe
@@ -82,13 +84,11 @@ func Test_taglist_ctags_etags()
endfunc
func Test_tags_too_long()
- call assert_fails('tag ' . repeat('x', 1020), 'E426')
+ call assert_fails('tag ' . repeat('x', 1020), ['E433', 'E426'])
tags
endfunc
func Test_tagfiles()
- " Nvim: different default for 'tags'.
- set tags=./tags,tags
call assert_equal([], tagfiles())
call writefile(["FFoo\tXfoo\t1"], 'Xtags1')
diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim
index 4eb6e69adf..3bce2eb24d 100644
--- a/src/nvim/testdir/test_textformat.vim
+++ b/src/nvim/testdir/test_textformat.vim
@@ -1096,6 +1096,20 @@ func Test_fo_a_w()
call feedkeys("iabc abc a abc\<Esc>k0weade", 'xt')
call assert_equal(['abc abcde ', 'a abc'], getline(1, '$'))
+ " when a line ends with space, it is not broken up.
+ %d
+ call feedkeys("ione two to ", 'xt')
+ call assert_equal('one two to ', getline(1))
+
+ " when a line ends with spaces and backspace is used in the next line, the
+ " last space in the previous line should be removed.
+ %d
+ set backspace=indent,eol,start
+ call setline(1, ['one ', 'two'])
+ exe "normal 2Gi\<BS>"
+ call assert_equal(['one two'], getline(1, '$'))
+ set backspace&
+
" Test for 'a', 'w' and '1' options.
setlocal textwidth=0
setlocal fo=1aw
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index 3a6abb3968..f94ee6c9f3 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -304,9 +304,7 @@ func Test_timer_ex_mode()
endfunc
func Test_timer_restore_count()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot run Vim in a terminal window'
- endif
+ CheckRunVimInTerminal
" Check that v:count is saved and restored, not changed by a timer.
call writefile([
\ 'nnoremap <expr><silent> L v:count ? v:count . "l" : "l"',
diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim
index d71bb5bbb8..8a1d2d3fa7 100644
--- a/src/nvim/testdir/test_trycatch.vim
+++ b/src/nvim/testdir/test_trycatch.vim
@@ -2000,13 +2000,11 @@ endfunc
func Test_try_catch_errors()
call assert_fails('throw |', 'E471:')
call assert_fails("throw \n ", 'E471:')
- call assert_fails('catch abc', 'E603:')
+ call assert_fails('catch abc', 'E654:')
call assert_fails('try | let i = 1| finally | catch | endtry', 'E604:')
call assert_fails('finally', 'E606:')
call assert_fails('try | finally | finally | endtry', 'E607:')
- " v8.2.3486 has been ported, but v8.2.1183 hasn't, so E170 appears here.
- " call assert_fails('try | for i in range(5) | endif | endtry', 'E580:')
- call assert_fails('try | for i in range(5) | endif | endtry', 'E170:')
+ call assert_fails('try | for i in range(5) | endif | endtry', 'E580:')
call assert_fails('try | while v:true | endtry', 'E170:')
call assert_fails('try | if v:true | endtry', 'E171:')
endfunc
@@ -2223,6 +2221,23 @@ func Test_user_command_throw_in_function_call()
unlet g:caught
endfunc
+" Test that after reporting an uncaught exception there is no error for a
+" missing :endif
+func Test_after_exception_no_endif_error()
+ function Throw()
+ throw "Failure"
+ endfunction
+
+ function Foo()
+ if 1
+ call Throw()
+ endif
+ endfunction
+ call assert_fails('call Foo()', ['E605:', 'E605:'])
+ delfunc Throw
+ delfunc Foo
+endfunc
+
" Test for using throw in a called function with following endtry {{{1
func Test_user_command_function_call_with_endtry()
let lines =<< trim END
diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim
index 7145d303cc..aa3b02b575 100644
--- a/src/nvim/testdir/test_utf8.vim
+++ b/src/nvim/testdir/test_utf8.vim
@@ -12,7 +12,7 @@ func Test_visual_block_insert()
bwipeout!
endfunc
-" Test for built-in function strchars()
+" Test for built-in functions strchars() and strcharlen()
func Test_strchars()
let inp = ["a", "あいa", "A\u20dd", "A\u20dd\u20dd", "\u20dd"]
let exp = [[1, 1, 1], [3, 3, 3], [2, 2, 1], [3, 3, 1], [1, 1, 1]]
@@ -21,6 +21,15 @@ func Test_strchars()
call assert_equal(exp[i][1], inp[i]->strchars(0))
call assert_equal(exp[i][2], strchars(inp[i], 1))
endfor
+
+ let exp = [1, 3, 1, 1, 1]
+ for i in range(len(inp))
+ call assert_equal(exp[i], inp[i]->strcharlen())
+ call assert_equal(exp[i], strcharlen(inp[i]))
+ endfor
+
+ call assert_fails("let v=strchars('abc', [])", 'E745:')
+ call assert_fails("let v=strchars('abc', 2)", 'E1023:')
endfunc
" Test for customlist completion
diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim
index 32ad64cda4..e12c71d521 100644
--- a/src/nvim/testdir/test_vartabs.vim
+++ b/src/nvim/testdir/test_vartabs.vim
@@ -379,6 +379,8 @@ func Test_vartabs_shiftwidth()
let lines = ScreenLines([1, 3], winwidth(0))
call s:compare_lines(expect4, lines)
+ call assert_fails('call shiftwidth([])', 'E745:')
+
" cleanup
bw!
bw!
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index f20fd12ed3..8faa9135e5 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -3,14 +3,12 @@
source check.vim
source shared.vim
+source script_util.vim
"-------------------------------------------------------------------------------
" Test environment {{{1
"-------------------------------------------------------------------------------
-com! XpathINIT let g:Xpath = ''
-com! -nargs=1 -bar Xpath let g:Xpath = g:Xpath . <args>
-
" Append a message to the "messages" file
func Xout(text)
split messages
@@ -20,67 +18,31 @@ endfunc
com! -nargs=1 Xout call Xout(<args>)
-" MakeScript() - Make a script file from a function. {{{2
-"
-" Create a script that consists of the body of the function a:funcname.
-" Replace any ":return" by a ":finish", any argument variable by a global
-" variable, and every ":call" by a ":source" for the next following argument
-" in the variable argument list. This function is useful if similar tests are
-" to be made for a ":return" from a function call or a ":finish" in a script
-" file.
-func MakeScript(funcname, ...)
- let script = tempname()
- execute "redir! >" . script
- execute "function" a:funcname
- redir END
- execute "edit" script
- " Delete the "function" and the "endfunction" lines. Do not include the
- " word "function" in the pattern since it might be translated if LANG is
- " set. When MakeScript() is being debugged, this deletes also the debugging
- " output of its line 3 and 4.
- exec '1,/.*' . a:funcname . '(.*)/d'
- /^\d*\s*endfunction\>/,$d
- %s/^\d*//e
- %s/return/finish/e
- %s/\<a:\(\h\w*\)/g:\1/ge
- normal gg0
- let cnt = 0
- while search('\<call\s*\%(\u\|s:\)\w*\s*(.*)', 'W') > 0
- let cnt = cnt + 1
- s/\<call\s*\%(\u\|s:\)\w*\s*(.*)/\='source ' . a:{cnt}/
- endwhile
- g/^\s*$/d
- write
- bwipeout
- return script
-endfunc
-
-" ExecAsScript - Source a temporary script made from a function. {{{2
-"
-" Make a temporary script file from the function a:funcname, ":source" it, and
-" delete it afterwards. However, if an exception is thrown the file may remain,
-" the caller should call DeleteTheScript() afterwards.
-let s:script_name = ''
-function! ExecAsScript(funcname)
- " Make a script from the function passed as argument.
- let s:script_name = MakeScript(a:funcname)
-
- " Source and delete the script.
- exec "source" s:script_name
- call delete(s:script_name)
- let s:script_name = ''
-endfunction
-
-function! DeleteTheScript()
- if s:script_name
- call delete(s:script_name)
- let s:script_name = ''
- endif
+" Create a new instance of Vim and run the commands in 'test' and then 'verify'
+" The commands in 'test' are expected to store the test results in the Xtest.out
+" file. If the test passes successfully, then Xtest.out should be empty.
+func RunInNewVim(test, verify)
+ let init =<< trim END
+ set cpo-=C " support line-continuation in sourced script
+ source script_util.vim
+ XpathINIT
+ XloopINIT
+ END
+ let cleanup =<< trim END
+ call writefile(v:errors, 'Xtest.out')
+ qall
+ END
+ call writefile(init, 'Xtest.vim')
+ call writefile(a:test, 'Xtest.vim', 'a')
+ call writefile(a:verify, 'Xverify.vim')
+ call writefile(cleanup, 'Xverify.vim', 'a')
+ call RunVim([], [], "-S Xtest.vim -S Xverify.vim")
+ call assert_equal([], readfile('Xtest.out'))
+ call delete('Xtest.out')
+ call delete('Xtest.vim')
+ call delete('Xverify.vim')
endfunc
-com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>)
-
-
"-------------------------------------------------------------------------------
" Test 1: :endwhile in function {{{1
"
@@ -90,7 +52,7 @@ com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>)
" tests will hang.
"-------------------------------------------------------------------------------
-function! T1_F()
+func T1_F()
Xpath 'a'
let first = 1
while 1
@@ -104,9 +66,9 @@ function! T1_F()
return
endif
endwhile
-endfunction
+endfunc
-function! T1_G()
+func T1_G()
Xpath 'h'
let first = 1
while 1
@@ -121,7 +83,7 @@ function! T1_G()
endif
if 1 " unmatched :if
endwhile
-endfunction
+endfunc
func Test_endwhile_function()
XpathINIT
@@ -175,7 +137,7 @@ endfunc
" Test 3: :if, :elseif, :while, :continue, :break {{{1
"-------------------------------------------------------------------------------
-function Test_if_while()
+func Test_if_while()
XpathINIT
if 1
Xpath 'a'
@@ -235,7 +197,7 @@ endfunc
" Test 4: :return {{{1
"-------------------------------------------------------------------------------
-function! T4_F()
+func T4_F()
if 1
Xpath 'a'
let loops = 3
@@ -253,15 +215,15 @@ function! T4_F()
else
Xpath 'g'
endif
-endfunction
+endfunc
-function Test_return()
+func Test_return()
XpathINIT
call T4_F()
Xpath '4'
call assert_equal('ab3e3b2c24', g:Xpath)
-endfunction
+endfunc
"-------------------------------------------------------------------------------
@@ -271,14 +233,14 @@ endfunction
" test as a script file (:return replaced by :finish).
"-------------------------------------------------------------------------------
-function Test_finish()
+func Test_finish()
XpathINIT
ExecAsScript T4_F
Xpath '5'
call DeleteTheScript()
call assert_equal('ab3e3b2c25', g:Xpath)
-endfunction
+endfunc
@@ -412,7 +374,7 @@ delfunction G31
delfunction G32
delfunction G33
-function Test_defining_functions()
+func Test_defining_functions()
call assert_equal('ade2ie3ibcg0h1g1h2g2h3fg0h1g1h2g2h3m', g:test6_result)
call assert_equal('F1G1F2G21G22G23F3G31G32G33', g:test6_calls)
endfunc
@@ -476,7 +438,7 @@ endfunc
XpathINIT
-function! T8_F()
+func T8_F()
if 1
Xpath 'a'
while 1
@@ -508,9 +470,9 @@ function! T8_F()
return novar " returns (default return value 0)
Xpath 'q'
return 1 " not reached
-endfunction
+endfunc
-function! T8_G() abort
+func T8_G() abort
if 1
Xpath 'r'
while 1
@@ -524,9 +486,9 @@ function! T8_G() abort
Xpath 'x'
return -4 " not reached
-endfunction
+endfunc
-function! T8_H() abort
+func T8_H() abort
while 1
Xpath 'A'
if 1
@@ -540,7 +502,7 @@ function! T8_H() abort
Xpath 'F'
return -4 " not reached
-endfunction
+endfunc
" Aborted functions (T8_G and T8_H) return -1.
let g:test8_sum = (T8_F() + 1) - 4 * T8_G() - 8 * T8_H()
@@ -567,7 +529,7 @@ endfunc
XpathINIT
-function! F() abort
+func F() abort
Xpath 'a'
let result = G() " not aborted
Xpath 'b'
@@ -575,30 +537,30 @@ function! F() abort
Xpath 'c'
endif
return 1
-endfunction
+endfunc
-function! G() " no abort attribute
+func G() " no abort attribute
Xpath 'd'
if H() != -1 " aborted
Xpath 'e'
endif
Xpath 'f'
return 2
-endfunction
+endfunc
-function! H() abort
+func H() abort
Xpath 'g'
call I() " aborted
Xpath 'h'
return 4
-endfunction
+endfunc
-function! I() abort
+func I() abort
Xpath 'i'
asdf " error
Xpath 'j'
return 8
-endfunction
+endfunc
if F() != 1
Xpath 'k'
@@ -626,7 +588,7 @@ endfunc
XpathINIT
-function! MSG(enr, emsg)
+func MSG(enr, emsg)
let english = v:lang == "C" || v:lang =~ '^[Ee]n'
if a:enr == ""
Xout "TODO: Add message number for:" a:emsg
@@ -710,10 +672,10 @@ XpathINIT
let calls = 0
-function! P(num)
+func P(num)
let g:calls = g:calls + a:num " side effect on call
return 0
-endfunction
+endfunc
if 1
Xpath 'a'
@@ -1092,7 +1054,4843 @@ func Test_unmatched_if_in_while()
endfunc
"-------------------------------------------------------------------------------
+" Test 18: Interrupt (Ctrl-C pressed) {{{1
+"
+" On an interrupt, the script processing is terminated immediately.
+"-------------------------------------------------------------------------------
+
+func Test_interrupt_while_if()
+ let test =<< trim [CODE]
+ try
+ if 1
+ Xpath 'a'
+ while 1
+ Xpath 'b'
+ if 1
+ Xpath 'c'
+ call interrupt()
+ call assert_report('should not get here')
+ break
+ finish
+ endif | call assert_report('should not get here')
+ call assert_report('should not get here')
+ endwhile | call assert_report('should not get here')
+ call assert_report('should not get here')
+ endif | call assert_report('should not get here')
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'd'
+ endtry | Xpath 'e'
+ Xpath 'f'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdef', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_interrupt_try()
+ let test =<< trim [CODE]
+ try
+ try
+ Xpath 'a'
+ call interrupt()
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'b'
+ endtry | Xpath 'c'
+ Xpath 'd'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcd', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_interrupt_func_while_if()
+ let test =<< trim [CODE]
+ func F()
+ if 1
+ Xpath 'a'
+ while 1
+ Xpath 'b'
+ if 1
+ Xpath 'c'
+ call interrupt()
+ call assert_report('should not get here')
+ break
+ return
+ endif | call assert_report('should not get here')
+ call assert_report('should not get here')
+ endwhile | call assert_report('should not get here')
+ call assert_report('should not get here')
+ endif | call assert_report('should not get here')
+ call assert_report('should not get here')
+ endfunc
+
+ Xpath 'd'
+ try
+ call F() | call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'e'
+ endtry | Xpath 'f'
+ Xpath 'g'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('dabcefg', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_interrupt_func_try()
+ let test =<< trim [CODE]
+ func G()
+ try
+ Xpath 'a'
+ call interrupt()
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ endfunc
+
+ Xpath 'b'
+ try
+ call G() | call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'c'
+ endtry | Xpath 'd'
+ Xpath 'e'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('bacde', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 19: Aborting on errors inside :try/:endtry {{{1
+"
+" An error in a command dynamically enclosed in a :try/:endtry region
+" aborts script processing immediately. It does not matter whether
+" the failing command is outside or inside a function and whether a
+" function has an "abort" attribute.
+"-------------------------------------------------------------------------------
+
+func Test_try_error_abort_1()
+ let test =<< trim [CODE]
+ func F() abort
+ Xpath 'a'
+ asdf
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ Xpath 'b'
+ call F()
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ba', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_2()
+ let test =<< trim [CODE]
+ func G()
+ Xpath 'a'
+ asdf
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ Xpath 'b'
+ call G()
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ba', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_3()
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ asdf
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_4()
+ let test =<< trim [CODE]
+ if 1
+ try
+ Xpath 'a'
+ asdf
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ endif | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_5()
+ let test =<< trim [CODE]
+ let p = 1
+ while p
+ let p = 0
+ try
+ Xpath 'a'
+ asdf
+ call assert_report('should not get here')
+ endtry | call assert_report('should not get here')
+ endwhile | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_try_error_abort_6()
+ let test =<< trim [CODE]
+ let p = 1
+ Xpath 'a'
+ while p
+ Xpath 'b'
+ let p = 0
+ try
+ Xpath 'c'
+ endwhile | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 20: Aborting on errors after :try/:endtry {{{1
+"
+" When an error occurs after the last active :try/:endtry region has
+" been left, termination behavior is as if no :try/:endtry has been
+" seen.
+"-------------------------------------------------------------------------------
+
+func Test_error_after_try_1()
+ let test =<< trim [CODE]
+ let p = 1
+ while p
+ let p = 0
+ Xpath 'a'
+ try
+ Xpath 'b'
+ endtry
+ asdf
+ call assert_report('should not get here')
+ endwhile | call assert_report('should not get here')
+ Xpath 'c'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_2()
+ let test =<< trim [CODE]
+ while 1
+ try
+ Xpath 'a'
+ break
+ call assert_report('should not get here')
+ endtry
+ endwhile
+ Xpath 'b'
+ asdf
+ Xpath 'c'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_3()
+ let test =<< trim [CODE]
+ while 1
+ try
+ Xpath 'a'
+ break
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ endtry
+ endwhile
+ Xpath 'c'
+ asdf
+ Xpath 'd'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcd', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_4()
+ let test =<< trim [CODE]
+ while 1
+ try
+ Xpath 'a'
+ finally
+ Xpath 'b'
+ break
+ call assert_report('should not get here')
+ endtry
+ endwhile
+ Xpath 'c'
+ asdf
+ Xpath 'd'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcd', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_5()
+ let test =<< trim [CODE]
+ let p = 1
+ while p
+ let p = 0
+ try
+ Xpath 'a'
+ continue
+ call assert_report('should not get here')
+ endtry
+ endwhile
+ Xpath 'b'
+ asdf
+ Xpath 'c'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_6()
+ let test =<< trim [CODE]
+ let p = 1
+ while p
+ let p = 0
+ try
+ Xpath 'a'
+ continue
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ endtry
+ endwhile
+ Xpath 'c'
+ asdf
+ Xpath 'd'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcd', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_error_after_try_7()
+ let test =<< trim [CODE]
+ let p = 1
+ while p
+ let p = 0
+ try
+ Xpath 'a'
+ finally
+ Xpath 'b'
+ continue
+ call assert_report('should not get here')
+ endtry
+ endwhile
+ Xpath 'c'
+ asdf
+ Xpath 'd'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcd', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 21: :finally for :try after :continue/:break/:return/:finish {{{1
+"
+" If a :try conditional stays inactive due to a preceding :continue,
+" :break, :return, or :finish, its :finally clause should not be
+" executed.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_loop_ctrl_statement()
+ let test =<< trim [CODE]
+ func F()
+ let loops = 2
+ while loops > 0
+ XloopNEXT
+ let loops = loops - 1
+ try
+ if loops == 1
+ Xloop 'a'
+ continue
+ call assert_report('should not get here')
+ elseif loops == 0
+ Xloop 'b'
+ break
+ call assert_report('should not get here')
+ endif
+
+ try " inactive
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ finally
+ Xloop 'c'
+ endtry
+ call assert_report('should not get here')
+ endwhile
+
+ try
+ Xpath 'd'
+ return
+ call assert_report('should not get here')
+ try " inactive
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ finally
+ Xpath 'e'
+ endtry
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ Xpath 'f'
+ call F()
+ Xpath 'g'
+ finish
+ call assert_report('should not get here')
+ try " inactive
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ finally
+ Xpath 'h'
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('fa2c2b3c3degh', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 22: :finally for a :try after an error/interrupt/:throw {{{1
+"
+" If a :try conditional stays inactive due to a preceding error or
+" interrupt or :throw, its :finally clause should not be executed.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_error_in_func()
+ let test =<< trim [CODE]
+ func Error()
+ try
+ Xpath 'b'
+ asdf " aborting error, triggering error exception
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endfunc
+
+ Xpath 'a'
+ call Error()
+ call assert_report('should not get here')
+
+ if 1 " not active due to error
+ try " not active since :if inactive
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ endif
+
+ try " not active due to error
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ab', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_finally_after_interrupt()
+ let test =<< trim [CODE]
+ func Interrupt()
+ try
+ Xpath 'a'
+ call interrupt() " triggering interrupt exception
+ call assert_report('should not get here')
+ endtry
+ endfunc
+
+ Xpath 'b'
+ try
+ call Interrupt()
+ catch /^Vim:Interrupt$/
+ Xpath 'c'
+ finish
+ endtry
+ call assert_report('should not get here')
+
+ if 1 " not active due to interrupt
+ try " not active since :if inactive
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ endif
+
+ try " not active due to interrupt
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('bac', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_finally_after_throw()
+ let test =<< trim [CODE]
+ func Throw()
+ Xpath 'a'
+ throw 'xyz'
+ endfunc
+
+ Xpath 'b'
+ call Throw()
+ call assert_report('should not get here')
+
+ if 1 " not active due to :throw
+ try " not active since :if inactive
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ endif
+
+ try " not active due to :throw
+ call assert_report('should not get here')
+ finally
+ call assert_report('should not get here')
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ba', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 23: :catch clauses for a :try after a :throw {{{1
+"
+" If a :try conditional stays inactive due to a preceding :throw,
+" none of its :catch clauses should be executed.
+"-------------------------------------------------------------------------------
+
+func Test_catch_after_throw()
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ throw "xyz"
+ call assert_report('should not get here')
+
+ if 1 " not active due to :throw
+ try " not active since :if inactive
+ call assert_report('should not get here')
+ catch /xyz/
+ call assert_report('should not get here')
+ endtry
+ endif
+ catch /xyz/
+ Xpath 'b'
+ endtry
+
+ Xpath 'c'
+ throw "abc"
+ call assert_report('should not get here')
+
+ try " not active due to :throw
+ call assert_report('should not get here')
+ catch /abc/
+ call assert_report('should not get here')
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 24: :endtry for a :try after a :throw {{{1
+"
+" If a :try conditional stays inactive due to a preceding :throw,
+" its :endtry should not rethrow the exception to the next surrounding
+" active :try conditional.
+"-------------------------------------------------------------------------------
+
+func Test_endtry_after_throw()
+ let test =<< trim [CODE]
+ try " try 1
+ try " try 2
+ Xpath 'a'
+ throw "xyz" " makes try 2 inactive
+ call assert_report('should not get here')
+
+ try " try 3
+ call assert_report('should not get here')
+ endtry " no rethrow to try 1
+ catch /xyz/ " should catch although try 2 inactive
+ Xpath 'b'
+ endtry
+ catch /xyz/ " try 1 active, but exception already caught
+ call assert_report('should not get here')
+ endtry
+ Xpath 'c'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 27: Executing :finally clauses after :return {{{1
+"
+" For a :return command dynamically enclosed in a :try/:endtry region,
+" :finally clauses are executed and the called function is ended.
+"-------------------------------------------------------------------------------
+
+func T27_F()
+ try
+ Xpath 'a'
+ try
+ Xpath 'b'
+ return
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ endtry
+ Xpath 'd'
+ finally
+ Xpath 'e'
+ endtry
+ call assert_report('should not get here')
+endfunc
+
+func T27_G()
+ try
+ Xpath 'f'
+ return
+ call assert_report('should not get here')
+ finally
+ Xpath 'g'
+ call T27_F()
+ Xpath 'h'
+ endtry
+ call assert_report('should not get here')
+endfunc
+
+func T27_H()
+ try
+ Xpath 'i'
+ call T27_G()
+ Xpath 'j'
+ finally
+ Xpath 'k'
+ return
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+endfunction
+
+func Test_finally_after_return()
+ XpathINIT
+ try
+ Xpath 'l'
+ call T27_H()
+ Xpath 'm'
+ finally
+ Xpath 'n'
+ endtry
+ call assert_equal('lifgabcehjkmn', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 28: Executing :finally clauses after :finish {{{1
+"
+" For a :finish command dynamically enclosed in a :try/:endtry region,
+" :finally clauses are executed and the sourced file is finished.
+"
+" This test executes the bodies of the functions F, G, and H from the
+" previous test as script files (:return replaced by :finish).
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_finish()
+ XpathINIT
+
+ let scriptF = MakeScript("T27_F")
+ let scriptG = MakeScript("T27_G", scriptF)
+ let scriptH = MakeScript("T27_H", scriptG)
+
+ try
+ Xpath 'A'
+ exec "source" scriptH
+ Xpath 'B'
+ finally
+ Xpath 'C'
+ endtry
+ Xpath 'D'
+ call assert_equal('AifgabcehjkBCD', g:Xpath)
+ call delete(scriptF)
+ call delete(scriptG)
+ call delete(scriptH)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 29: Executing :finally clauses on errors {{{1
+"
+" After an error in a command dynamically enclosed in a :try/:endtry
+" region, :finally clauses are executed and the script processing is
+" terminated.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_error_1()
+ let test =<< trim [CODE]
+ func F()
+ while 1
+ try
+ Xpath 'a'
+ while 1
+ try
+ Xpath 'b'
+ asdf " error
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ break
+ endwhile
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ endtry | call assert_report('should not get here')
+ call assert_report('should not get here')
+ break
+ endwhile
+ call assert_report('should not get here')
+ endfunc
+
+ while 1
+ try
+ Xpath 'e'
+ while 1
+ call F()
+ call assert_report('should not get here')
+ break
+ endwhile | call assert_report('should not get here')
+ call assert_report('should not get here')
+ finally
+ Xpath 'f'
+ endtry | call assert_report('should not get here')
+ endwhile | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('eabcdf', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_finally_after_error_2()
+ let test =<< trim [CODE]
+ func G() abort
+ if 1
+ try
+ Xpath 'a'
+ asdf " error
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ endtry | Xpath 'c'
+ endif | Xpath 'd'
+ call assert_report('should not get here')
+ endfunc
+
+ if 1
+ try
+ Xpath 'e'
+ call G()
+ call assert_report('should not get here')
+ finally
+ Xpath 'f'
+ endtry | call assert_report('should not get here')
+ endif | call assert_report('should not get here')
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('eabf', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 30: Executing :finally clauses on interrupt {{{1
+"
+" After an interrupt in a command dynamically enclosed in
+" a :try/:endtry region, :finally clauses are executed and the
+" script processing is terminated.
+"-------------------------------------------------------------------------------
+
+func Test_finally_on_interrupt()
+ let test =<< trim [CODE]
+ func F()
+ try
+ Xloop 'a'
+ call interrupt()
+ call assert_report('should not get here')
+ finally
+ Xloop 'b'
+ endtry
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ try
+ Xpath 'c'
+ try
+ Xpath 'd'
+ call interrupt()
+ call assert_report('should not get here')
+ finally
+ Xpath 'e'
+ try
+ Xpath 'f'
+ try
+ Xpath 'g'
+ finally
+ Xpath 'h'
+ try
+ Xpath 'i'
+ call interrupt()
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ finally
+ Xpath 'j'
+ try
+ Xpath 'k'
+ call F()
+ call assert_report('should not get here')
+ finally
+ Xpath 'l'
+ try
+ Xpath 'm'
+ XloopNEXT
+ ExecAsScript F
+ call assert_report('should not get here')
+ finally
+ Xpath 'n'
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'o'
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('cdefghijka1b1lma2b2no', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 31: Executing :finally clauses after :throw {{{1
+"
+" After a :throw dynamically enclosed in a :try/:endtry region,
+" :finally clauses are executed and the script processing is
+" terminated.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_throw_2()
+ let test =<< trim [CODE]
+ func F()
+ try
+ Xloop 'a'
+ throw "exception"
+ call assert_report('should not get here')
+ finally
+ Xloop 'b'
+ endtry
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ Xpath 'c'
+ try
+ Xpath 'd'
+ throw "exception"
+ call assert_report('should not get here')
+ finally
+ Xpath 'e'
+ try
+ Xpath 'f'
+ try
+ Xpath 'g'
+ finally
+ Xpath 'h'
+ try
+ Xpath 'i'
+ throw "exception"
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ finally
+ Xpath 'j'
+ try
+ Xpath 'k'
+ call F()
+ call assert_report('should not get here')
+ finally
+ Xpath 'l'
+ try
+ Xpath 'm'
+ XloopNEXT
+ ExecAsScript F
+ call assert_report('should not get here')
+ finally
+ Xpath 'n'
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('cdefghijka1b1lma2b2n', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 34: :finally reason discarded by :continue {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by a :continue in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_after_continue()
+ let test =<< trim [CODE]
+ func C(jump)
+ XloopNEXT
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "return" || a:jump == "finish"
+ return
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ continue " discards jump that caused the :finally
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ elseif loop == 2
+ Xloop 'a'
+ endif
+ endwhile
+ endfunc
+
+ call C("continue")
+ Xpath 'b'
+ call C("break")
+ Xpath 'c'
+ call C("return")
+ Xpath 'd'
+ let g:jump = "finish"
+ ExecAsScript C
+ unlet g:jump
+ Xpath 'e'
+ try
+ call C("error")
+ Xpath 'f'
+ finally
+ Xpath 'g'
+ try
+ call C("interrupt")
+ Xpath 'h'
+ finally
+ Xpath 'i'
+ call C("throw")
+ Xpath 'j'
+ endtry
+ endtry
+ Xpath 'k'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 35: :finally reason discarded by :break {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by a :break in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_break()
+ let test =<< trim [CODE]
+ func B(jump)
+ XloopNEXT
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "return" || a:jump == "finish"
+ return
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ break " discards jump that caused the :finally
+ call assert_report('should not get here')
+ endtry
+ elseif loop == 2
+ call assert_report('should not get here')
+ endif
+ endwhile
+ Xloop 'a'
+ endfunc
+
+ call B("continue")
+ Xpath 'b'
+ call B("break")
+ Xpath 'c'
+ call B("return")
+ Xpath 'd'
+ let g:jump = "finish"
+ ExecAsScript B
+ unlet g:jump
+ Xpath 'e'
+ try
+ call B("error")
+ Xpath 'f'
+ finally
+ Xpath 'g'
+ try
+ call B("interrupt")
+ Xpath 'h'
+ finally
+ Xpath 'i'
+ call B("throw")
+ Xpath 'j'
+ endtry
+ endtry
+ Xpath 'k'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 36: :finally reason discarded by :return {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by a :return in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_return()
+ let test =<< trim [CODE]
+ func R(jump, retval) abort
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "return"
+ return
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ return a:retval " discards jump that caused the :finally
+ call assert_report('should not get here')
+ endtry
+ elseif loop == 2
+ call assert_report('should not get here')
+ endif
+ endwhile
+ call assert_report('should not get here')
+ endfunc
+
+ let sum = -R("continue", -8)
+ Xpath 'a'
+ let sum = sum - R("break", -16)
+ Xpath 'b'
+ let sum = sum - R("return", -32)
+ Xpath 'c'
+ try
+ let sum = sum - R("error", -64)
+ Xpath 'd'
+ finally
+ Xpath 'e'
+ try
+ let sum = sum - R("interrupt", -128)
+ Xpath 'f'
+ finally
+ Xpath 'g'
+ let sum = sum - R("throw", -256)
+ Xpath 'h'
+ endtry
+ endtry
+ Xpath 'i'
+
+ let expected = 8 + 16 + 32 + 64 + 128 + 256
+ call assert_equal(sum, expected)
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefghi', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 37: :finally reason discarded by :finish {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by a :finish in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_finish()
+ let test =<< trim [CODE]
+ func F(jump) " not executed as function, transformed to a script
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "finish"
+ finish
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ finish " discards jump that caused the :finally
+ call assert_report('should not get here')
+ endtry
+ elseif loop == 2
+ call assert_report('should not get here')
+ endif
+ endwhile
+ call assert_report('should not get here')
+ endfunc
+
+ let scriptF = MakeScript("F")
+ delfunction F
+
+ let g:jump = "continue"
+ exec "source" scriptF
+ Xpath 'a'
+ let g:jump = "break"
+ exec "source" scriptF
+ Xpath 'b'
+ let g:jump = "finish"
+ exec "source" scriptF
+ Xpath 'c'
+ try
+ let g:jump = "error"
+ exec "source" scriptF
+ Xpath 'd'
+ finally
+ Xpath 'e'
+ try
+ let g:jump = "interrupt"
+ exec "source" scriptF
+ Xpath 'f'
+ finally
+ Xpath 'g'
+ try
+ let g:jump = "throw"
+ exec "source" scriptF
+ Xpath 'h'
+ finally
+ Xpath 'i'
+ endtry
+ endtry
+ endtry
+ unlet g:jump
+ call delete(scriptF)
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefghi', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 38: :finally reason discarded by an error {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by an error in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_error()
+ let test =<< trim [CODE]
+ func E(jump)
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "return" || a:jump == "finish"
+ return
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ asdf " error; discards jump that caused the :finally
+ endtry
+ elseif loop == 2
+ call assert_report('should not get here')
+ endif
+ endwhile
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ Xpath 'a'
+ call E("continue")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'b'
+ call E("break")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'c'
+ call E("return")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'd'
+ let g:jump = "finish"
+ ExecAsScript E
+ call assert_report('should not get here')
+ finally
+ unlet g:jump
+ try
+ Xpath 'e'
+ call E("error")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'f'
+ call E("interrupt")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'g'
+ call E("throw")
+ call assert_report('should not get here')
+ finally
+ Xpath 'h'
+ delfunction E
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefgh', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 39: :finally reason discarded by an interrupt {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by an interrupt in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discarded_by_interrupt()
+ let test =<< trim [CODE]
+ func I(jump)
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "return" || a:jump == "finish"
+ return
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ call interrupt()
+ let dummy = 0
+ endtry
+ elseif loop == 2
+ call assert_report('should not get here')
+ endif
+ endwhile
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ try
+ Xpath 'a'
+ call I("continue")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'b'
+ call I("break")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'c'
+ call I("return")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'd'
+ let g:jump = "finish"
+ ExecAsScript I
+ call assert_report('should not get here')
+ finally
+ unlet g:jump
+ try
+ Xpath 'e'
+ call I("error")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'f'
+ call I("interrupt")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'g'
+ call I("throw")
+ call assert_report('should not get here')
+ finally
+ Xpath 'h'
+ delfunction I
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'A'
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefghA', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 40: :finally reason discarded by :throw {{{1
+"
+" When a :finally clause is executed due to a :continue, :break,
+" :return, :finish, error, interrupt or :throw, the jump reason is
+" discarded by a :throw in the finally clause.
+"-------------------------------------------------------------------------------
+
+func Test_finally_discard_by_throw()
+ let test =<< trim [CODE]
+ func T(jump)
+ let loop = 0
+ while loop < 2
+ let loop = loop + 1
+ if loop == 1
+ try
+ if a:jump == "continue"
+ continue
+ elseif a:jump == "break"
+ break
+ elseif a:jump == "return" || a:jump == "finish"
+ return
+ elseif a:jump == "error"
+ asdf
+ elseif a:jump == "interrupt"
+ call interrupt()
+ let dummy = 0
+ elseif a:jump == "throw"
+ throw "abc"
+ endif
+ finally
+ throw "xyz" " discards jump that caused the :finally
+ endtry
+ elseif loop == 2
+ call assert_report('should not get here')
+ endif
+ endwhile
+ call assert_report('should not get here')
+ endfunc
+
+ try
+ Xpath 'a'
+ call T("continue")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'b'
+ call T("break")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'c'
+ call T("return")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'd'
+ let g:jump = "finish"
+ ExecAsScript T
+ call assert_report('should not get here')
+ finally
+ unlet g:jump
+ try
+ Xpath 'e'
+ call T("error")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'f'
+ call T("interrupt")
+ call assert_report('should not get here')
+ finally
+ try
+ Xpath 'g'
+ call T("throw")
+ call assert_report('should not get here')
+ finally
+ Xpath 'h'
+ delfunction T
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefgh', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 49: Throwing exceptions across functions {{{1
+"
+" When an exception is thrown but not caught inside a function, the
+" caller is checked for a matching :catch clause.
+"-------------------------------------------------------------------------------
+
+func T49_C()
+ try
+ Xpath 'a'
+ throw "arrgh"
+ call assert_report('should not get here')
+ catch /arrgh/
+ Xpath 'b'
+ endtry
+ Xpath 'c'
+endfunc
+
+func T49_T1()
+ XloopNEXT
+ try
+ Xloop 'd'
+ throw "arrgh"
+ call assert_report('should not get here')
+ finally
+ Xloop 'e'
+ endtry
+ Xloop 'f'
+endfunc
+
+func T49_T2()
+ try
+ Xpath 'g'
+ call T49_T1()
+ call assert_report('should not get here')
+ finally
+ Xpath 'h'
+ endtry
+ call assert_report('should not get here')
+endfunc
+
+func Test_throw_exception_across_funcs()
+ XpathINIT
+ XloopINIT
+ try
+ Xpath 'i'
+ call T49_C() " throw and catch
+ Xpath 'j'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ try
+ Xpath 'k'
+ call T49_T1() " throw, one level
+ call assert_report('should not get here')
+ catch /arrgh/
+ Xpath 'l'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ try
+ Xpath 'm'
+ call T49_T2() " throw, two levels
+ call assert_report('should not get here')
+ catch /arrgh/
+ Xpath 'n'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ Xpath 'o'
+
+ call assert_equal('iabcjkd2e2lmgd3e3hno', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 50: Throwing exceptions across script files {{{1
+"
+" When an exception is thrown but not caught inside a script file,
+" the sourcing script or function is checked for a matching :catch
+" clause.
+"
+" This test executes the bodies of the functions C, T1, and T2 from
+" the previous test as script files (:return replaced by :finish).
"-------------------------------------------------------------------------------
+
+func T50_F()
+ try
+ Xpath 'A'
+ exec "source" g:scriptC
+ Xpath 'B'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ try
+ Xpath 'C'
+ exec "source" g:scriptT1
+ call assert_report('should not get here')
+ catch /arrgh/
+ Xpath 'D'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+endfunc
+
+func Test_throw_across_script()
+ XpathINIT
+ XloopINIT
+ let g:scriptC = MakeScript("T49_C")
+ let g:scriptT1 = MakeScript("T49_T1")
+ let scriptT2 = MakeScript("T49_T2", g:scriptT1)
+
+ try
+ Xpath 'E'
+ call T50_F()
+ Xpath 'F'
+ exec "source" scriptT2
+ call assert_report('should not get here')
+ catch /arrgh/
+ Xpath 'G'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ Xpath 'H'
+ call assert_equal('EAabcBCd2e2DFgd3e3hGH', g:Xpath)
+
+ call delete(g:scriptC)
+ call delete(g:scriptT1)
+ call delete(scriptT2)
+ unlet g:scriptC g:scriptT1 scriptT2
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 52: Uncaught exceptions {{{1
+"
+" When an exception is thrown but not caught, an error message is
+" displayed when the script is terminated. In case of an interrupt
+" or error exception, the normal interrupt or error message(s) are
+" displayed.
+"-------------------------------------------------------------------------------
+
+func Test_uncaught_exception_1()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ Xpath 'a'
+ throw "arrgh"
+ call assert_report('should not get here')`
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('E605: Exception not caught: arrgh', v:errmsg)
+ call assert_equal('a', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_2()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ throw "oops"
+ call assert_report('should not get here')`
+ catch /arrgh/
+ call assert_report('should not get here')`
+ endtry
+ call assert_report('should not get here')`
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('E605: Exception not caught: oops', v:errmsg)
+ call assert_equal('a', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_3()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ func T()
+ Xpath 'c'
+ throw "brrr"
+ call assert_report('should not get here')`
+ endfunc
+
+ try
+ Xpath 'a'
+ throw "arrgh"
+ call assert_report('should not get here')`
+ catch /.*/
+ Xpath 'b'
+ call T()
+ call assert_report('should not get here')`
+ endtry
+ call assert_report('should not get here')`
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('E605: Exception not caught: brrr', v:errmsg)
+ call assert_equal('abc', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_4()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ throw "arrgh"
+ call assert_report('should not get here')`
+ finally
+ Xpath 'b'
+ throw "brrr"
+ call assert_report('should not get here')`
+ endtry
+ call assert_report('should not get here')`
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('E605: Exception not caught: brrr', v:errmsg)
+ call assert_equal('ab', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_5()
+ CheckEnglish
+
+ " Need to catch and handle interrupt, otherwise the test will wait for the
+ " user to press <Enter> to continue
+ let test =<< trim [CODE]
+ try
+ try
+ Xpath 'a'
+ call interrupt()
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'b'
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ab', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_6()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ let x = novar " error E121; exception: E121
+ catch /E15:/ " should not catch
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a', g:Xpath)
+ call assert_equal('E121: Undefined variable: novar', v:errmsg)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+func Test_uncaught_exception_7()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ " error E108/E488; exception: E488
+ unlet novar #
+ catch /E108:/ " should not catch
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('a', g:Xpath)
+ call assert_equal('E488: Trailing characters: #', v:errmsg)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 53: Nesting errors: :endif/:else/:elseif {{{1
+"
+" For nesting errors of :if conditionals the correct error messages
+" should be given.
+"-------------------------------------------------------------------------------
+
+func Test_nested_if_else_errors()
+ CheckEnglish
+
+ " :endif without :if
+ let code =<< trim END
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+ " :endif without :if
+ let code =<< trim END
+ while 1
+ endif
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+ " :endif without :if
+ let code =<< trim END
+ try
+ finally
+ endif
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+ " :endif without :if
+ let code =<< trim END
+ try
+ endif
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+ " :endif without :if
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ endif
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')
+
+ " :else without :if
+ let code =<< trim END
+ else
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+ " :else without :if
+ let code =<< trim END
+ while 1
+ else
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+ " :else without :if
+ let code =<< trim END
+ try
+ finally
+ else
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+ " :else without :if
+ let code =<< trim END
+ try
+ else
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+ " :else without :if
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ else
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')
+
+ " :elseif without :if
+ let code =<< trim END
+ elseif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+ " :elseif without :if
+ let code =<< trim END
+ while 1
+ elseif
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+ " :elseif without :if
+ let code =<< trim END
+ try
+ finally
+ elseif
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+ " :elseif without :if
+ let code =<< trim END
+ try
+ elseif
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+ " :elseif without :if
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ elseif
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')
+
+ " multiple :else
+ let code =<< trim END
+ if 1
+ else
+ else
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(else):E583: multiple :else')
+
+ " :elseif after :else
+ let code =<< trim END
+ if 1
+ else
+ elseif 1
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(elseif):E584: :elseif after :else')
+
+ call delete('Xtest')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 54: Nesting errors: :while/:endwhile {{{1
+"
+" For nesting errors of :while conditionals the correct error messages
+" should be given.
+"
+" This test reuses the function MESSAGES() from the previous test.
+" This functions checks the messages in g:msgfile.
+"-------------------------------------------------------------------------------
+
+func Test_nested_while_error()
+ CheckEnglish
+
+ " :endwhile without :while
+ let code =<< trim END
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ " :endwhile without :while
+ let code =<< trim END
+ if 1
+ endwhile
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ " Missing :endif
+ let code =<< trim END
+ while 1
+ if 1
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif')
+
+ " :endwhile without :while
+ let code =<< trim END
+ try
+ finally
+ endwhile
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ " Missing :endtry
+ let code =<< trim END
+ while 1
+ try
+ finally
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry')
+
+ " Missing :endtry
+ let code =<< trim END
+ while 1
+ if 1
+ try
+ finally
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry')
+
+ " Missing :endif
+ let code =<< trim END
+ while 1
+ try
+ finally
+ if 1
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif')
+
+ " :endwhile without :while
+ let code =<< trim END
+ try
+ endwhile
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ " :endwhile without :while
+ let code =<< trim END
+ while 1
+ try
+ endwhile
+ endtry
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ " :endwhile without :while
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ endwhile
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ " :endwhile without :while
+ let code =<< trim END
+ while 1
+ try
+ throw "a"
+ catch /a/
+ endwhile
+ endtry
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
+
+ call delete('Xtest')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 55: Nesting errors: :continue/:break {{{1
+"
+" For nesting errors of :continue and :break commands the correct
+" error messages should be given.
+"
+" This test reuses the function MESSAGES() from the previous test.
+" This functions checks the messages in g:msgfile.
+"-------------------------------------------------------------------------------
+
+func Test_nested_cont_break_error()
+ CheckEnglish
+
+ " :continue without :while
+ let code =<< trim END
+ continue
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+ " :continue without :while
+ let code =<< trim END
+ if 1
+ continue
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+ " :continue without :while
+ let code =<< trim END
+ try
+ finally
+ continue
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+ " :continue without :while
+ let code =<< trim END
+ try
+ continue
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+ " :continue without :while
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ continue
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')
+
+ " :break without :while
+ let code =<< trim END
+ break
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+ " :break without :while
+ let code =<< trim END
+ if 1
+ break
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+ " :break without :while
+ let code =<< trim END
+ try
+ finally
+ break
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+ " :break without :while
+ let code =<< trim END
+ try
+ break
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+ " :break without :while
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ break
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
+
+ call delete('Xtest')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 56: Nesting errors: :endtry {{{1
+"
+" For nesting errors of :try conditionals the correct error messages
+" should be given.
+"
+" This test reuses the function MESSAGES() from the previous test.
+" This functions checks the messages in g:msgfile.
+"-------------------------------------------------------------------------------
+
+func Test_nested_endtry_error()
+ CheckEnglish
+
+ " :endtry without :try
+ let code =<< trim END
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')
+
+ " :endtry without :try
+ let code =<< trim END
+ if 1
+ endtry
+ endif
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')
+
+ " :endtry without :try
+ let code =<< trim END
+ while 1
+ endtry
+ endwhile
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')
+
+ " Missing :endif
+ let code =<< trim END
+ try
+ if 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')
+
+ " Missing :endwhile
+ let code =<< trim END
+ try
+ while 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')
+
+ " Missing :endif
+ let code =<< trim END
+ try
+ finally
+ if 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')
+
+ " Missing :endwhile
+ let code =<< trim END
+ try
+ finally
+ while 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')
+
+ " Missing :endif
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ if 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')
+
+ " Missing :endwhile
+ let code =<< trim END
+ try
+ throw "a"
+ catch /a/
+ while 1
+ endtry
+ END
+ call writefile(code, 'Xtest')
+ call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')
+
+ call delete('Xtest')
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 57: v:exception and v:throwpoint for user exceptions {{{1
+"
+" v:exception evaluates to the value of the exception that was caught
+" most recently and is not finished. (A caught exception is finished
+" when the next ":catch", ":finally", or ":endtry" is reached.)
+" v:throwpoint evaluates to the script/function name and line number
+" where that exception has been thrown.
+"-------------------------------------------------------------------------------
+
+func Test_user_exception_info()
+ CheckEnglish
+
+ XpathINIT
+ XloopINIT
+
+ func FuncException()
+ let g:exception = v:exception
+ endfunc
+
+ func FuncThrowpoint()
+ let g:throwpoint = v:throwpoint
+ endfunc
+
+ let scriptException = MakeScript("FuncException")
+ let scriptThrowPoint = MakeScript("FuncThrowpoint")
+
+ command! CmdException let g:exception = v:exception
+ command! CmdThrowpoint let g:throwpoint = v:throwpoint
+
+ func T(arg, line)
+ if a:line == 2
+ throw a:arg " in line 2
+ elseif a:line == 4
+ throw a:arg " in line 4
+ elseif a:line == 6
+ throw a:arg " in line 6
+ elseif a:line == 8
+ throw a:arg " in line 8
+ endif
+ endfunc
+
+ func G(arg, line)
+ call T(a:arg, a:line)
+ endfunc
+
+ func F(arg, line)
+ call G(a:arg, a:line)
+ endfunc
+
+ let scriptT = MakeScript("T")
+ let scriptG = MakeScript("G", scriptT)
+ let scriptF = MakeScript("F", scriptG)
+
+ try
+ Xpath 'a'
+ call F("oops", 2)
+ catch /.*/
+ Xpath 'b'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+
+ exec "let exception = v:exception"
+ exec "let throwpoint = v:throwpoint"
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+
+ CmdException
+ CmdThrowpoint
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+
+ call FuncException()
+ call FuncThrowpoint()
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+
+ exec "source" scriptException
+ exec "source" scriptThrowPoint
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+
+ try
+ Xpath 'c'
+ call G("arrgh", 4)
+ catch /.*/
+ Xpath 'd'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("arrgh", v:exception)
+ call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<4\>', v:throwpoint)
+
+ try
+ Xpath 'e'
+ let g:arg = "autsch"
+ let g:line = 6
+ exec "source" scriptF
+ catch /.*/
+ Xpath 'f'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("autsch", v:exception)
+ call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint)
+ call assert_match('\<6\>', v:throwpoint)
+ finally
+ Xpath 'g'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("arrgh", v:exception)
+ call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<4\>', v:throwpoint)
+ try
+ Xpath 'h'
+ let g:arg = "brrrr"
+ let g:line = 8
+ exec "source" scriptG
+ catch /.*/
+ Xpath 'i'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ " Resolve scriptT for matching it against v:throwpoint.
+ call assert_equal("brrrr", v:exception)
+ call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint)
+ call assert_match('\<8\>', v:throwpoint)
+ finally
+ Xpath 'j'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("arrgh", v:exception)
+ call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<4\>', v:throwpoint)
+ endtry
+ Xpath 'k'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("arrgh", v:exception)
+ call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<4\>', v:throwpoint)
+ endtry
+ Xpath 'l'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("arrgh", v:exception)
+ call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<4\>', v:throwpoint)
+ finally
+ Xpath 'm'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+ endtry
+ Xpath 'n'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("oops", v:exception)
+ call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
+ call assert_match('\<2\>', v:throwpoint)
+ finally
+ Xpath 'o'
+ let exception = v:exception
+ let throwpoint = v:throwpoint
+ call assert_equal("", v:exception)
+ call assert_match('^$', v:throwpoint)
+ call assert_match('^$', v:throwpoint)
+ endtry
+
+ call assert_equal('abcdefghijklmno', g:Xpath)
+
+ unlet exception throwpoint
+ delfunction FuncException
+ delfunction FuncThrowpoint
+ call delete(scriptException)
+ call delete(scriptThrowPoint)
+ unlet scriptException scriptThrowPoint
+ delcommand CmdException
+ delcommand CmdThrowpoint
+ delfunction T
+ delfunction G
+ delfunction F
+ call delete(scriptT)
+ call delete(scriptG)
+ call delete(scriptF)
+ unlet scriptT scriptG scriptF
+endfunc
+
+"-------------------------------------------------------------------------------
+"
+" Test 58: v:exception and v:throwpoint for error/interrupt exceptions {{{1
+"
+" v:exception and v:throwpoint work also for error and interrupt
+" exceptions.
+"-------------------------------------------------------------------------------
+
+func Test_execption_info_for_error()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ func T(line)
+ if a:line == 2
+ delfunction T " error (function in use) in line 2
+ elseif a:line == 4
+ call interrupt()
+ endif
+ endfunc
+
+ while 1
+ try
+ Xpath 'a'
+ call T(2)
+ call assert_report('should not get here')
+ catch /.*/
+ Xpath 'b'
+ if v:exception !~ 'Vim(delfunction):'
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint !~ '\<T\>'
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint !~ '\<2\>'
+ call assert_report('should not get here')
+ endif
+ finally
+ Xpath 'c'
+ if v:exception != ""
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint != ""
+ call assert_report('should not get here')
+ endif
+ break
+ endtry
+ endwhile
+
+ Xpath 'd'
+ if v:exception != ""
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint != ""
+ call assert_report('should not get here')
+ endif
+
+ while 1
+ try
+ Xpath 'e'
+ call T(4)
+ call assert_report('should not get here')
+ catch /.*/
+ Xpath 'f'
+ if v:exception != 'Vim:Interrupt'
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint !~ 'function T'
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint !~ '\<4\>'
+ call assert_report('should not get here')
+ endif
+ finally
+ Xpath 'g'
+ if v:exception != ""
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint != ""
+ call assert_report('should not get here')
+ endif
+ break
+ endtry
+ endwhile
+
+ Xpath 'h'
+ if v:exception != ""
+ call assert_report('should not get here')
+ endif
+ if v:throwpoint != ""
+ call assert_report('should not get here')
+ endif
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefgh', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+"
+" Test 59: v:exception and v:throwpoint when discarding exceptions {{{1
+"
+" When a :catch clause is left by a ":break" etc or an error or
+" interrupt exception, v:exception and v:throwpoint are reset. They
+" are not affected by an exception that is discarded before being
+" caught.
+"-------------------------------------------------------------------------------
+func Test_exception_info_on_discard()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ let sfile = expand("<sfile>")
+
+ while 1
+ try
+ throw "x1"
+ catch /.*/
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ while 1
+ try
+ throw "x2"
+ catch /.*/
+ break
+ finally
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+ endtry
+ break
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ while 1
+ try
+ let errcaught = 0
+ try
+ try
+ throw "x3"
+ catch /.*/
+ let lnum = expand("<sflnum>")
+ asdf
+ endtry
+ catch /.*/
+ let errcaught = 1
+ call assert_match('Vim:E492: Not an editor command:', v:exception)
+ call assert_match('line ' .. (lnum + 1), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, errcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'a'
+
+ while 1
+ try
+ let intcaught = 0
+ try
+ try
+ throw "x4"
+ catch /.*/
+ let lnum = expand("<sflnum>")
+ call interrupt()
+ endtry
+ catch /.*/
+ let intcaught = 1
+ call assert_match('Vim:Interrupt', v:exception)
+ call assert_match('line ' .. (lnum + 1), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, intcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'b'
+
+ while 1
+ try
+ let errcaught = 0
+ try
+ try
+ if 1
+ let lnum = expand("<sflnum>")
+ throw "x5"
+ " missing endif
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ catch /.*/
+ let errcaught = 1
+ call assert_match('Vim(catch):E171: Missing :endif:', v:exception)
+ call assert_match('line ' .. (lnum + 3), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, errcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'c'
+
+ try
+ while 1
+ try
+ throw "x6"
+ finally
+ break
+ endtry
+ break
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ try
+ while 1
+ try
+ throw "x7"
+ finally
+ break
+ endtry
+ break
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+ endtry
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ while 1
+ try
+ let errcaught = 0
+ try
+ try
+ throw "x8"
+ finally
+ let lnum = expand("<sflnum>")
+ asdf
+ endtry
+ catch /.*/
+ let errcaught = 1
+ call assert_match('Vim:E492: Not an editor command:', v:exception)
+ call assert_match('line ' .. (lnum + 1), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, errcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'd'
+
+ while 1
+ try
+ let intcaught = 0
+ try
+ try
+ throw "x9"
+ finally
+ let lnum = expand("<sflnum>")
+ call interrupt()
+ endtry
+ catch /.*/
+ let intcaught = 1
+ call assert_match('Vim:Interrupt', v:exception)
+ call assert_match('line ' .. (lnum + 1), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, intcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'e'
+
+ while 1
+ try
+ let errcaught = 0
+ try
+ try
+ if 1
+ let lnum = expand("<sflnum>")
+ throw "x10"
+ " missing endif
+ finally
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+ endtry
+ catch /.*/
+ let errcaught = 1
+ call assert_match('Vim(finally):E171: Missing :endif:', v:exception)
+ call assert_match('line ' .. (lnum + 3), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, errcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'f'
+
+ while 1
+ try
+ let errcaught = 0
+ try
+ try
+ if 1
+ let lnum = expand("<sflnum>")
+ throw "x11"
+ " missing endif
+ endtry
+ catch /.*/
+ let errcaught = 1
+ call assert_match('Vim(endtry):E171: Missing :endif:', v:exception)
+ call assert_match('line ' .. (lnum + 3), v:throwpoint)
+ endtry
+ finally
+ call assert_equal(1, errcaught)
+ break
+ endtry
+ endwhile
+ call assert_equal('', v:exception)
+ call assert_equal('', v:throwpoint)
+
+ Xpath 'g'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefg', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+"
+" Test 60: (Re)throwing v:exception; :echoerr. {{{1
+"
+" A user exception can be rethrown after catching by throwing
+" v:exception. An error or interrupt exception cannot be rethrown
+" because Vim exceptions cannot be faked. A Vim exception using the
+" value of v:exception can, however, be triggered by the :echoerr
+" command.
+"-------------------------------------------------------------------------------
+
+func Test_rethrow_exception_1()
+ XpathINIT
+ try
+ try
+ Xpath 'a'
+ throw "oops"
+ catch /oops/
+ Xpath 'b'
+ throw v:exception " rethrow user exception
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ catch /^oops$/ " catches rethrown user exception
+ Xpath 'c'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_rethrow_exception_2()
+ XpathINIT
+ try
+ let caught = 0
+ try
+ Xpath 'a'
+ write /n/o/n/w/r/i/t/a/b/l/e/_/f/i/l/e
+ call assert_report('should not get here')
+ catch /^Vim(write):/
+ let caught = 1
+ throw v:exception " throw error: cannot fake Vim exception
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ endtry
+ catch /^Vim(throw):/ " catches throw error
+ let caught = caught + 1
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ call assert_equal(2, caught)
+ endtry
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_rethrow_exception_3()
+ XpathINIT
+ try
+ let caught = 0
+ try
+ Xpath 'a'
+ asdf
+ catch /^Vim/ " catch error exception
+ let caught = 1
+ " Trigger Vim error exception with value specified after :echoerr
+ let value = substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
+ echoerr value
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ endtry
+ catch /^Vim(echoerr):/
+ let caught = caught + 1
+ call assert_match(value, v:exception)
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ call assert_equal(2, caught)
+ endtry
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_rethrow_exception_3()
+ XpathINIT
+ try
+ let errcaught = 0
+ try
+ Xpath 'a'
+ let intcaught = 0
+ call interrupt()
+ catch /^Vim:/ " catch interrupt exception
+ let intcaught = 1
+ " Trigger Vim error exception with value specified after :echoerr
+ echoerr substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ call assert_equal(1, intcaught)
+ endtry
+ catch /^Vim(echoerr):/
+ let errcaught = 1
+ call assert_match('Interrupt', v:exception)
+ finally
+ Xpath 'c'
+ call assert_equal(1, errcaught)
+ endtry
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 61: Catching interrupt exceptions {{{1
+"
+" When an interrupt occurs inside a :try/:endtry region, an
+" interrupt exception is thrown and can be caught. Its value is
+" "Vim:Interrupt". If the interrupt occurs after an error or a :throw
+" but before a matching :catch is reached, all following :catches of
+" that try block are ignored, but the interrupt exception can be
+" caught by the next surrounding try conditional. An interrupt is
+" ignored when there is a previous interrupt that has not been caught
+" or causes a :finally clause to be executed.
+"-------------------------------------------------------------------------------
+
+func Test_catch_intr_exception()
+ let test =<< trim [CODE]
+ while 1
+ try
+ try
+ Xpath 'a'
+ call interrupt()
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'b'
+ finally
+ Xpath 'c'
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ try
+ try
+ Xpath 'e'
+ asdf
+ call assert_report('should not get here')
+ catch /do_not_catch/
+ call assert_report('should not get here')
+ catch /.*/
+ Xpath 'f'
+ call interrupt()
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'g'
+ call interrupt()
+ call assert_report('should not get here')
+ endtry
+ catch /^Vim:Interrupt$/
+ Xpath 'h'
+ finally
+ Xpath 'i'
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'j'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ try
+ try
+ Xpath 'k'
+ throw "x"
+ call assert_report('should not get here')
+ catch /do_not_catch/
+ call assert_report('should not get here')
+ catch /x/
+ Xpath 'l'
+ call interrupt()
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ catch /^Vim:Interrupt$/
+ Xpath 'm'
+ finally
+ Xpath 'n'
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'o'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ try
+ Xpath 'p'
+ call interrupt()
+ call assert_report('should not get here')
+ catch /do_not_catch/
+ call interrupt()
+ call assert_report('should not get here')
+ catch /^Vim:Interrupt$/
+ Xpath 'q'
+ finally
+ Xpath 'r'
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 's'
+ break
+ endtry
+ endwhile
+
+ Xpath 't'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefghijklmnopqrst', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 62: Catching error exceptions {{{1
+"
+" An error inside a :try/:endtry region is converted to an exception
+" and can be caught. The error exception has a "Vim(cmdname):" prefix
+" where cmdname is the name of the failing command, or a "Vim:" prefix
+" if no command name is known. The "Vim" prefixes cannot be faked.
+"-------------------------------------------------------------------------------
+
+func Test_catch_err_exception_1()
+ XpathINIT
+ while 1
+ try
+ try
+ let caught = 0
+ unlet novar
+ catch /^Vim(unlet):/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim(unlet):', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match('E108: No such variable: "novar"', v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_catch_err_exception_2()
+ XpathINIT
+ while 1
+ try
+ try
+ let caught = 0
+ throw novar " error in :throw
+ catch /^Vim(throw):/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match('E121: Undefined variable: novar', v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_catch_err_exception_3()
+ XpathINIT
+ while 1
+ try
+ try
+ let caught = 0
+ throw "Vim:faked" " error: cannot fake Vim exception
+ catch /^Vim(throw):/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match("E608: Cannot :throw exceptions with 'Vim' prefix",
+ \ v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abc', g:Xpath)
+endfunc
+
+func Test_catch_err_exception_4()
+ XpathINIT
+ func F()
+ while 1
+ " Missing :endwhile
+ endfunc
+
+ while 1
+ try
+ try
+ let caught = 0
+ call F()
+ catch /^Vim(endfunction):/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim(endfunction):', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match("E170: Missing :endwhile", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abc', g:Xpath)
+ delfunc F
+endfunc
+
+func Test_catch_err_exception_5()
+ XpathINIT
+ func F()
+ while 1
+ " Missing :endwhile
+ endfunc
+
+ while 1
+ try
+ try
+ let caught = 0
+ ExecAsScript F
+ catch /^Vim:/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim:', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match("E170: Missing :endwhile", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abc', g:Xpath)
+ delfunc F
+endfunc
+
+func Test_catch_err_exception_6()
+ XpathINIT
+ func G()
+ call G()
+ endfunc
+
+ while 1
+ try
+ let mfd_save = &mfd
+ set mfd=3
+ try
+ let caught = 0
+ call G()
+ catch /^Vim(call):/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim(call):', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ let &mfd = mfd_save
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abc', g:Xpath)
+ delfunc G
+endfunc
+
+func Test_catch_err_exception_7()
+ XpathINIT
+ func H()
+ return H()
+ endfunc
+
+ while 1
+ try
+ let mfd_save = &mfd
+ set mfd=3
+ try
+ let caught = 0
+ call H()
+ catch /^Vim(return):/
+ Xpath 'a'
+ let caught = 1
+ let v:errmsg = substitute(v:exception, '^Vim(return):', '', "")
+ finally
+ Xpath 'b'
+ call assert_equal(1, caught)
+ call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ let &mfd = mfd_save
+ break " discard error for $VIMNOERRTHROW
+ endtry
+ call assert_report('should not get here')
+ endwhile
+
+ call assert_equal('abc', g:Xpath)
+ delfunc H
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 63: Suppressing error exceptions by :silent!. {{{1
+"
+" A :silent! command inside a :try/:endtry region suppresses the
+" conversion of errors to an exception and the immediate abortion on
+" error. When the commands executed by the :silent! themselves open
+" a new :try/:endtry region, conversion of errors to exception and
+" immediate abortion is switched on again - until the next :silent!
+" etc. The :silent! has the effect of setting v:errmsg to the error
+" message text (without displaying it) and continuing with the next
+" script line.
+"
+" When a command triggering autocommands is executed by :silent!
+" inside a :try/:endtry, the autocommand execution is not suppressed
+" on error.
+"
+" This test reuses the function MSG() from the previous test.
+"-------------------------------------------------------------------------------
+
+func Test_silent_exception()
+ XpathINIT
+ XloopINIT
+ let g:taken = ""
+
+ func S(n) abort
+ XloopNEXT
+ let g:taken = g:taken . "E" . a:n
+ let v:errmsg = ""
+ exec "asdf" . a:n
+
+ " Check that ":silent!" continues:
+ Xloop 'a'
+
+ " Check that ":silent!" sets "v:errmsg":
+ call assert_match("E492: Not an editor command", v:errmsg)
+ endfunc
+
+ func Foo()
+ while 1
+ try
+ try
+ let caught = 0
+ " This is not silent:
+ call S(3)
+ catch /^Vim:/
+ Xpath 'b'
+ let caught = 1
+ let errmsg3 = substitute(v:exception, '^Vim:', '', "")
+ silent! call S(4)
+ finally
+ call assert_equal(1, caught)
+ Xpath 'c'
+ call assert_match("E492: Not an editor command", errmsg3)
+ silent! call S(5)
+ " Break out of try conditionals that cover ":silent!". This also
+ " discards the aborting error when $VIMNOERRTHROW is non-zero.
+ break
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ endwhile
+ " This is a double ":silent!" (see caller).
+ silent! call S(6)
+ endfunc
+
+ func Bar()
+ try
+ silent! call S(2)
+ silent! execute "call Foo() | call S(7)"
+ silent! call S(8)
+ endtry " normal end of try cond that covers ":silent!"
+ " This has a ":silent!" from the caller:
+ call S(9)
+ endfunc
+
+ silent! call S(1)
+ silent! call Bar()
+ silent! call S(10)
+
+ call assert_equal("E1E2E3E4E5E6E7E8E9E10", g:taken)
+
+ augroup TMP
+ au!
+ autocmd BufWritePost * Xpath 'd'
+ augroup END
+
+ Xpath 'e'
+ silent! write /i/m/p/o/s/s/i/b/l/e
+ Xpath 'f'
+
+ call assert_equal('a2a3ba5ca6a7a8a9a10a11edf', g:Xpath)
+
+ augroup TMP
+ au!
+ augroup END
+ augroup! TMP
+ delfunction S
+ delfunction Foo
+ delfunction Bar
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 64: Error exceptions after error, interrupt or :throw {{{1
+"
+" When an error occurs after an interrupt or a :throw but before
+" a matching :catch is reached, all following :catches of that try
+" block are ignored, but the error exception can be caught by the next
+" surrounding try conditional. Any previous error exception is
+" discarded. An error is ignored when there is a previous error that
+" has not been caught.
+"-------------------------------------------------------------------------------
+
+func Test_exception_after_error_1()
+ XpathINIT
+ while 1
+ try
+ try
+ Xpath 'a'
+ let caught = 0
+ while 1
+ if 1
+ " Missing :endif
+ endwhile " throw error exception
+ catch /^Vim(/
+ Xpath 'b'
+ let caught = 1
+ finally
+ Xpath 'c'
+ call assert_equal(1, caught)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abcd', g:Xpath)
+endfunc
+
+func Test_exception_after_error_2()
+ XpathINIT
+ while 1
+ try
+ try
+ Xpath 'a'
+ let caught = 0
+ try
+ if 1
+ " Missing :endif
+ catch /.*/ " throw error exception
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ catch /^Vim(/
+ Xpath 'b'
+ let caught = 1
+ finally
+ Xpath 'c'
+ call assert_equal(1, caught)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abcd', g:Xpath)
+endfunc
+
+func Test_exception_after_error_3()
+ XpathINIT
+ while 1
+ try
+ try
+ let caught = 0
+ try
+ Xpath 'a'
+ call interrupt()
+ catch /do_not_catch/
+ call assert_report('should not get here')
+ if 1
+ " Missing :endif
+ catch /.*/ " throw error exception
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ catch /^Vim(/
+ Xpath 'b'
+ let caught = 1
+ finally
+ Xpath 'c'
+ call assert_equal(1, caught)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abcd', g:Xpath)
+endfunc
+
+func Test_exception_after_error_4()
+ XpathINIT
+ while 1
+ try
+ try
+ let caught = 0
+ try
+ Xpath 'a'
+ throw "x"
+ catch /do_not_catch/
+ call assert_report('should not get here')
+ if 1
+ " Missing :endif
+ catch /x/ " throw error exception
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ catch /^Vim(/
+ Xpath 'b'
+ let caught = 1
+ finally
+ Xpath 'c'
+ call assert_equal(1, caught)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abcd', g:Xpath)
+endfunc
+
+func Test_exception_after_error_5()
+ XpathINIT
+ while 1
+ try
+ try
+ let caught = 0
+ Xpath 'a'
+ endif " :endif without :if; throw error exception
+ if 1
+ " Missing :endif
+ catch /do_not_catch/ " ignore new error
+ call assert_report('should not get here')
+ catch /^Vim(endif):/
+ Xpath 'b'
+ let caught = 1
+ catch /^Vim(/
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ call assert_equal(1, caught)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ break
+ endtry
+ call assert_report('should not get here')
+ endwhile
+ call assert_equal('abcd', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 65: Errors in the /pattern/ argument of a :catch {{{1
+"
+" On an error in the /pattern/ argument of a :catch, the :catch does
+" not match. Any following :catches of the same :try/:endtry don't
+" match either. Finally clauses are executed.
+"-------------------------------------------------------------------------------
+
+func Test_catch_pattern_error()
+ CheckEnglish
+ XpathINIT
+
+ try
+ try
+ Xpath 'a'
+ throw "oops"
+ catch /^oops$/
+ Xpath 'b'
+ catch /\)/ " not checked; exception has already been caught
+ call assert_report('should not get here')
+ endtry
+ Xpath 'c'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ call assert_equal('abc', g:Xpath)
+
+ XpathINIT
+ func F()
+ try
+ try
+ try
+ Xpath 'a'
+ throw "ab"
+ catch /abc/ " does not catch
+ call assert_report('should not get here')
+ catch /\)/ " error; discards exception
+ call assert_report('should not get here')
+ catch /.*/ " not checked
+ call assert_report('should not get here')
+ finally
+ Xpath 'b'
+ endtry
+ call assert_report('should not get here')
+ catch /^ab$/ " checked, but original exception is discarded
+ call assert_report('should not get here')
+ catch /^Vim(catch):/
+ Xpath 'c'
+ call assert_match('Vim(catch):E475: Invalid argument:', v:exception)
+ finally
+ Xpath 'd'
+ endtry
+ Xpath 'e'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ Xpath 'f'
+ endfunc
+
+ call F()
+ call assert_equal('abcdef', g:Xpath)
+
+ delfunc F
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 66: Stop range :call on error, interrupt, or :throw {{{1
+"
+" When a function which is multiply called for a range since it
+" doesn't handle the range itself has an error in a command
+" dynamically enclosed by :try/:endtry or gets an interrupt or
+" executes a :throw, no more calls for the remaining lines in the
+" range are made. On an error in a command not dynamically enclosed
+" by :try/:endtry, the function is executed again for the remaining
+" lines in the range.
+"-------------------------------------------------------------------------------
+
+func Test_stop_range_on_error()
+ let test =<< trim [CODE]
+ let file = tempname()
+ exec "edit" file
+ call setline(1, ['line 1', 'line 2', 'line 3'])
+ let taken = ""
+ let expected = "G1EF1E(1)F1E(2)F1E(3)G2EF2E(1)G3IF3I(1)G4TF4T(1)G5AF5A(1)"
+
+ func F(reason, n) abort
+ let g:taken = g:taken .. "F" .. a:n ..
+ \ substitute(a:reason, '\(\l\).*', '\u\1', "") ..
+ \ "(" .. line(".") .. ")"
+
+ if a:reason == "error"
+ asdf
+ elseif a:reason == "interrupt"
+ call interrupt()
+ elseif a:reason == "throw"
+ throw "xyz"
+ elseif a:reason == "aborting error"
+ XloopNEXT
+ call assert_equal(g:taken, g:expected)
+ try
+ bwipeout!
+ call delete(g:file)
+ asdf
+ endtry
+ endif
+ endfunc
+
+ func G(reason, n)
+ let g:taken = g:taken .. "G" .. a:n ..
+ \ substitute(a:reason, '\(\l\).*', '\u\1', "")
+ 1,3call F(a:reason, a:n)
+ endfunc
+
+ Xpath 'a'
+ call G("error", 1)
+ try
+ Xpath 'b'
+ try
+ call G("error", 2)
+ call assert_report('should not get here')
+ finally
+ Xpath 'c'
+ try
+ call G("interrupt", 3)
+ call assert_report('should not get here')
+ finally
+ Xpath 'd'
+ try
+ call G("throw", 4)
+ call assert_report('should not get here')
+ endtry
+ endtry
+ endtry
+ catch /xyz/
+ Xpath 'e'
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ Xpath 'f'
+ call G("aborting error", 5)
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdef', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 67: :throw across :call command {{{1
+"
+" On a call command, an exception might be thrown when evaluating the
+" function name, during evaluation of the arguments, or when the
+" function is being executed. The exception can be caught by the
+" caller.
+"-------------------------------------------------------------------------------
+
+func THROW(x, n)
+ if a:n == 1
+ Xpath 'A'
+ elseif a:n == 2
+ Xpath 'B'
+ elseif a:n == 3
+ Xpath 'C'
+ endif
+ throw a:x
+endfunc
+
+func NAME(x, n)
+ if a:n == 1
+ call assert_report('should not get here')
+ elseif a:n == 2
+ Xpath 'D'
+ elseif a:n == 3
+ Xpath 'E'
+ elseif a:n == 4
+ Xpath 'F'
+ endif
+ return a:x
+endfunc
+
+func ARG(x, n)
+ if a:n == 1
+ call assert_report('should not get here')
+ elseif a:n == 2
+ call assert_report('should not get here')
+ elseif a:n == 3
+ Xpath 'G'
+ elseif a:n == 4
+ Xpath 'I'
+ endif
+ return a:x
+endfunc
+
+func Test_throw_across_call_cmd()
+ XpathINIT
+
+ func F(x, n)
+ if a:n == 2
+ call assert_report('should not get here')
+ elseif a:n == 4
+ Xpath 'a'
+ endif
+ endfunc
+
+ while 1
+ try
+ let v:errmsg = ""
+
+ while 1
+ try
+ Xpath 'b'
+ call {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
+ call assert_report('should not get here')
+ catch /^name$/
+ Xpath 'c'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ Xpath 'd'
+ call {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
+ call assert_report('should not get here')
+ catch /^arg$/
+ Xpath 'e'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ Xpath 'f'
+ call {NAME("THROW", 3)}(ARG("call", 3), 3)
+ call assert_report('should not get here')
+ catch /^call$/
+ Xpath 'g'
+ catch /^0$/ " default return value
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ Xpath 'h'
+ call {NAME("F", 4)}(ARG(4711, 4), 4)
+ Xpath 'i'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+
+ catch /^0$/ " default return value
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+
+ call assert_equal('bAcdDBefEGCghFIai', g:Xpath)
+ delfunction F
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 68: :throw across function calls in expressions {{{1
+"
+" On a function call within an expression, an exception might be
+" thrown when evaluating the function name, during evaluation of the
+" arguments, or when the function is being executed. The exception
+" can be caught by the caller.
+"
+" This test reuses the functions THROW(), NAME(), and ARG() from the
+" previous test.
+"-------------------------------------------------------------------------------
+
+func Test_throw_across_call_expr()
+ XpathINIT
+
+ func F(x, n)
+ if a:n == 2
+ call assert_report('should not get here')
+ elseif a:n == 4
+ Xpath 'a'
+ endif
+ return a:x
+ endfunction
+
+ while 1
+ try
+ let error = 0
+ let v:errmsg = ""
+
+ while 1
+ try
+ Xpath 'b'
+ let var1 = {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
+ call assert_report('should not get here')
+ catch /^name$/
+ Xpath 'c'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+ call assert_true(!exists('var1'))
+
+ while 1
+ try
+ Xpath 'd'
+ let var2 = {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
+ call assert_report('should not get here')
+ catch /^arg$/
+ Xpath 'e'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+ call assert_true(!exists('var2'))
+
+ while 1
+ try
+ Xpath 'f'
+ let var3 = {NAME("THROW", 3)}(ARG("call", 3), 3)
+ call assert_report('should not get here')
+ catch /^call$/
+ Xpath 'g'
+ catch /^0$/ " default return value
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+ call assert_true(!exists('var3'))
+
+ while 1
+ try
+ Xpath 'h'
+ let var4 = {NAME("F", 4)}(ARG(4711, 4), 4)
+ Xpath 'i'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ let v:errmsg = ""
+ break
+ endtry
+ endwhile
+ call assert_true(exists('var4') && var4 == 4711)
+
+ catch /^0$/ " default return value
+ call assert_report('should not get here')
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ call assert_equal("", v:errmsg)
+ break
+ endtry
+ endwhile
+
+ call assert_equal('bAcdDBefEGCghFIai', g:Xpath)
+ delfunc F
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 76: Errors, interrupts, :throw during expression evaluation {{{1
+"
+" When a function call made during expression evaluation is aborted
+" due to an error inside a :try/:endtry region or due to an interrupt
+" or a :throw, the expression evaluation is aborted as well. No
+" message is displayed for the cancelled expression evaluation. On an
+" error not inside :try/:endtry, the expression evaluation continues.
+"-------------------------------------------------------------------------------
+
+func Test_expr_eval_error()
+ let test =<< trim [CODE]
+ let taken = ""
+
+ func ERR(n)
+ let g:taken = g:taken .. "E" .. a:n
+ asdf
+ endfunc
+
+ func ERRabort(n) abort
+ let g:taken = g:taken .. "A" .. a:n
+ asdf
+ endfunc " returns -1; may cause follow-up msg for illegal var/func name
+
+ func WRAP(n, arg)
+ let g:taken = g:taken .. "W" .. a:n
+ let g:saved_errmsg = v:errmsg
+ return arg
+ endfunc
+
+ func INT(n)
+ let g:taken = g:taken .. "I" .. a:n
+ call interrupt()
+ endfunc
+
+ func THR(n)
+ let g:taken = g:taken .. "T" .. a:n
+ throw "should not be caught"
+ endfunc
+
+ func CONT(n)
+ let g:taken = g:taken .. "C" .. a:n
+ endfunc
+
+ func MSG(n)
+ let g:taken = g:taken .. "M" .. a:n
+ let errmsg = (a:n >= 37 && a:n <= 44) ? g:saved_errmsg : v:errmsg
+ let msgptn = (a:n >= 10 && a:n <= 27) ? "^$" : "asdf"
+ call assert_match(msgptn, errmsg)
+ let v:errmsg = ""
+ let g:saved_errmsg = ""
+ endfunc
+
+ let v:errmsg = ""
+
+ try
+ let t = 1
+ while t <= 9
+ Xloop 'a'
+ try
+ if t == 1
+ let v{ERR(t) + CONT(t)} = 0
+ elseif t == 2
+ let v{ERR(t) + CONT(t)}
+ elseif t == 3
+ let var = exists('v{ERR(t) + CONT(t)}')
+ elseif t == 4
+ unlet v{ERR(t) + CONT(t)}
+ elseif t == 5
+ function F{ERR(t) + CONT(t)}()
+ endfunction
+ elseif t == 6
+ function F{ERR(t) + CONT(t)}
+ elseif t == 7
+ let var = exists('*F{ERR(t) + CONT(t)}')
+ elseif t == 8
+ delfunction F{ERR(t) + CONT(t)}
+ elseif t == 9
+ let var = ERR(t) + CONT(t)
+ endif
+ catch /asdf/
+ " v:errmsg is not set when the error message is converted to an
+ " exception. Set it to the original error message.
+ let v:errmsg = substitute(v:exception, '^Vim:', '', "")
+ catch /^Vim\((\a\+)\)\=:/
+ " An error exception has been thrown after the original error.
+ let v:errmsg = ""
+ finally
+ call MSG(t)
+ let t = t + 1
+ XloopNEXT
+ continue " discard an aborting error
+ endtry
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ try
+ let t = 10
+ while t <= 18
+ Xloop 'b'
+ try
+ if t == 10
+ let v{INT(t) + CONT(t)} = 0
+ elseif t == 11
+ let v{INT(t) + CONT(t)}
+ elseif t == 12
+ let var = exists('v{INT(t) + CONT(t)}')
+ elseif t == 13
+ unlet v{INT(t) + CONT(t)}
+ elseif t == 14
+ function F{INT(t) + CONT(t)}()
+ endfunction
+ elseif t == 15
+ function F{INT(t) + CONT(t)}
+ elseif t == 16
+ let var = exists('*F{INT(t) + CONT(t)}')
+ elseif t == 17
+ delfunction F{INT(t) + CONT(t)}
+ elseif t == 18
+ let var = INT(t) + CONT(t)
+ endif
+ catch /^Vim\((\a\+)\)\=:\(Interrupt\)\@!/
+ " An error exception has been triggered after the interrupt.
+ let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
+ finally
+ call MSG(t)
+ let t = t + 1
+ XloopNEXT
+ continue " discard interrupt
+ endtry
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ try
+ let t = 19
+ while t <= 27
+ Xloop 'c'
+ try
+ if t == 19
+ let v{THR(t) + CONT(t)} = 0
+ elseif t == 20
+ let v{THR(t) + CONT(t)}
+ elseif t == 21
+ let var = exists('v{THR(t) + CONT(t)}')
+ elseif t == 22
+ unlet v{THR(t) + CONT(t)}
+ elseif t == 23
+ function F{THR(t) + CONT(t)}()
+ endfunction
+ elseif t == 24
+ function F{THR(t) + CONT(t)}
+ elseif t == 25
+ let var = exists('*F{THR(t) + CONT(t)}')
+ elseif t == 26
+ delfunction F{THR(t) + CONT(t)}
+ elseif t == 27
+ let var = THR(t) + CONT(t)
+ endif
+ catch /^Vim\((\a\+)\)\=:/
+ " An error exception has been triggered after the :throw.
+ let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
+ finally
+ call MSG(t)
+ let t = t + 1
+ XloopNEXT
+ continue " discard exception
+ endtry
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ let v{ERR(28) + CONT(28)} = 0
+ call MSG(28)
+ let v{ERR(29) + CONT(29)}
+ call MSG(29)
+ let var = exists('v{ERR(30) + CONT(30)}')
+ call MSG(30)
+ unlet v{ERR(31) + CONT(31)}
+ call MSG(31)
+ function F{ERR(32) + CONT(32)}()
+ endfunction
+ call MSG(32)
+ function F{ERR(33) + CONT(33)}
+ call MSG(33)
+ let var = exists('*F{ERR(34) + CONT(34)}')
+ call MSG(34)
+ delfunction F{ERR(35) + CONT(35)}
+ call MSG(35)
+ let var = ERR(36) + CONT(36)
+ call MSG(36)
+
+ let saved_errmsg = ""
+
+ let v{WRAP(37, ERRabort(37)) + CONT(37)} = 0
+ call MSG(37)
+ let v{WRAP(38, ERRabort(38)) + CONT(38)}
+ call MSG(38)
+ let var = exists('v{WRAP(39, ERRabort(39)) + CONT(39)}')
+ call MSG(39)
+ unlet v{WRAP(40, ERRabort(40)) + CONT(40)}
+ call MSG(40)
+ function F{WRAP(41, ERRabort(41)) + CONT(41)}()
+ endfunction
+ call MSG(41)
+ function F{WRAP(42, ERRabort(42)) + CONT(42)}
+ call MSG(42)
+ let var = exists('*F{WRAP(43, ERRabort(43)) + CONT(43)}')
+ call MSG(43)
+ delfunction F{WRAP(44, ERRabort(44)) + CONT(44)}
+ call MSG(44)
+ let var = ERRabort(45) + CONT(45)
+ call MSG(45)
+ Xpath 'd'
+
+ let expected = ""
+ \ .. "E1M1E2M2E3M3E4M4E5M5E6M6E7M7E8M8E9M9"
+ \ .. "I10M10I11M11I12M12I13M13I14M14I15M15I16M16I17M17I18M18"
+ \ .. "T19M19T20M20T21M21T22M22T23M23T24M24T25M25T26M26T27M27"
+ \ .. "E28C28M28E29C29M29E30C30M30E31C31M31E32C32M32E33C33M33"
+ \ .. "E34C34M34E35C35M35E36C36M36"
+ \ .. "A37W37C37M37A38W38C38M38A39W39C39M39A40W40C40M40A41W41C41M41"
+ \ .. "A42W42C42M42A43W43C43M43A44W44C44M44A45C45M45"
+ call assert_equal(expected, taken)
+ [CODE]
+ let verify =<< trim [CODE]
+ let expected = "a1a2a3a4a5a6a7a8a9"
+ \ .. "b10b11b12b13b14b15b16b17b18"
+ \ .. "c19c20c21c22c23c24c25c26c27d"
+ call assert_equal(expected, g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 77: Errors, interrupts, :throw in name{brace-expression} {{{1
+"
+" When a function call made during evaluation of an expression in
+" braces as part of a function name after ":function" is aborted due
+" to an error inside a :try/:endtry region or due to an interrupt or
+" a :throw, the expression evaluation is aborted as well, and the
+" function definition is ignored, skipping all commands to the
+" ":endfunction". On an error not inside :try/:endtry, the expression
+" evaluation continues and the function gets defined, and can be
+" called and deleted.
+"-------------------------------------------------------------------------------
+func Test_brace_expr_error()
+ let test =<< trim [CODE]
+ func ERR() abort
+ Xloop 'a'
+ asdf
+ endfunc " returns -1
+
+ func OK()
+ Xloop 'b'
+ let v:errmsg = ""
+ return 0
+ endfunc
+
+ let v:errmsg = ""
+
+ Xpath 'c'
+ func F{1 + ERR() + OK()}(arg)
+ " F0 should be defined.
+ if exists("a:arg") && a:arg == "calling"
+ Xpath 'd'
+ else
+ call assert_report('should not get here')
+ endif
+ endfunction
+ call assert_equal("", v:errmsg)
+ XloopNEXT
+
+ Xpath 'e'
+ call F{1 + ERR() + OK()}("calling")
+ call assert_equal("", v:errmsg)
+ XloopNEXT
+
+ Xpath 'f'
+ delfunction F{1 + ERR() + OK()}
+ call assert_equal("", v:errmsg)
+ XloopNEXT
+
+ try
+ while 1
+ try
+ Xpath 'g'
+ func G{1 + ERR() + OK()}(arg)
+ " G0 should not be defined, and the function body should be
+ " skipped.
+ call assert_report('should not get here')
+ " Use an unmatched ":finally" to check whether the body is
+ " skipped when an error occurs in ERR(). This works whether or
+ " not the exception is converted to an exception.
+ finally
+ call assert_report('should not get here')
+ endtry
+ try
+ call assert_report('should not get here')
+ endfunction
+
+ call assert_report('should not get here')
+ catch /asdf/
+ " Jumped to when the function is not defined and the body is
+ " skipped.
+ Xpath 'h'
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'i'
+ break
+ endtry " jumped to when the body is not skipped
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ca1b1ea2b2dfa3b3ga4hi', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 78: Messages on parsing errors in expression evaluation {{{1
+"
+" When an expression evaluation detects a parsing error, an error
+" message is given and converted to an exception, and the expression
+" evaluation is aborted.
+"-------------------------------------------------------------------------------
+func Test_expr_eval_error_msg()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ let taken = ""
+
+ func F(n)
+ let g:taken = g:taken . "F" . a:n
+ endfunc
+
+ func MSG(n, enr, emsg)
+ let g:taken = g:taken . "M" . a:n
+ call assert_match('^' .. a:enr .. ':', v:errmsg)
+ call assert_match(a:emsg, v:errmsg)
+ endfunc
+
+ func CONT(n)
+ let g:taken = g:taken . "C" . a:n
+ endfunc
+
+ let v:errmsg = ""
+ try
+ let t = 1
+ while t <= 14
+ let g:taken = g:taken . "T" . t
+ let v:errmsg = ""
+ try
+ if t == 1
+ let v{novar + CONT(t)} = 0
+ elseif t == 2
+ let v{novar + CONT(t)}
+ elseif t == 3
+ let var = exists('v{novar + CONT(t)}')
+ elseif t == 4
+ unlet v{novar + CONT(t)}
+ elseif t == 5
+ function F{novar + CONT(t)}()
+ endfunction
+ elseif t == 6
+ function F{novar + CONT(t)}
+ elseif t == 7
+ let var = exists('*F{novar + CONT(t)}')
+ elseif t == 8
+ delfunction F{novar + CONT(t)}
+ elseif t == 9
+ echo novar + CONT(t)
+ elseif t == 10
+ echo v{novar + CONT(t)}
+ elseif t == 11
+ echo F{novar + CONT(t)}
+ elseif t == 12
+ let var = novar + CONT(t)
+ elseif t == 13
+ let var = v{novar + CONT(t)}
+ elseif t == 14
+ let var = F{novar + CONT(t)}()
+ endif
+ catch /^Vim\((\a\+)\)\=:/
+ Xloop 'a'
+ " v:errmsg is not set when the error message is converted to an
+ " exception. Set it to the original error message.
+ let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
+ finally
+ Xloop 'b'
+ if t <= 8 && t != 3 && t != 7
+ call MSG(t, 'E475', 'Invalid argument\>')
+ else
+ call MSG(t, 'E121', "Undefined variable")
+ endif
+ let t = t + 1
+ XloopNEXT
+ continue " discard an aborting error
+ endtry
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+
+ func T(n, expr, enr, emsg)
+ try
+ let g:taken = g:taken . "T" . a:n
+ let v:errmsg = ""
+ try
+ execute "let var = " . a:expr
+ catch /^Vim\((\a\+)\)\=:/
+ Xloop 'c'
+ " v:errmsg is not set when the error message is converted to an
+ " exception. Set it to the original error message.
+ let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
+ finally
+ Xloop 'd'
+ call MSG(a:n, a:enr, a:emsg)
+ XloopNEXT
+ " Discard an aborting error:
+ return
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ endfunc
+
+ call T(15, 'Nofunc() + CONT(15)', 'E117', "Unknown function")
+ call T(16, 'F(1 2 + CONT(16))', 'E116', "Invalid arguments")
+ call T(17, 'F(1, 2) + CONT(17)', 'E118', "Too many arguments")
+ call T(18, 'F() + CONT(18)', 'E119', "Not enough arguments")
+ call T(19, '{(1} + CONT(19)', 'E110', "Missing ')'")
+ call T(20, '("abc"[1) + CONT(20)', 'E111', "Missing ']'")
+ call T(21, '(1 +) + CONT(21)', 'E15', "Invalid expression")
+ call T(22, '1 2 + CONT(22)', 'E15', "Invalid expression")
+ call T(23, '(1 ? 2) + CONT(23)', 'E109', "Missing ':' after '?'")
+ call T(24, '("abc) + CONT(24)', 'E114', "Missing quote")
+ call T(25, "('abc) + CONT(25)", 'E115', "Missing quote")
+ call T(26, '& + CONT(26)', 'E112', "Option name missing")
+ call T(27, '&asdf + CONT(27)', 'E113', "Unknown option")
+
+ let expected = ""
+ \ .. "T1M1T2M2T3M3T4M4T5M5T6M6T7M7T8M8T9M9T10M10T11M11T12M12T13M13T14M14"
+ \ .. "T15M15T16M16T17M17T18M18T19M19T20M20T21M21T22M22T23M23T24M24T25M25"
+ \ .. "T26M26T27M27"
+
+ call assert_equal(expected, taken)
+ [CODE]
+ let verify =<< trim [CODE]
+ let expected = "a1b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9a10b10a11b11a12b12"
+ \ .. "a13b13a14b14c15d15c16d16c17d17c18d18c19d19c20d20"
+ \ .. "c21d21c22d22c23d23c24d24c25d25c26d26c27d27"
+ call assert_equal(expected, g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 79: Throwing one of several errors for the same command {{{1
+"
+" When several errors appear in a row (for instance during expression
+" evaluation), the first as the most specific one is used when
+" throwing an error exception. If, however, a syntax error is
+" detected afterwards, this one is used for the error exception.
+" On a syntax error, the next command is not executed, on a normal
+" error, however, it is (relevant only in a function without the
+" "abort" flag). v:errmsg is not set.
+"
+" If throwing error exceptions is configured off, v:errmsg is always
+" set to the latest error message, that is, to the more general
+" message or the syntax error, respectively.
+"-------------------------------------------------------------------------------
+func Test_throw_multi_error()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ func NEXT(cmd)
+ exec a:cmd . " | Xloop 'a'"
+ endfun
+
+ call NEXT('echo novar') " (checks nextcmd)
+ XloopNEXT
+ call NEXT('let novar #') " (skips nextcmd)
+ XloopNEXT
+ call NEXT('unlet novar #') " (skips nextcmd)
+ XloopNEXT
+ call NEXT('let {novar}') " (skips nextcmd)
+ XloopNEXT
+ call NEXT('unlet{ novar}') " (skips nextcmd)
+
+ call assert_equal('a1', g:Xpath)
+ XpathINIT
+ XloopINIT
+
+ func EXEC(cmd)
+ exec a:cmd
+ endfunc
+
+ try
+ while 1 " dummy loop
+ try
+ let v:errmsg = ""
+ call EXEC('echo novar') " normal error
+ catch /^Vim\((\a\+)\)\=:/
+ Xpath 'b'
+ call assert_match('E121: Undefined variable: novar', v:exception)
+ finally
+ Xpath 'c'
+ call assert_equal("", v:errmsg)
+ break
+ endtry
+ endwhile
+
+ Xpath 'd'
+ let cmd = "let"
+ while cmd != ""
+ try
+ let v:errmsg = ""
+ call EXEC(cmd . ' novar #') " normal plus syntax error
+ catch /^Vim\((\a\+)\)\=:/
+ Xloop 'e'
+ call assert_match('E488: Trailing characters', v:exception)
+ finally
+ Xloop 'f'
+ call assert_equal("", v:errmsg)
+ if cmd == "let"
+ let cmd = "unlet"
+ else
+ let cmd = ""
+ endif
+ XloopNEXT
+ continue
+ endtry
+ endwhile
+
+ Xpath 'g'
+ let cmd = "let"
+ while cmd != ""
+ try
+ let v:errmsg = ""
+ call EXEC(cmd . ' {novar}') " normal plus syntax error
+ catch /^Vim\((\a\+)\)\=:/
+ Xloop 'h'
+ call assert_match('E475: Invalid argument: {novar}', v:exception)
+ finally
+ Xloop 'i'
+ call assert_equal("", v:errmsg)
+ if cmd == "let"
+ let cmd = "unlet"
+ else
+ let cmd = ""
+ endif
+ XloopNEXT
+ continue
+ endtry
+ endwhile
+ catch /.*/
+ call assert_report('should not get here')
+ endtry
+ Xpath 'j'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('bcde1f1e2f2gh3i3h4i4j', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 80: Syntax error in expression for illegal :elseif {{{1
+"
+" If there is a syntax error in the expression after an illegal
+" :elseif, an error message is given (or an error exception thrown)
+" for the illegal :elseif rather than the expression error.
+"-------------------------------------------------------------------------------
+func Test_if_syntax_error()
+ CheckEnglish
+
+ let test =<< trim [CODE]
+ let v:errmsg = ""
+ if 0
+ else
+ elseif 1 ||| 2
+ endif
+ Xpath 'a'
+ call assert_match('E584: :elseif after :else', v:errmsg)
+
+ let v:errmsg = ""
+ if 1
+ else
+ elseif 1 ||| 2
+ endif
+ Xpath 'b'
+ call assert_match('E584: :elseif after :else', v:errmsg)
+
+ let v:errmsg = ""
+ elseif 1 ||| 2
+ Xpath 'c'
+ call assert_match('E582: :elseif without :if', v:errmsg)
+
+ let v:errmsg = ""
+ while 1
+ elseif 1 ||| 2
+ endwhile
+ Xpath 'd'
+ call assert_match('E582: :elseif without :if', v:errmsg)
+
+ while 1
+ try
+ try
+ let v:errmsg = ""
+ if 0
+ else
+ elseif 1 ||| 2
+ endif
+ catch /^Vim\((\a\+)\)\=:/
+ Xpath 'e'
+ call assert_match('E584: :elseif after :else', v:exception)
+ finally
+ Xpath 'f'
+ call assert_equal("", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'g'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ try
+ let v:errmsg = ""
+ if 1
+ else
+ elseif 1 ||| 2
+ endif
+ catch /^Vim\((\a\+)\)\=:/
+ Xpath 'h'
+ call assert_match('E584: :elseif after :else', v:exception)
+ finally
+ Xpath 'i'
+ call assert_equal("", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'j'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ try
+ let v:errmsg = ""
+ elseif 1 ||| 2
+ catch /^Vim\((\a\+)\)\=:/
+ Xpath 'k'
+ call assert_match('E582: :elseif without :if', v:exception)
+ finally
+ Xpath 'l'
+ call assert_equal("", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'm'
+ break
+ endtry
+ endwhile
+
+ while 1
+ try
+ try
+ let v:errmsg = ""
+ while 1
+ elseif 1 ||| 2
+ endwhile
+ catch /^Vim\((\a\+)\)\=:/
+ Xpath 'n'
+ call assert_match('E582: :elseif without :if', v:exception)
+ finally
+ Xpath 'o'
+ call assert_equal("", v:errmsg)
+ endtry
+ catch /.*/
+ call assert_report('should not get here')
+ finally
+ Xpath 'p'
+ break
+ endtry
+ endwhile
+ Xpath 'q'
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('abcdefghijklmnopq', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 81: Discarding exceptions after an error or interrupt {{{1
+"
+" When an exception is thrown from inside a :try conditional without
+" :catch and :finally clauses and an error or interrupt occurs before
+" the :endtry is reached, the exception is discarded.
+"-------------------------------------------------------------------------------
+
+func Test_discard_exception_after_error_1()
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ try
+ Xpath 'b'
+ throw "arrgh"
+ call assert_report('should not get here')
+ if 1
+ call assert_report('should not get here')
+ " error after :throw: missing :endif
+ endtry
+ call assert_report('should not get here')
+ catch /arrgh/
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ab', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
+" TODO: Not able inject an interrupt after throwing an exception
+func Disable_Test_discard_exception_after_error_2()
+ let test =<< trim [CODE]
+ try
+ Xpath 'a'
+ try
+ Xpath 'b'
+ throw "arrgh"
+ call interrupt() " FIXME: throw is not interrupted here
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ catch /arrgh/
+ call assert_report('should not get here')
+ endtry
+ call assert_report('should not get here')
+ [CODE]
+ let verify =<< trim [CODE]
+ call assert_equal('ab', g:Xpath)
+ [CODE]
+ call RunInNewVim(test, verify)
+endfunc
+
"-------------------------------------------------------------------------------
" Test 87 using (expr) ? funcref : funcref {{{1
"
@@ -1443,7 +6241,7 @@ endfunc
" Undefined behavior was detected by ubsan with line continuation
" after an empty line.
"-------------------------------------------------------------------------------
-func Test_script_emty_line_continuation()
+func Test_script_empty_line_continuation()
\
endfunc
@@ -1707,9 +6505,7 @@ endfunc
" Test for deep nesting of if/for/while/try statements {{{1
func Test_deep_nest()
- if !CanRunVimInTerminal()
- throw 'Skipped: cannot run vim in terminal'
- endif
+ CheckRunVimInTerminal
let lines =<< trim [SCRIPT]
" Deep nesting of if ... endif
@@ -1812,6 +6608,7 @@ func Test_float_conversion_errors()
endif
endfunc
+" invalid function names {{{1
func Test_invalid_function_names()
" function name not starting with capital
let caught_e128 = 0
@@ -1872,7 +6669,7 @@ func Test_invalid_function_names()
call delete('Xscript')
endfunc
-" substring and variable name
+" substring and variable name {{{1
func Test_substring_var()
let str = 'abcdef'
let n = 3
@@ -1892,6 +6689,20 @@ func Test_substring_var()
unlet b:nn
endfunc
+" Test using s: with a typed command {{{1
+func Test_typed_script_var()
+ CheckRunVimInTerminal
+
+ let buf = RunVimInTerminal('', {'rows': 6})
+
+ " Deep nesting of if ... endif
+ call term_sendkeys(buf, ":echo get(s:, 'foo', 'x')\n")
+ call TermWait(buf)
+ call WaitForAssert({-> assert_match('^E116:', term_getline(buf, 5))})
+
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_for_over_string()
let res = ''
for c in 'aéc̀d'
diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim
index e712896562..2bf8c3fc77 100644
--- a/src/nvim/testdir/test_virtualedit.vim
+++ b/src/nvim/testdir/test_virtualedit.vim
@@ -80,6 +80,10 @@ func Test_edit_change()
call setline(1, "\t⒌")
normal Cx
call assert_equal('x', getline(1))
+ " Do a visual block change
+ call setline(1, ['a', 'b', 'c'])
+ exe "normal gg3l\<C-V>2jcx"
+ call assert_equal(['a x', 'b x', 'c x'], getline(1, '$'))
bwipe!
set virtualedit=
endfunc
@@ -289,6 +293,16 @@ func Test_replace_after_eol()
call append(0, '"r"')
normal gg$5lrxa
call assert_equal('"r" x', getline(1))
+ " visual block replace
+ %d _
+ call setline(1, ['a', '', 'b'])
+ exe "normal 2l\<C-V>2jrx"
+ call assert_equal(['a x', ' x', 'b x'], getline(1, '$'))
+ " visual characterwise selection replace after eol
+ %d _
+ call setline(1, 'a')
+ normal 4lv2lrx
+ call assert_equal('a xxx', getline(1))
bwipe!
set virtualedit=
endfunc
@@ -346,6 +360,48 @@ func Test_yank_paste_small_del_reg()
set virtualedit=
endfunc
+" Test for delete that breaks a tab into spaces
+func Test_delete_break_tab()
+ new
+ call setline(1, "one\ttwo")
+ set virtualedit=all
+ normal v3ld
+ call assert_equal(' two', getline(1))
+ set virtualedit&
+ close!
+endfunc
+
+" Test for using <BS>, <C-W> and <C-U> in virtual edit mode
+" to erase character, word and line.
+func Test_ve_backspace()
+ new
+ call setline(1, 'sample')
+ set virtualedit=all
+ set backspace=indent,eol,start
+ exe "normal 15|i\<BS>\<BS>"
+ call assert_equal([0, 1, 7, 5], getpos('.'))
+ exe "normal 15|i\<C-W>"
+ call assert_equal([0, 1, 6, 0], getpos('.'))
+ exe "normal 15|i\<C-U>"
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+ set backspace&
+ set virtualedit&
+ close!
+endfunc
+
+" Test for delete (x) on EOL character and after EOL
+func Test_delete_past_eol()
+ new
+ call setline(1, "ab")
+ set virtualedit=all
+ exe "normal 2lx"
+ call assert_equal('ab', getline(1))
+ exe "normal 10lx"
+ call assert_equal('ab', getline(1))
+ set virtualedit&
+ bw!
+endfunc
+
" After calling s:TryVirtualeditReplace(), line 1 will contain one of these
" two strings, depending on whether virtual editing is on or off.
let s:result_ve_on = 'a x'
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index a9c057088d..712ea343ed 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -225,6 +225,15 @@ func Test_virtual_replace()
exe "normal iabcdefghijklmnopqrst\<Esc>0gRAB\tIJKLMNO\tQR"
call assert_equal(['AB......CDEFGHI.Jkl',
\ 'AB IJKLMNO QRst'], getline(12, 13))
+
+ " Test inserting Tab with 'noexpandtab' and 'softabstop' set to 4
+ %d
+ call setline(1, 'aaaaaaaaaaaaa')
+ set softtabstop=4
+ exe "normal gggR\<Tab>\<Tab>x"
+ call assert_equal("\txaaaa", getline(1))
+ set softtabstop&
+
enew!
set noai bs&vim
if exists('save_t_kD')
diff --git a/src/nvim/testdir/test_winbuf_close.vim b/src/nvim/testdir/test_winbuf_close.vim
index f4878c2397..643c1068bd 100644
--- a/src/nvim/testdir/test_winbuf_close.vim
+++ b/src/nvim/testdir/test_winbuf_close.vim
@@ -115,7 +115,7 @@ func Test_winbuf_close()
call assert_equal('Xtest2', bufname('%'))
quit!
call assert_equal('Xtest3', bufname('%'))
- call assert_fails('silent! quit!', 'E162')
+ call assert_fails('silent! quit!', 'E37')
call assert_equal('Xtest1', bufname('%'))
call delete('Xtest1')
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index 902a3791d4..20564fed66 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -501,10 +501,13 @@ func Test_win_screenpos()
call assert_equal([1, 32], win_screenpos(2))
call assert_equal([12, 1], win_screenpos(3))
call assert_equal([0, 0], win_screenpos(4))
+ call assert_fails('let l = win_screenpos([])', 'E745:')
only
endfunc
func Test_window_jump_tag()
+ CheckFeature quickfix
+
help
/iccf
call assert_match('^|iccf|', getline('.'))
@@ -542,6 +545,7 @@ func Test_window_newtab()
call assert_equal(2, tabpagenr('$'))
call assert_equal(['Xb', 'Xa'], map(tabpagebuflist(1), 'bufname(v:val)'))
call assert_equal(['Xc' ], map(2->tabpagebuflist(), 'bufname(v:val)'))
+ call assert_equal(['Xc' ], map(tabpagebuflist(), 'bufname(v:val)'))
%bw!
endfunc
@@ -733,6 +737,7 @@ func Test_relative_cursor_position_in_one_line_window()
only!
bwipe!
+ call assert_fails('call winrestview(v:_null_dict)', 'E474:')
endfunc
func Test_relative_cursor_position_after_move_and_resize()
@@ -909,6 +914,10 @@ func Test_winnr()
call assert_fails("echo winnr('ll')", 'E15:')
call assert_fails("echo winnr('5')", 'E15:')
call assert_equal(4, winnr('0h'))
+ call assert_fails("let w = winnr([])", 'E730:')
+ call assert_equal('unknown', win_gettype(-1))
+ call assert_equal(-1, winheight(-1))
+ call assert_equal(-1, winwidth(-1))
tabnew
call assert_equal(8, tabpagewinnr(1, 'j'))
@@ -929,9 +938,12 @@ func Test_winrestview()
call assert_equal(view, winsaveview())
bwipe!
+ call assert_fails('call winrestview(v:_null_dict)', 'E474:')
endfunc
func Test_win_splitmove()
+ CheckFeature quickfix
+
edit a
leftabove split b
leftabove vsplit c
@@ -957,6 +969,7 @@ func Test_win_splitmove()
call assert_equal(bufname(winbufnr(2)), 'b')
call assert_equal(bufname(winbufnr(3)), 'a')
call assert_equal(bufname(winbufnr(4)), 'd')
+ call assert_fails('call win_splitmove(winnr(), winnr("k"), v:_null_dict)', 'E474:')
only | bd
call assert_fails('call win_splitmove(winnr(), 123)', 'E957:')
@@ -1129,6 +1142,18 @@ func Test_split_cmds_with_no_room()
call Run_noroom_for_newwindow_test('v')
endfunc
+" Test for various wincmd failures
+func Test_wincmd_fails()
+ only!
+ call assert_beeps("normal \<C-W>w")
+ call assert_beeps("normal \<C-W>p")
+ call assert_beeps("normal \<C-W>gk")
+ call assert_beeps("normal \<C-W>r")
+ call assert_beeps("normal \<C-W>K")
+ call assert_beeps("normal \<C-W>H")
+ call assert_beeps("normal \<C-W>2gt")
+endfunc
+
func Test_window_resize()
" Vertical :resize (absolute, relative, min and max size).
vsplit
@@ -1214,7 +1239,7 @@ endfunc
" Test for jumping to a vertical/horizontal neighbor window based on the
" current cursor position
-func Test_window_goto_neightbor()
+func Test_window_goto_neighbor()
%bw!
" Vertical window movement
@@ -1393,17 +1418,20 @@ func Test_win_move_separator()
call assert_equal(w0, winwidth(0))
call assert_true(win_move_separator(0, -1))
call assert_equal(w0, winwidth(0))
+
" check that win_move_separator doesn't error with offsets beyond moving
" possibility
call assert_true(win_move_separator(id, 5000))
call assert_true(winwidth(id) > w)
call assert_true(win_move_separator(id, -5000))
call assert_true(winwidth(id) < w)
+
" check that win_move_separator returns false for an invalid window
wincmd =
let w = winwidth(0)
call assert_false(win_move_separator(-1, 1))
call assert_equal(w, winwidth(0))
+
" check that win_move_separator returns false for a floating window
let id = nvim_open_win(
\ 0, 0, #{relative: 'editor', row: 2, col: 2, width: 5, height: 3})
@@ -1411,6 +1439,13 @@ func Test_win_move_separator()
call assert_false(win_move_separator(id, 1))
call assert_equal(w, winwidth(id))
call nvim_win_close(id, 1)
+
+ " check that using another tabpage fails without crash
+ let id = win_getid()
+ tabnew
+ call assert_fails('call win_move_separator(id, -1)', 'E1308:')
+ tabclose
+
%bwipe!
endfunc
diff --git a/src/nvim/testdir/test_window_id.vim b/src/nvim/testdir/test_window_id.vim
index 8bf4ede350..396a49b55f 100644
--- a/src/nvim/testdir/test_window_id.vim
+++ b/src/nvim/testdir/test_window_id.vim
@@ -1,5 +1,7 @@
" Test using the window ID.
+source check.vim
+
func Test_win_getid()
edit one
let id1 = win_getid()
@@ -90,10 +92,16 @@ func Test_win_getid()
split
call assert_equal(sort([id5, win_getid()]), sort(win_findbuf(bufnr5)))
+ call assert_fails('let w = win_getid([])', 'E745:')
+ call assert_equal(0, win_getid(-1))
+ call assert_equal(-1, win_getid(1, -1))
+
only!
endfunc
func Test_win_getid_curtab()
+ CheckFeature quickfix
+
tabedit X
tabfirst
copen
@@ -127,4 +135,8 @@ func Test_winlayout()
let w2 = win_getid()
call assert_equal(['leaf', w2], 2->winlayout())
tabclose
+
+ call assert_equal([], winlayout(-1))
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index adc05ab979..8fb4c8fa4c 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -260,6 +260,193 @@ func Test_write_errors()
close
call delete('Xfile')
+
+ " Nvim treats NULL list/blob more like empty list/blob
+ " call writefile(v:_null_list, 'Xfile')
+ " call assert_false(filereadable('Xfile'))
+ " call writefile(v:_null_blob, 'Xfile')
+ " call assert_false(filereadable('Xfile'))
+ call assert_fails('call writefile([], "")', 'E482:')
+
+ " very long file name
+ let long_fname = repeat('n', 5000)
+ call assert_fails('exe "w " .. long_fname', 'E75:')
+ call assert_fails('call writefile([], long_fname)', 'E482:')
+endfunc
+
+" Test for writing to a file which is modified after Vim read it
+func Test_write_file_mtime()
+ CheckEnglish
+ CheckRunVimInTerminal
+
+ " First read the file into a buffer
+ call writefile(["Line1", "Line2"], 'Xfile')
+ let old_ftime = getftime('Xfile')
+ let buf = RunVimInTerminal('Xfile', #{rows : 10})
+ call term_wait(buf)
+ call term_sendkeys(buf, ":set noswapfile\<CR>")
+ call term_wait(buf)
+
+ " Modify the file directly. Make sure the file modification time is
+ " different. Note that on Linux/Unix, the file is considered modified
+ " outside, only if the difference is 2 seconds or more
+ sleep 1
+ call writefile(["Line3", "Line4"], 'Xfile')
+ let new_ftime = getftime('Xfile')
+ while new_ftime - old_ftime < 2
+ sleep 100m
+ call writefile(["Line3", "Line4"], 'Xfile')
+ let new_ftime = getftime('Xfile')
+ endwhile
+
+ " Try to overwrite the file and check for the prompt
+ call term_sendkeys(buf, ":w\<CR>")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal("WARNING: The file has been changed since reading it!!!", term_getline(buf, 9))})
+ call assert_equal("Do you really want to write to it (y/n)?",
+ \ term_getline(buf, 10))
+ call term_sendkeys(buf, "n\<CR>")
+ call term_wait(buf)
+ call assert_equal(new_ftime, getftime('Xfile'))
+ call term_sendkeys(buf, ":w\<CR>")
+ call term_wait(buf)
+ call term_sendkeys(buf, "y\<CR>")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal('Line2', readfile('Xfile')[1])})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xfile')
+endfunc
+
+" Test for an autocmd unloading a buffer during a write command
+func Test_write_autocmd_unloadbuf_lockmark()
+ augroup WriteTest
+ autocmd BufWritePre Xfile enew | write
+ augroup END
+ e Xfile
+ call assert_fails('lockmarks write', ['E32', 'E203:'])
+ augroup WriteTest
+ au!
+ augroup END
+ augroup! WriteTest
+endfunc
+
+" Test for writing a buffer with 'acwrite' but without autocmds
+func Test_write_acwrite_error()
+ new Xfile
+ call setline(1, ['line1', 'line2', 'line3'])
+ set buftype=acwrite
+ call assert_fails('write', 'E676:')
+ call assert_fails('1,2write!', 'E676:')
+ call assert_fails('w >>', 'E676:')
+ close!
+endfunc
+
+" Test for adding and removing lines from an autocmd when writing a buffer
+func Test_write_autocmd_add_remove_lines()
+ new Xfile
+ call setline(1, ['aaa', 'bbb', 'ccc', 'ddd'])
+
+ " Autocmd deleting lines from the file when writing a partial file
+ augroup WriteTest2
+ au!
+ autocmd FileWritePre Xfile 1,2d
+ augroup END
+ call assert_fails('2,3w!', 'E204:')
+
+ " Autocmd adding lines to a file when writing a partial file
+ augroup WriteTest2
+ au!
+ autocmd FileWritePre Xfile call append(0, ['xxx', 'yyy'])
+ augroup END
+ %d
+ call setline(1, ['aaa', 'bbb', 'ccc', 'ddd'])
+ 1,2w!
+ call assert_equal(['xxx', 'yyy', 'aaa', 'bbb'], readfile('Xfile'))
+
+ " Autocmd deleting lines from the file when writing the whole file
+ augroup WriteTest2
+ au!
+ autocmd BufWritePre Xfile 1,2d
+ augroup END
+ %d
+ call setline(1, ['aaa', 'bbb', 'ccc', 'ddd'])
+ w
+ call assert_equal(['ccc', 'ddd'], readfile('Xfile'))
+
+ augroup WriteTest2
+ au!
+ augroup END
+ augroup! WriteTest2
+
+ close!
+ call delete('Xfile')
+endfunc
+
+" Test for writing to a readonly file
+func Test_write_readonly()
+ " In Cirrus-CI, the freebsd tests are run under a root account. So this test
+ " doesn't fail.
+ CheckNotBSD
+ call writefile([], 'Xfile')
+ call setfperm('Xfile', "r--------")
+ edit Xfile
+ set noreadonly
+ call assert_fails('write', 'E505:')
+ let save_cpo = &cpo
+ set cpo+=W
+ call assert_fails('write!', 'E504:')
+ let &cpo = save_cpo
+ call setline(1, ['line1'])
+ write!
+ call assert_equal(['line1'], readfile('Xfile'))
+ call delete('Xfile')
+endfunc
+
+" Test for 'patchmode'
+func Test_patchmode()
+ CheckNotBSD
+ call writefile(['one'], 'Xfile')
+ set patchmode=.orig nobackup writebackup
+ new Xfile
+ call setline(1, 'two')
+ " first write should create the .orig file
+ write
+ " TODO: Xfile.orig is not created in Cirrus FreeBSD CI test
+ call assert_equal(['one'], readfile('Xfile.orig'))
+ call setline(1, 'three')
+ " subsequent writes should not create/modify the .orig file
+ write
+ call assert_equal(['one'], readfile('Xfile.orig'))
+ set patchmode& backup& writebackup&
+ call delete('Xfile')
+ call delete('Xfile.orig')
+endfunc
+
+" Test for writing to a file in a readonly directory
+func Test_write_readonly_dir()
+ if !has('unix') || has('bsd')
+ " On MS-Windows, modifying files in a read-only directory is allowed.
+ " In Cirrus-CI for Freebsd, tests are run under a root account where
+ " modifying files in a read-only directory are allowed.
+ return
+ endif
+ call mkdir('Xdir')
+ call writefile(['one'], 'Xdir/Xfile1')
+ call setfperm('Xdir', 'r-xr--r--')
+ " try to create a new file in the directory
+ new Xdir/Xfile2
+ call setline(1, 'two')
+ call assert_fails('write', 'E212:')
+ " try to create a backup file in the directory
+ edit! Xdir/Xfile1
+ set backupdir=./Xdir
+ set patchmode=.orig
+ call assert_fails('write', 'E509:')
+ call setfperm('Xdir', 'rwxr--r--')
+ call delete('Xdir', 'rf')
+ set backupdir& patchmode&
endfunc
" Test for writing a file using invalid file encoding
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index 45134db14f..9dd41224da 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -14,6 +14,12 @@
# include "testing.c.generated.h"
#endif
+static char e_assert_fails_second_arg[]
+ = N_("E856: assert_fails() second argument must be a string or a list with one or two strings");
+static char e_assert_fails_fourth_argument[]
+ = N_("E1115: assert_fails() fourth argument must be a number");
+static char e_assert_fails_fifth_argument[]
+ = N_("E1116: assert_fails() fifth argument must be a string");
static char e_calling_test_garbagecollect_now_while_v_testing_is_not_set[]
= N_("E1142: Calling test_garbagecollect_now() while v:testing is not set");
@@ -68,7 +74,7 @@ static void ga_concat_esc(garray_T *gap, const char_u *p, int clen)
case '\\':
ga_concat(gap, "\\\\"); break;
default:
- if (*p < ' ') {
+ if (*p < ' ' || *p == 0x7f) {
vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p);
ga_concat(gap, (char *)buf);
} else {
@@ -124,7 +130,10 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s
bool did_copy = false;
int omitted = 0;
- if (opt_msg_tv->v_type != VAR_UNKNOWN) {
+ if (opt_msg_tv->v_type != VAR_UNKNOWN
+ && !(opt_msg_tv->v_type == VAR_STRING
+ && (opt_msg_tv->vval.v_string == NULL
+ || *opt_msg_tv->vval.v_string == NUL))) {
tofree = (char_u *)encode_tv2echo(opt_msg_tv, NULL);
ga_concat(gap, (char *)tofree);
xfree(tofree);
@@ -244,13 +253,12 @@ static int assert_match_common(typval_T *argvars, assert_type_T atype)
{
char buf1[NUMBUFLEN];
char buf2[NUMBUFLEN];
+ const int called_emsg_before = called_emsg;
const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1);
const char *const text = tv_get_string_buf_chk(&argvars[1], buf2);
- if (pat == NULL || text == NULL) {
- emsg(_(e_invarg));
- } else if (pattern_match((char *)pat, (char *)text, false)
- != (atype == ASSERT_MATCH)) {
+ if (called_emsg == called_emsg_before
+ && pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) {
garray_T ga;
prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype);
@@ -351,11 +359,12 @@ static int assert_equalfile(typval_T *argvars)
{
char buf1[NUMBUFLEN];
char buf2[NUMBUFLEN];
+ const int called_emsg_before = called_emsg;
const char *const fname1 = tv_get_string_buf_chk(&argvars[0], buf1);
const char *const fname2 = tv_get_string_buf_chk(&argvars[1], buf2);
garray_T ga;
- if (fname1 == NULL || fname2 == NULL) {
+ if (called_emsg > called_emsg_before) {
return 0;
}
@@ -477,11 +486,13 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
garray_T ga;
int save_trylevel = trylevel;
const int called_emsg_before = called_emsg;
+ char *wrong_arg_msg = NULL;
// trylevel must be zero for a ":throw" command to be considered failed
trylevel = 0;
suppress_errthrow = true;
emsg_silent = true;
+ emsg_assert_fails_used = true;
do_cmdline_cmd(cmd);
if (called_emsg == called_emsg_before) {
@@ -493,13 +504,75 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_number = 1;
} else if (argvars[1].v_type != VAR_UNKNOWN) {
char buf[NUMBUFLEN];
- const char *const error = tv_get_string_buf_chk(&argvars[1], buf);
+ const char *expected;
+ bool error_found = false;
+ int error_found_index = 1;
+ char *actual = emsg_assert_fails_msg == NULL ? "[unknown]" : emsg_assert_fails_msg;
+
+ if (argvars[1].v_type == VAR_STRING) {
+ expected = tv_get_string_buf_chk(&argvars[1], buf);
+ error_found = expected == NULL || strstr(actual, expected) == NULL;
+ } else if (argvars[1].v_type == VAR_LIST) {
+ const list_T *const list = argvars[1].vval.v_list;
+ if (list == NULL || tv_list_len(list) < 1 || tv_list_len(list) > 2) {
+ wrong_arg_msg = e_assert_fails_second_arg;
+ goto theend;
+ }
+ const typval_T *tv = TV_LIST_ITEM_TV(tv_list_first(list));
+ expected = tv_get_string_buf_chk(tv, buf);
+ if (!pattern_match(expected, actual, false)) {
+ error_found = true;
+ } else if (tv_list_len(list) == 2) {
+ tv = TV_LIST_ITEM_TV(tv_list_last(list));
+ actual = get_vim_var_str(VV_ERRMSG);
+ expected = tv_get_string_buf_chk(tv, buf);
+ if (!pattern_match(expected, actual, false)) {
+ error_found = true;
+ }
+ }
+ } else {
+ wrong_arg_msg = e_assert_fails_second_arg;
+ goto theend;
+ }
- if (error == NULL
- || strstr(get_vim_var_str(VV_ERRMSG), error) == NULL) {
+ if (!error_found && argvars[2].v_type != VAR_UNKNOWN
+ && argvars[3].v_type != VAR_UNKNOWN) {
+ if (argvars[3].v_type != VAR_NUMBER) {
+ wrong_arg_msg = e_assert_fails_fourth_argument;
+ goto theend;
+ } else if (argvars[3].vval.v_number >= 0
+ && argvars[3].vval.v_number != emsg_assert_fails_lnum) {
+ error_found = true;
+ error_found_index = 3;
+ }
+ if (!error_found && argvars[4].v_type != VAR_UNKNOWN) {
+ if (argvars[4].v_type != VAR_STRING) {
+ wrong_arg_msg = e_assert_fails_fifth_argument;
+ goto theend;
+ } else if (argvars[4].vval.v_string != NULL
+ && !pattern_match(argvars[4].vval.v_string,
+ emsg_assert_fails_context, false)) {
+ error_found = true;
+ error_found_index = 4;
+ }
+ }
+ }
+
+ if (error_found) {
+ typval_T actual_tv;
prepare_assert_error(&ga);
- fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
- get_vim_var_tv(VV_ERRMSG), ASSERT_OTHER);
+ if (error_found_index == 3) {
+ actual_tv.v_type = VAR_NUMBER;
+ actual_tv.vval.v_number = emsg_assert_fails_lnum;
+ } else if (error_found_index == 4) {
+ actual_tv.v_type = VAR_STRING;
+ actual_tv.vval.v_string = emsg_assert_fails_context;
+ } else {
+ actual_tv.v_type = VAR_STRING;
+ actual_tv.vval.v_string = actual;
+ }
+ fill_assert_error(&ga, &argvars[2], NULL,
+ &argvars[error_found_index], &actual_tv, ASSERT_OTHER);
ga_concat(&ga, ": ");
assert_append_cmd_or_arg(&ga, argvars, cmd);
assert_error(&ga);
@@ -508,11 +581,17 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
+theend:
trylevel = save_trylevel;
suppress_errthrow = false;
emsg_silent = false;
emsg_on_display = false;
+ emsg_assert_fails_used = false;
+ XFREE_CLEAR(emsg_assert_fails_msg);
set_vim_var_string(VV_ERRMSG, NULL, 0);
+ if (wrong_arg_msg != NULL) {
+ emsg(_(wrong_arg_msg));
+ }
}
// "assert_false(actual[, msg])" function
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index 408cdc93a7..bed4d55d4e 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -1215,7 +1215,7 @@ static size_t add_cmd_modifier(char *buf, char *mod_str, bool *multi_mods)
/// was added.
///
/// @return the number of bytes added
-size_t add_win_cmd_modifers(char *buf, const cmdmod_T *cmod, bool *multi_mods)
+size_t add_win_cmd_modifiers(char *buf, const cmdmod_T *cmod, bool *multi_mods)
{
size_t result = 0;
@@ -1320,7 +1320,7 @@ size_t uc_mods(char *buf, const cmdmod_T *cmod, bool quote)
}
}
// flags from cmod->cmod_split
- result += add_win_cmd_modifers(buf, cmod, &multi_mods);
+ result += add_win_cmd_modifiers(buf, cmod, &multi_mods);
if (quote && buf != NULL) {
buf += result - 2;
diff --git a/src/nvim/version.c b/src/nvim/version.c
index e76d3ef9bf..98f34ca11f 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -669,7 +669,7 @@ static const int included_patches[] = {
1829,
1828,
// 1827,
- // 1826,
+ 1826,
1825,
1824,
1823,
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 4812b9ef9d..c755f58c4f 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -4213,6 +4213,8 @@ static int leave_tabpage(buf_T *new_curbuf, bool trigger_leave_autocmds)
return FAIL;
}
}
+
+ reset_dragwin();
tp->tp_curwin = curwin;
tp->tp_prevwin = prevwin;
tp->tp_firstwin = firstwin;
@@ -4275,6 +4277,10 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
clear_cmdline = true;
}
+ // If there was a click in a window, it won't be usable for a following
+ // drag.
+ reset_dragwin();
+
// The tabpage line may have appeared or disappeared, may need to resize the frames for that.
// When the Vim window was resized or ROWS_AVAIL changed need to update frame sizes too.
if (curtab->tp_old_Rows_avail != ROWS_AVAIL || (old_off != firstwin->w_winrow)) {