aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ci/common/build.sh2
-rw-r--r--runtime/doc/eval.txt4
-rw-r--r--runtime/doc/options.txt3
-rw-r--r--src/nvim/buffer.c14
-rw-r--r--src/nvim/diff.c83
-rw-r--r--src/nvim/eval.c12
-rw-r--r--src/nvim/mark.c6
-rw-r--r--src/nvim/mbyte.c4
-rw-r--r--src/nvim/strings.c9
-rw-r--r--src/nvim/testdir/setup.vim1
-rw-r--r--src/nvim/testdir/test_diffmode.vim31
-rw-r--r--src/nvim/testdir/test_functions.vim97
-rw-r--r--src/nvim/testdir/test_match.vim14
-rw-r--r--src/nvim/testdir/test_undo.vim132
-rw-r--r--third-party/CMakeLists.txt2
15 files changed, 337 insertions, 77 deletions
diff --git a/ci/common/build.sh b/ci/common/build.sh
index a3cf64d47a..f4313578c9 100644
--- a/ci/common/build.sh
+++ b/ci/common/build.sh
@@ -35,7 +35,7 @@ build_deps() {
elif test -f "${CACHE_MARKER}" ; then
echo "Using third-party dependencies from Travis cache (last update: $(_stat "${CACHE_MARKER}"))."
cp -r "${HOME}/.cache/nvim-deps"/. "${DEPS_BUILD_DIR}"
- cp -r "${HOME}/.cache/nvim-deps-downloads" "${DEPS_DOWNLOAD_DIR}"
+ cp -r "${HOME}/.cache/nvim-deps-downloads"/. "${DEPS_DOWNLOAD_DIR}"
fi
# Even if we're using cached dependencies, run CMake and make to
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index b3ab0a4500..4c0ee6cc66 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2998,8 +2998,8 @@ count({comp}, {expr} [, {ic} [, {start}]]) *count()*
When {ic} is given and it's |TRUE| then case is ignored.
When {comp} is a string then the number of not overlapping
- occurences of {expr} is returned.
-
+ occurrences of {expr} is returned. Zero is returned when
+ {expr} is an empty string.
*cscope_connection()*
cscope_connection([{num} , {dbpath} [, {prepend}]])
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 81726ca46b..cdec599a74 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1960,6 +1960,9 @@ A jump table for the options with a short description can be found at |Q_op|.
vertical Start diff mode with vertical splits (unless
explicitly specified otherwise).
+ hiddenoff Do not use diff mode for a buffer when it
+ becomes hidden.
+
foldcolumn:{n} Set the 'foldcolumn' option to {n} when
starting diff mode. Without this 2 is used.
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 71e04ec0fb..3fadcc75bf 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -505,14 +505,20 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
int nwindows = buf->b_nwindows;
- /* decrease the link count from windows (unless not in any window) */
- if (buf->b_nwindows > 0)
- --buf->b_nwindows;
+ // decrease the link count from windows (unless not in any window)
+ if (buf->b_nwindows > 0) {
+ buf->b_nwindows--;
+ }
+
+ if (diffopt_hiddenoff() && !unload_buf && buf->b_nwindows == 0) {
+ diff_buf_delete(buf); // Clear 'diff' for hidden buffer.
+ }
/* Return when a window is displaying the buffer or when it's not
* unloaded. */
- if (buf->b_nwindows > 0 || !unload_buf)
+ if (buf->b_nwindows > 0 || !unload_buf) {
return;
+ }
if (buf->terminal) {
terminal_close(buf->terminal, NULL);
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 8699c16351..878971a35c 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -44,6 +44,7 @@ static int diff_busy = FALSE; // ex_diffgetput() is busy
#define DIFF_IWHITE 4 // ignore change in white space
#define DIFF_HORIZONTAL 8 // horizontal splits
#define DIFF_VERTICAL 16 // vertical splits
+#define DIFF_HIDDEN_OFF 32 // diffoff when hidden
static int diff_flags = DIFF_FILLER;
#define LBUFLEN 50 // length of line in diff file
@@ -1597,6 +1598,34 @@ static bool diff_equal_entry(diff_T *dp, int idx1, int idx2)
return true;
}
+// Compare the characters at "p1" and "p2". If they are equal (possibly
+// ignoring case) return true and set "len" to the number of bytes.
+static bool diff_equal_char(const char_u *const p1, const char_u *const p2,
+ int *const len)
+{
+ const int l = utfc_ptr2len(p1);
+
+ if (l != utfc_ptr2len(p2)) {
+ return false;
+ }
+ if (l > 1) {
+ if (STRNCMP(p1, p2, l) != 0
+ && (!(diff_flags & DIFF_ICASE)
+ || utf_fold(utf_ptr2char(p1)) != utf_fold(utf_ptr2char(p2)))) {
+ return false;
+ }
+ *len = l;
+ } else {
+ if ((*p1 != *p2)
+ && (!(diff_flags & DIFF_ICASE)
+ || TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2))) {
+ return false;
+ }
+ *len = 1;
+ }
+ return true;
+}
+
/// Compare strings "s1" and "s2" according to 'diffopt'.
/// Return non-zero when they are different.
///
@@ -1623,30 +1652,12 @@ static int diff_cmp(char_u *s1, char_u *s2)
p1 = skipwhite(p1);
p2 = skipwhite(p2);
} else {
- int l = (*mb_ptr2len)(p1);
- if (l != (*mb_ptr2len)(p2)) {
+ int l;
+ if (!diff_equal_char(p1, p2, &l)) {
break;
}
-
- if (l > 1) {
- if ((STRNCMP(p1, p2, l) != 0)
- && (!enc_utf8
- || !(diff_flags & DIFF_ICASE)
- || (utf_fold(utf_ptr2char(p1))
- != utf_fold(utf_ptr2char(p2))))) {
- break;
- }
- p1 += l;
- p2 += l;
- } else {
- if ((*p1 != *p2)
- && (!(diff_flags & DIFF_ICASE)
- || (TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2)))) {
- break;
- }
- ++p1;
- ++p2;
- }
+ p1 += l;
+ p2 += l;
}
}
@@ -1828,6 +1839,9 @@ int diffopt_changed(void)
} else if ((STRNCMP(p, "foldcolumn:", 11) == 0) && ascii_isdigit(p[11])) {
p += 11;
diff_foldcolumn_new = getdigits_int(&p);
+ } else if (STRNCMP(p, "hiddenoff", 9) == 0) {
+ p += 9;
+ diff_flags_new |= DIFF_HIDDEN_OFF;
}
if ((*p != ',') && (*p != NUL)) {
@@ -1870,6 +1884,12 @@ bool diffopt_horizontal(void)
return (diff_flags & DIFF_HORIZONTAL) != 0;
}
+// Return true if 'diffopt' contains "hiddenoff".
+bool diffopt_hiddenoff(void)
+{
+ return (diff_flags & DIFF_HIDDEN_OFF) != 0;
+}
+
/// Find the difference within a changed line.
///
/// @param wp window whose current buffer to check
@@ -1887,6 +1907,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
int ei_org;
int ei_new;
bool added = true;
+ int l;
// Make a copy of the line, the next ml_get() will invalidate it.
char_u *line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, FALSE));
@@ -1933,11 +1954,11 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
si_org = (int)(skipwhite(line_org + si_org) - line_org);
si_new = (int)(skipwhite(line_new + si_new) - line_new);
} else {
- if (line_org[si_org] != line_new[si_new]) {
+ if (!diff_equal_char(line_org + si_org, line_new + si_new, &l)) {
break;
}
- ++si_org;
- ++si_new;
+ si_org += l;
+ si_new += l;
}
}
@@ -1972,11 +1993,17 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
ei_new--;
}
} else {
- if (line_org[ei_org] != line_new[ei_new]) {
+ const char_u *p1 = line_org + ei_org;
+ const char_u *p2 = line_new + ei_new;
+
+ p1 -= utf_head_off(line_org, p1);
+ p2 -= utf_head_off(line_new, p2);
+
+ if (!diff_equal_char(p1, p2, &l)) {
break;
}
- ei_org--;
- ei_new--;
+ ei_org -= l;
+ ei_new -= l;
}
}
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 9765b04922..22cb544f54 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -7603,7 +7603,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char_u *expr = (char_u *)tv_get_string_chk(&argvars[1]);
const char_u *p = argvars[0].vval.v_string;
- if (!error && expr != NULL && p != NULL) {
+ if (!error && expr != NULL && *expr != NUL && p != NULL) {
if (ic) {
const size_t len = STRLEN(expr);
@@ -12227,7 +12227,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
long start = 0;
long nth = 1;
colnr_T startcol = 0;
- int match = 0;
+ bool match = false;
list_T *l = NULL;
listitem_T *li = NULL;
long idx = 0;
@@ -12325,7 +12325,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
for (;; ) {
if (l != NULL) {
if (li == NULL) {
- match = FALSE;
+ match = false;
break;
}
xfree(tofree);
@@ -12351,7 +12351,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
startcol = (colnr_T)(regmatch.startp[0]
+ (*mb_ptr2len)(regmatch.startp[0]) - str);
if (startcol > (colnr_T)len || str + startcol <= regmatch.startp[0]) {
- match = FALSE;
+ match = false;
break;
}
}
@@ -12424,13 +12424,13 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
vim_regfree(regmatch.regprog);
}
- if (type == kSomeMatchStrPos && l == NULL) {
+theend:
+ if (type == kSomeMatchStrPos && l == NULL && rettv->vval.v_list != NULL) {
// matchstrpos() without a list: drop the second item
list_T *const ret_l = rettv->vval.v_list;
tv_list_item_remove(ret_l, TV_LIST_ITEM_NEXT(ret_l, tv_list_first(ret_l)));
}
-theend:
xfree(tofree);
p_cpo = save_cpo;
}
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index b9c91de2a8..3861d9ceb8 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -1465,7 +1465,11 @@ void mark_mb_adjustpos(buf_T *buf, pos_T *lp)
{
if (lp->col > 0 || lp->coladd > 1) {
const char_u *const p = ml_get_buf(buf, lp->lnum, false);
- lp->col -= (*mb_head_off)(p, p + lp->col);
+ if (*p == NUL || (int)STRLEN(p) < lp->col) {
+ lp->col = 0;
+ } else {
+ lp->col -= (*mb_head_off)(p, p + lp->col);
+ }
// Reset "coladd" when the cursor would be on the right half of a
// double-wide character.
if (lp->coladd == 1
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 7c196831ba..15fe51cad1 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -566,7 +566,9 @@ int utf_off2cells(unsigned off, unsigned max_off)
/// Convert a UTF-8 byte sequence to a wide character
///
/// If the sequence is illegal or truncated by a NUL then the first byte is
-/// returned. Does not include composing characters for obvious reasons.
+/// returned.
+/// For an overlong sequence this may return zero.
+/// Does not include composing characters for obvious reasons.
///
/// @param[in] p String to convert.
///
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 3b075f8b70..f24de72743 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -344,14 +344,17 @@ char *strcase_save(const char *const orig, bool upper)
char *p = res;
while (*p != NUL) {
- int l;
-
int c = utf_ptr2char((const char_u *)p);
+ int l = utf_ptr2len((const char_u *)p);
+ if (c == 0) {
+ // overlong sequence, use only the first byte
+ c = *p;
+ l = 1;
+ }
int uc = upper ? mb_toupper(c) : mb_tolower(c);
// Reallocate string when byte count changes. This is rare,
// thus it's OK to do another malloc()/free().
- l = utf_ptr2len((const char_u *)p);
int newl = utf_char2len(uc);
if (newl != l) {
// TODO(philix): use xrealloc() in strup_save()
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index 51bf8ce0fc..b38f50b501 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -9,6 +9,7 @@ let s:did_load = 1
" Align Nvim defaults to Vim.
set sidescroll=0
set directory^=.
+set undodir^=.
set backspace=
set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd
set listchars=eol:$
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index d95b29759e..90fd34d93e 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -279,13 +279,13 @@ func Test_diffopt_icase()
set diffopt=icase,foldcolumn:0
e one
- call setline(1, ['One', 'Two', 'Three', 'Four'])
+ call setline(1, ['One', 'Two', 'Three', 'Four', 'Fi#ve'])
redraw
let normattr = screenattr(1, 1)
diffthis
botright vert new two
- call setline(1, ['one', 'TWO', 'Three ', 'Four'])
+ call setline(1, ['one', 'TWO', 'Three ', 'Four', 'fI=VE'])
diffthis
redraw
@@ -294,6 +294,10 @@ func Test_diffopt_icase()
call assert_notequal(normattr, screenattr(3, 1))
call assert_equal(normattr, screenattr(4, 1))
+ let dtextattr = screenattr(5, 3)
+ call assert_notequal(dtextattr, screenattr(5, 1))
+ call assert_notequal(dtextattr, screenattr(5, 5))
+
diffoff!
%bwipe!
set diffopt&
@@ -371,6 +375,29 @@ func Test_diffopt_vertical()
%bwipe
endfunc
+func Test_diffopt_hiddenoff()
+ set diffopt=filler,foldcolumn:0,hiddenoff
+ e! one
+ call setline(1, ['Two', 'Three'])
+ redraw
+ let normattr = screenattr(1, 1)
+ diffthis
+ botright vert new two
+ call setline(1, ['One', 'Four'])
+ diffthis
+ redraw
+ call assert_notequal(normattr, screenattr(1, 1))
+ set hidden
+ close
+ redraw
+ " should not diffing with hidden buffer two while 'hiddenoff' is enabled
+ call assert_equal(normattr, screenattr(1, 1))
+
+ bwipe!
+ bwipe!
+ set hidden& diffopt&
+endfunc
+
func Test_diffoff_hidden()
set diffopt=filler,foldcolumn:0
e! one
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 3b16f2ce9f..6d0a6b9d5e 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -96,6 +96,30 @@ func Test_min()
" call assert_fails('call min(v:none)', 'E712:')
endfunc
+func Test_strwidth()
+ for aw in ['single', 'double']
+ exe 'set ambiwidth=' . aw
+ call assert_equal(0, strwidth(''))
+ call assert_equal(1, strwidth("\t"))
+ call assert_equal(3, strwidth('Vim'))
+ call assert_equal(4, strwidth(1234))
+ call assert_equal(5, strwidth(-1234))
+
+ if has('multi_byte')
+ call assert_equal(2, strwidth('😉'))
+ call assert_equal(17, strwidth('Eĥoŝanĝo ĉiuĵaŭde'))
+ call assert_equal((aw == 'single') ? 6 : 7, strwidth('Straße'))
+ endif
+
+ call assert_fails('call strwidth({->0})', 'E729:')
+ call assert_fails('call strwidth([])', 'E730:')
+ call assert_fails('call strwidth({})', 'E731:')
+ call assert_fails('call strwidth(1.2)', 'E806:')
+ endfor
+
+ set ambiwidth&
+endfunc
+
func Test_str2nr()
call assert_equal(0, str2nr(''))
call assert_equal(1, str2nr('1'))
@@ -215,6 +239,21 @@ func Test_setbufvar_options()
bwipe!
endfunc
+func Test_pathshorten()
+ call assert_equal('', pathshorten(''))
+ call assert_equal('foo', pathshorten('foo'))
+ call assert_equal('/foo', pathshorten('/foo'))
+ call assert_equal('f/', pathshorten('foo/'))
+ call assert_equal('f/bar', pathshorten('foo/bar'))
+ call assert_equal('f/b/foobar', pathshorten('foo/bar/foobar'))
+ call assert_equal('/f/b/foobar', pathshorten('/foo/bar/foobar'))
+ call assert_equal('.f/bar', pathshorten('.foo/bar'))
+ call assert_equal('~f/bar', pathshorten('~foo/bar'))
+ call assert_equal('~.f/bar', pathshorten('~.foo/bar'))
+ call assert_equal('.~f/bar', pathshorten('.~foo/bar'))
+ call assert_equal('~/f/bar', pathshorten('~/foo/bar'))
+endfunc
+
func Test_strpart()
call assert_equal('de', strpart('abcdefg', 3, 2))
call assert_equal('ab', strpart('abcdefg', -2, 4))
@@ -299,6 +338,11 @@ func Test_tolower()
" Ⱥ (U+023A) and Ⱦ (U+023E) are the *only* code points to increase
" in length (2 to 3 bytes) when lowercased. So let's test them.
call assert_equal("ⱥ ⱦ", tolower("Ⱥ Ⱦ"))
+
+ " This call to tolower with invalid utf8 sequence used to cause access to
+ " invalid memory.
+ call tolower("\xC0\x80\xC0")
+ call tolower("123\xC0\x80\xC0")
endfunc
func Test_toupper()
@@ -369,6 +413,11 @@ func Test_toupper()
call assert_equal("ZŹŻŽƵẐẔ", toupper("ZŹŻŽƵẐẔ"))
call assert_equal("Ⱥ Ⱦ", toupper("ⱥ ⱦ"))
+
+ " This call to toupper with invalid utf8 sequence used to cause access to
+ " invalid memory.
+ call toupper("\xC0\x80\xC0")
+ call toupper("123\xC0\x80\xC0")
endfunc
" Tests for the mode() function
@@ -573,10 +622,46 @@ func Test_strridx()
call assert_equal(-1, strridx('hello', 'hello world'))
endfunc
+func Test_match_func()
+ call assert_equal(4, match('testing', 'ing'))
+ call assert_equal(4, match('testing', 'ing', 2))
+ call assert_equal(-1, match('testing', 'ing', 5))
+ 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'))
+endfunc
+
func Test_matchend()
call assert_equal(7, matchend('testing', 'ing'))
call assert_equal(7, matchend('testing', 'ing', 2))
call assert_equal(-1, matchend('testing', 'ing', 5))
+ call assert_equal(-1, matchend('testing', 'ing', 8))
+ call assert_equal(match(['vim', 'testing', 'execute'], 'ing'), matchend(['vim', 'testing', 'execute'], 'ing'))
+ call assert_equal(match(['vim', 'testing', 'execute'], 'img'), matchend(['vim', 'testing', 'execute'], 'img'))
+endfunc
+
+func Test_matchlist()
+ call assert_equal(['acd', 'a', '', 'c', 'd', '', '', '', '', ''], matchlist('acd', '\(a\)\?\(b\)\?\(c\)\?\(.*\)'))
+ call assert_equal(['d', '', '', '', 'd', '', '', '', '', ''], matchlist('acd', '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2))
+ call assert_equal([], matchlist('acd', '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 4))
+endfunc
+
+func Test_matchstr()
+ call assert_equal('ing', matchstr('testing', 'ing'))
+ call assert_equal('ing', matchstr('testing', 'ing', 2))
+ call assert_equal('', matchstr('testing', 'ing', 5))
+ call assert_equal('', matchstr('testing', 'ing', 8))
+ call assert_equal('testing', matchstr(['vim', 'testing', 'execute'], 'ing'))
+ call assert_equal('', matchstr(['vim', 'testing', 'execute'], 'img'))
+endfunc
+
+func Test_matchstrpos()
+ call assert_equal(['ing', 4, 7], matchstrpos('testing', 'ing'))
+ call assert_equal(['ing', 4, 7], matchstrpos('testing', 'ing', 2))
+ call assert_equal(['', -1, -1], matchstrpos('testing', 'ing', 5))
+ call assert_equal(['', -1, -1], matchstrpos('testing', 'ing', 8))
+ call assert_equal(['ing', 1, 4, 7], matchstrpos(['vim', 'testing', 'execute'], 'ing'))
+ call assert_equal(['', -1, -1, -1], matchstrpos(['vim', 'testing', 'execute'], 'img'))
endfunc
func Test_nextnonblank_prevnonblank()
@@ -687,6 +772,7 @@ func Test_count()
call assert_equal(0, count("foo", "O"))
call assert_equal(2, count("foo", "O", 1))
call assert_equal(2, count("fooooo", "oo"))
+ call assert_equal(0, count("foo", ""))
endfunc
func Test_changenr()
@@ -770,6 +856,17 @@ func Test_col()
bw!
endfunc
+func Test_inputlist()
+ call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>1\<cr>", 'tx')
+ call assert_equal(1, c)
+ call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>2\<cr>", 'tx')
+ call assert_equal(2, c)
+ call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>3\<cr>", 'tx')
+ call assert_equal(3, c)
+
+ call assert_fails('call inputlist("")', 'E686:')
+endfunc
+
func Test_balloon_show()
if has('balloon_eval')
" This won't do anything but must not crash either.
diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim
index 066bb2f6a1..e608a2e58b 100644
--- a/src/nvim/testdir/test_match.vim
+++ b/src/nvim/testdir/test_match.vim
@@ -1,5 +1,5 @@
" Test for :match, :2match, :3match, clearmatches(), getmatches(), matchadd(),
-" matchaddpos(), matcharg(), matchdelete(), matchstrpos() and setmatches().
+" matchaddpos(), matcharg(), matchdelete(), and setmatches().
function Test_match()
highlight MyGroup1 term=bold ctermbg=red guibg=red
@@ -150,18 +150,6 @@ function Test_match()
highlight MyGroup3 NONE
endfunc
-func Test_matchstrpos()
- call assert_equal(['ing', 4, 7], matchstrpos('testing', 'ing'))
-
- call assert_equal(['ing', 4, 7], matchstrpos('testing', 'ing', 2))
-
- call assert_equal(['', -1, -1], matchstrpos('testing', 'ing', 5))
-
- call assert_equal(['ing', 1, 4, 7], matchstrpos(['vim', 'testing', 'execute'], 'ing'))
-
- call assert_equal(['', -1, -1, -1], matchstrpos(['vim', 'testing', 'execute'], 'img'))
-endfunc
-
func Test_matchaddpos()
syntax on
set hlsearch
diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim
index 2bc6073d52..f31499607b 100644
--- a/src/nvim/testdir/test_undo.vim
+++ b/src/nvim/testdir/test_undo.vim
@@ -4,28 +4,88 @@
" Also tests :earlier and :later.
func Test_undotree()
- exe "normal Aabc\<Esc>"
+ new
+
+ normal! Aabc
+ set ul=100
+ let d = undotree()
+ call assert_equal(1, d.seq_last)
+ call assert_equal(1, d.seq_cur)
+ call assert_equal(0, d.save_last)
+ call assert_equal(0, d.save_cur)
+ call assert_equal(1, len(d.entries))
+ call assert_equal(1, d.entries[0].newhead)
+ call assert_equal(1, d.entries[0].seq)
+ call assert_true(d.entries[0].time <= d.time_cur)
+
+ normal! Adef
+ set ul=100
+ let d = undotree()
+ call assert_equal(2, d.seq_last)
+ call assert_equal(2, d.seq_cur)
+ call assert_equal(0, d.save_last)
+ call assert_equal(0, d.save_cur)
+ call assert_equal(2, len(d.entries))
+ call assert_equal(1, d.entries[0].seq)
+ call assert_equal(1, d.entries[1].newhead)
+ call assert_equal(2, d.entries[1].seq)
+ call assert_true(d.entries[1].time <= d.time_cur)
+
+ undo
set ul=100
- exe "normal Adef\<Esc>"
+ let d = undotree()
+ call assert_equal(2, d.seq_last)
+ call assert_equal(1, d.seq_cur)
+ call assert_equal(0, d.save_last)
+ call assert_equal(0, d.save_cur)
+ call assert_equal(2, len(d.entries))
+ call assert_equal(1, d.entries[0].seq)
+ call assert_equal(1, d.entries[1].curhead)
+ call assert_equal(1, d.entries[1].newhead)
+ call assert_equal(2, d.entries[1].seq)
+ call assert_true(d.entries[1].time == d.time_cur)
+
+ normal! Aghi
set ul=100
+ let d = undotree()
+ call assert_equal(3, d.seq_last)
+ call assert_equal(3, d.seq_cur)
+ call assert_equal(0, d.save_last)
+ call assert_equal(0, d.save_cur)
+ call assert_equal(2, len(d.entries))
+ call assert_equal(1, d.entries[0].seq)
+ call assert_equal(2, d.entries[1].alt[0].seq)
+ call assert_equal(1, d.entries[1].newhead)
+ call assert_equal(3, d.entries[1].seq)
+ call assert_true(d.entries[1].time <= d.time_cur)
+
undo
+ set ul=100
let d = undotree()
- call assert_true(d.seq_last > 0)
- call assert_true(d.seq_cur > 0)
- call assert_true(d.seq_cur < d.seq_last)
- call assert_true(len(d.entries) > 0)
- " TODO: check more members of d
+ call assert_equal(3, d.seq_last)
+ call assert_equal(1, d.seq_cur)
+ call assert_equal(0, d.save_last)
+ call assert_equal(0, d.save_cur)
+ call assert_equal(2, len(d.entries))
+ call assert_equal(1, d.entries[0].seq)
+ call assert_equal(2, d.entries[1].alt[0].seq)
+ call assert_equal(1, d.entries[1].curhead)
+ call assert_equal(1, d.entries[1].newhead)
+ call assert_equal(3, d.entries[1].seq)
+ call assert_true(d.entries[1].time == d.time_cur)
w! Xtest
- call assert_equal(d.save_last + 1, undotree().save_last)
+ let d = undotree()
+ call assert_equal(1, d.save_cur)
+ call assert_equal(1, d.save_last)
call delete('Xtest')
- bwipe Xtest
+ bwipe! Xtest
endfunc
func FillBuffer()
for i in range(1,13)
put=i
- " Set 'undolevels' to split undo.
+ " Set 'undolevels' to split undo.
exe "setg ul=" . &g:ul
endfor
endfunc
@@ -135,19 +195,19 @@ func Test_undolist()
new
set ul=100
- let a=execute('undolist')
+ let a = execute('undolist')
call assert_equal("\nNothing to undo", a)
" 1 leaf (2 changes).
call feedkeys('achange1', 'xt')
call feedkeys('achange2', 'xt')
- let a=execute('undolist')
+ let a = execute('undolist')
call assert_match("^\nnumber changes when *saved\n *2 *2 .*$", a)
" 2 leaves.
call feedkeys('u', 'xt')
call feedkeys('achange3\<Esc>', 'xt')
- let a=execute('undolist')
+ let a = execute('undolist')
call assert_match("^\nnumber changes when *saved\n *2 *2 *.*\n *3 *2 .*$", a)
close!
endfunc
@@ -270,7 +330,7 @@ endfunc
" Also test this in an empty buffer.
func Test_cmd_in_reg_undo()
enew!
- let @a="Ox\<Esc>jAy\<Esc>kdd"
+ let @a = "Ox\<Esc>jAy\<Esc>kdd"
edit +/^$ test_undo.vim
normal @au
call assert_equal(0, &modified)
@@ -279,7 +339,7 @@ func Test_cmd_in_reg_undo()
normal @au
call assert_equal(0, &modified)
only!
- let @a=''
+ let @a = ''
endfunc
func Test_redo_empty_line()
@@ -288,3 +348,45 @@ func Test_redo_empty_line()
exe "norm."
bwipe!
endfunc
+
+" This used to cause an illegal memory access
+func Test_undo_append()
+ new
+ call feedkeys("axx\<Esc>v", 'xt')
+ undo
+ norm o
+ quit
+endfunc
+
+funct Test_undofile()
+ " Test undofile() without setting 'undodir'.
+ if has('persistent_undo')
+ call assert_equal(fnamemodify('.Xundofoo.un~', ':p'), undofile('Xundofoo'))
+ else
+ call assert_equal('', undofile('Xundofoo'))
+ endif
+ call assert_equal('', undofile(''))
+
+ " Test undofile() with 'undodir' set to to an existing directory.
+ call mkdir('Xundodir')
+ set undodir=Xundodir
+ let cwd = getcwd()
+ if has('win32')
+ " Replace windows drive such as C:... into C%...
+ let cwd = substitute(cwd, '^\([A-Z]\):', '\1%', 'g')
+ endif
+ let pathsep = has('win32') ? '\' : '/'
+ let cwd = substitute(cwd . pathsep . 'Xundofoo', pathsep, '%', 'g')
+ if has('persistent_undo')
+ call assert_equal('Xundodir' . pathsep . cwd, undofile('Xundofoo'))
+ else
+ call assert_equal('', undofile('Xundofoo'))
+ endif
+ call assert_equal('', undofile(''))
+ call delete('Xundodir', 'd')
+
+ " Test undofile() with 'undodir' set to a non-existing directory.
+ " call assert_equal('', undofile('Xundofoo'))
+
+ set undodir&
+endfunc
diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt
index cf49a67f60..83d7b69b45 100644
--- a/third-party/CMakeLists.txt
+++ b/third-party/CMakeLists.txt
@@ -147,7 +147,7 @@ set(JEMALLOC_SHA256 9409d85664b4f135b77518b0b118c549009dc10f6cba14557d170476611f
set(LUV_URL https://github.com/luvit/luv/archive/1.9.1-1.tar.gz)
set(LUV_SHA256 562b9efaad30aa051a40eac9ade0c3df48bb8186763769abe47ec3fb3edb1268)
-set(GPERF_URL https://ftp.gnu.org/pub/gnu/gperf/gperf-3.1.tar.gz)
+set(GPERF_URL https://github.com/neovim/deps/raw/ff5b4b18a87397a8564016071ae64f64bcd8c635/opt/gperf-3.1.tar.gz)
set(GPERF_SHA256 588546b945bba4b70b6a3a616e80b4ab466e3f33024a352fc2198112cdbb3ae2)
# 7za.exe cat.exe curl.exe ca-bundle.crt diff.exe tee.exe tidy.exe xxd.exe