263 lines
7.8 KiB
Diff
263 lines
7.8 KiB
Diff
diff --git a/src/insexpand.c b/src/insexpand.c
|
||
index 8d76c68..2ce675d 100644
|
||
--- a/src/insexpand.c
|
||
+++ b/src/insexpand.c
|
||
@@ -2162,7 +2162,8 @@ ins_compl_next_buf(buf_T *buf, int flag)
|
||
|
||
if (flag == 'w') // just windows
|
||
{
|
||
- if (buf == curbuf || wp == NULL) // first call for this flag/expansion
|
||
+ if (buf == curbuf || !win_valid(wp))
|
||
+ // first call for this flag/expansion or window was closed
|
||
wp = curwin;
|
||
while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin
|
||
&& wp->w_buffer->b_scanned)
|
||
@@ -2675,10 +2676,12 @@ ins_compl_get_exp(pos_T *ini)
|
||
{
|
||
static pos_T first_match_pos;
|
||
static pos_T last_match_pos;
|
||
- static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete'
|
||
+ static char_u *e_cpt; // curr. entry in 'complete'
|
||
+ static char_u *e_cpt_copy; // copy of curr. entry in 'complete'
|
||
static int found_all = FALSE; // Found all matches of a
|
||
// certain type.
|
||
static buf_T *ins_buf = NULL; // buffer being scanned
|
||
+ static int st_cleared = FALSE;
|
||
|
||
pos_T *pos;
|
||
char_u **matches;
|
||
@@ -2697,12 +2700,34 @@ ins_compl_get_exp(pos_T *ini)
|
||
|
||
if (!compl_started)
|
||
{
|
||
- FOR_ALL_BUFFERS(ins_buf)
|
||
- ins_buf->b_scanned = 0;
|
||
+ buf_T *buf;
|
||
+
|
||
+ FOR_ALL_BUFFERS(buf)
|
||
+ buf->b_scanned = 0;
|
||
+ if (!st_cleared)
|
||
+ {
|
||
+ // this code does not match upstream commit, upstream clears struct `st`
|
||
+ // which includes the members which are arguments of `CLEAR_FIELD()` macros
|
||
+ // below. The code below should do the same thing as upstream `CLEAR_FIELD(st)`
|
||
+ CLEAR_FIELD(e_cpt);
|
||
+ CLEAR_FIELD(e_cpt_copy);
|
||
+ CLEAR_FIELD(ins_buf);
|
||
+ CLEAR_FIELD(pos);
|
||
+ CLEAR_FIELD(set_match_pos);
|
||
+ CLEAR_FIELD(first_match_pos);
|
||
+ CLEAR_FIELD(last_match_pos);
|
||
+ CLEAR_FIELD(found_all);
|
||
+ CLEAR_FIELD(dict);
|
||
+ CLEAR_FIELD(dict_f);
|
||
+ st_cleared = TRUE;
|
||
+ }
|
||
found_all = FALSE;
|
||
ins_buf = curbuf;
|
||
- e_cpt = (compl_cont_status & CONT_LOCAL)
|
||
- ? (char_u *)"." : curbuf->b_p_cpt;
|
||
+ vim_free(e_cpt_copy);
|
||
+ // Make a copy of 'complete', if case the buffer is wiped out.
|
||
+ e_cpt_copy = vim_strsave((compl_cont_status & CONT_LOCAL)
|
||
+ ? (char_u *)"." : curbuf->b_p_cpt);
|
||
+ e_cpt = e_cpt_copy == NULL ? (char_u *)"" : e_cpt_copy;
|
||
last_match_pos = first_match_pos = *ini;
|
||
}
|
||
else if (ins_buf != curbuf && !buf_valid(ins_buf))
|
||
@@ -3100,7 +3125,7 @@ ins_compl_get_exp(pos_T *ini)
|
||
else
|
||
{
|
||
// Mark a buffer scanned when it has been scanned completely
|
||
- if (type == 0 || type == CTRL_X_PATH_PATTERNS)
|
||
+ if (buf_valid(ins_buf) && (type == 0 || type == CTRL_X_PATH_PATTERNS))
|
||
ins_buf->b_scanned = TRUE;
|
||
|
||
compl_started = FALSE;
|
||
@@ -3210,6 +3235,7 @@ ins_compl_next(
|
||
int found_end = FALSE;
|
||
int advance;
|
||
int started = compl_started;
|
||
+ buf_T *orig_curbuf = curbuf;
|
||
|
||
// When user complete function return -1 for findstart which is next
|
||
// time of 'always', compl_shown_match become NULL.
|
||
@@ -3342,6 +3368,13 @@ ins_compl_next(
|
||
}
|
||
}
|
||
|
||
+ if (curbuf != orig_curbuf)
|
||
+ {
|
||
+ // In case some completion function switched buffer, don't want to
|
||
+ // insert the completion elsewhere.
|
||
+ return -1;
|
||
+ }
|
||
+
|
||
// Insert the text of the new completion, or the compl_leader.
|
||
if (compl_no_insert && !started)
|
||
{
|
||
diff --git a/src/move.c b/src/move.c
|
||
index 8ac1d0e..55cbd24 100644
|
||
--- a/src/move.c
|
||
+++ b/src/move.c
|
||
@@ -649,6 +649,7 @@ cursor_valid(void)
|
||
void
|
||
validate_cursor(void)
|
||
{
|
||
+ check_cursor_lnum();
|
||
check_cursor_moved(curwin);
|
||
if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
|
||
curs_columns(TRUE);
|
||
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
|
||
index 108c528..e800864 100644
|
||
--- a/src/testdir/Make_all.mak
|
||
+++ b/src/testdir/Make_all.mak
|
||
@@ -92,6 +92,7 @@ NEW_TESTS = \
|
||
test_conceal \
|
||
test_const \
|
||
test_cpoptions \
|
||
+ test_crash \
|
||
test_crypt \
|
||
test_cscope \
|
||
test_cursor_func \
|
||
@@ -348,6 +349,7 @@ NEW_TESTS_RES = \
|
||
test_conceal.res \
|
||
test_const.res \
|
||
test_cpoptions.res \
|
||
+ test_crash.res \
|
||
test_crypt.res \
|
||
test_cscope.res \
|
||
test_cursor_func.res \
|
||
diff --git a/src/testdir/crash/poc_tagfunc.vim b/src/testdir/crash/poc_tagfunc.vim
|
||
new file mode 100644
|
||
index 0000000..49d9b6f
|
||
--- /dev/null
|
||
+++ b/src/testdir/crash/poc_tagfunc.vim
|
||
@@ -0,0 +1,6 @@
|
||
+fu Tagfunc(t,f,o)
|
||
+ bw
|
||
+endf
|
||
+set tagfunc=Tagfunc
|
||
+n0
|
||
+sil0norm0i
|
||
diff --git a/src/testdir/term_util.vim b/src/testdir/term_util.vim
|
||
index 8541ef3..3a06019 100644
|
||
--- a/src/testdir/term_util.vim
|
||
+++ b/src/testdir/term_util.vim
|
||
@@ -54,6 +54,7 @@ endfunc
|
||
" "cols" - width of the terminal window (max. 78)
|
||
" "statusoff" - number of lines the status is offset from default
|
||
" "wait_for_ruler" - if zero then don't wait for ruler to show
|
||
+" "cmd" - run any other command, e.g. "xxd" (used in xxd test)
|
||
func RunVimInTerminal(arguments, options)
|
||
" If Vim doesn't exit a swap file remains, causing other tests to fail.
|
||
" Remove it here.
|
||
@@ -88,7 +89,11 @@ func RunVimInTerminal(arguments, options)
|
||
let reset_u7 = ' --cmd "set t_u7=" '
|
||
endif
|
||
|
||
- let cmd = GetVimCommandCleanTerm() .. reset_u7 .. a:arguments
|
||
+ if empty(get(a:options, 'cmd', ''))
|
||
+ let cmd = GetVimCommandCleanTerm() .. reset_u7 .. a:arguments
|
||
+ else
|
||
+ let cmd = get(a:options, 'cmd')
|
||
+ endif
|
||
|
||
let options = #{curwin: 1}
|
||
if &termwinsize == ''
|
||
@@ -114,7 +119,7 @@ func RunVimInTerminal(arguments, options)
|
||
|
||
call TermWait(buf)
|
||
|
||
- if get(a:options, 'wait_for_ruler', 1)
|
||
+ if get(a:options, 'wait_for_ruler', 1) && empty(get(a:options, 'cmd', ''))
|
||
" Wait for "All" or "Top" of the ruler to be shown in the last line or in
|
||
" the status line of the last window. This can be quite slow (e.g. when
|
||
" using valgrind).
|
||
diff --git a/src/testdir/test_crash.vim b/src/testdir/test_crash.vim
|
||
new file mode 100644
|
||
index 0000000..4be1b49
|
||
--- /dev/null
|
||
+++ b/src/testdir/test_crash.vim
|
||
@@ -0,0 +1,40 @@
|
||
+" Some tests, that used to crash Vim
|
||
+source check.vim
|
||
+source screendump.vim
|
||
+
|
||
+CheckScreendump
|
||
+
|
||
+func Test_crash1()
|
||
+ if !executable('sh')
|
||
+ throw 'Skipped: sh not executable!'
|
||
+ endif
|
||
+ " The following used to crash Vim
|
||
+ let opts = #{cmd: 'sh'}
|
||
+ let vim = GetVimProg()
|
||
+
|
||
+ let buf = RunVimInTerminal('sh', opts)
|
||
+
|
||
+ let file = 'crash/poc_tagfunc.vim'
|
||
+ let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
|
||
+ let args = printf(cmn_args, vim, file)
|
||
+ call term_sendkeys(buf, args ..
|
||
+ \ ' || echo "crash 5: [OK]" >> X_crash1_result.txt' .. "\<cr>")
|
||
+
|
||
+ call TermWait(buf, 100)
|
||
+
|
||
+ " clean up
|
||
+ exe buf .. "bw!"
|
||
+
|
||
+ sp X_crash1_result.txt
|
||
+
|
||
+ let expected = [
|
||
+ \ 'crash 5: [OK]',
|
||
+ \ ]
|
||
+
|
||
+ call assert_equal(expected, getline(1, '$'))
|
||
+ bw!
|
||
+
|
||
+ call delete('X_crash1_result.txt')
|
||
+endfunc
|
||
+
|
||
+" vim: shiftwidth=2 sts=2 expandtab
|
||
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
|
||
index 613e952..24232d8 100644
|
||
--- a/src/testdir/test_ins_complete.vim
|
||
+++ b/src/testdir/test_ins_complete.vim
|
||
@@ -496,9 +496,8 @@ func Test_pum_with_preview_win()
|
||
|
||
call writefile(lines, 'Xpreviewscript')
|
||
let buf = RunVimInTerminal('-S Xpreviewscript', #{rows: 12})
|
||
- call TermWait(buf, 50)
|
||
call term_sendkeys(buf, "Gi\<C-X>\<C-O>")
|
||
- call TermWait(buf, 100)
|
||
+ call TermWait(buf, 200)
|
||
call term_sendkeys(buf, "\<C-N>")
|
||
call VerifyScreenDump(buf, 'Test_pum_with_preview_win', {})
|
||
|
||
@@ -724,7 +723,23 @@ func Test_z1_complete_no_history()
|
||
exe "normal owh\<C-X>\<C-K>"
|
||
exe "normal owh\<C-N>"
|
||
call assert_equal(currmess, execute('messages'))
|
||
- close!
|
||
+ bwipe!
|
||
+endfunc
|
||
+
|
||
+func s:Tagfunc(t,f,o)
|
||
+ bwipe!
|
||
+ return []
|
||
+endfunc
|
||
+
|
||
+" This was using freed memory, since 'complete' was in a wiped out buffer.
|
||
+" Also using a window that was closed.
|
||
+func Test_tagfunc_wipes_out_buffer()
|
||
+ new
|
||
+ set complete=.,t,w,b,u,i
|
||
+ se tagfunc=s:Tagfunc
|
||
+ sil norm i␎
|
||
+ bwipe!
|
||
endfunc
|
||
|
||
+
|
||
" vim: shiftwidth=2 sts=2 expandtab
|