diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/cursor.c | 3 | ||||
-rw-r--r-- | src/nvim/edit.c | 21 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 3 | ||||
-rw-r--r-- | src/nvim/memline.c | 1 | ||||
-rw-r--r-- | src/nvim/ops.c | 5 | ||||
-rw-r--r-- | src/nvim/spellfile.c | 3 | ||||
-rw-r--r-- | src/nvim/tag.c | 1 | ||||
-rw-r--r-- | src/nvim/testdir/test_cmdline.vim | 15 | ||||
-rw-r--r-- | src/nvim/testdir/test_excmd.vim | 36 | ||||
-rw-r--r-- | src/nvim/testdir/test_ins_complete_no_halt.vim | 51 | ||||
-rw-r--r-- | src/nvim/testdir/test_number.vim | 11 | ||||
-rw-r--r-- | src/nvim/testdir/test_textformat.vim | 7 | ||||
-rw-r--r-- | src/nvim/testdir/test_writefile.vim | 8 | ||||
-rw-r--r-- | src/nvim/window.c | 77 |
14 files changed, 196 insertions, 46 deletions
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index d4a68adeda..21f23b7fd4 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -130,7 +130,8 @@ static int coladvance2( if (finetune && curwin->w_p_wrap && curwin->w_width_inner != 0 - && wcol >= (colnr_T)width) { + && wcol >= (colnr_T)width + && width > 0) { csize = linetabsize(line); if (csize > 0) csize--; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 3c4bf504cf..aeab1cdad4 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -4127,6 +4127,7 @@ static int ins_compl_get_exp(pos_T *ini) char_u *dict = NULL; int dict_f = 0; bool set_match_pos; + pos_T prev_pos = { 0, 0, 0 }; int l_ctrl_x_mode = ctrl_x_mode; assert(curbuf != NULL); @@ -4365,6 +4366,7 @@ static int ins_compl_get_exp(pos_T *ini) } else if (*e_cpt == '.') { p_ws = true; } + bool looped_around = false; for (;; ) { bool cont_s_ipos = false; @@ -4393,7 +4395,26 @@ static int ins_compl_get_exp(pos_T *ini) } else if (first_match_pos.lnum == last_match_pos.lnum && first_match_pos.col == last_match_pos.col) { found_new_match = FAIL; + } else if ((compl_direction == FORWARD) + && (prev_pos.lnum > pos->lnum + || (prev_pos.lnum == pos->lnum + && prev_pos.col >= pos->col))) { + if (looped_around) { + found_new_match = FAIL; + } else { + looped_around = true; + } + } else if ((compl_direction != FORWARD) + && (prev_pos.lnum < pos->lnum + || (prev_pos.lnum == pos->lnum + && prev_pos.col <= pos->col))) { + if (looped_around) { + found_new_match = FAIL; + } else { + looped_around = true; + } } + prev_pos = *pos; if (found_new_match == FAIL) { if (ins_buf == curbuf) { found_all = true; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index bcbd2266d3..821a2c8e8e 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -120,7 +120,8 @@ char_u *get_function_name(expand_T *xp, int idx) if (intidx < 0) { name = get_user_func_name(xp, idx); if (name != NULL) { - if (*name != '<' && STRNCMP("g:", xp->xp_pattern, 2) == 0) { + if (*name != NUL && *name != '<' + && STRNCMP("g:", xp->xp_pattern, 2) == 0) { return cat_prefix_varname('g', name); } return name; diff --git a/src/nvim/memline.c b/src/nvim/memline.c index f1774a20cf..4ccbb31e2c 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -264,6 +264,7 @@ int ml_open(buf_T *buf) buf->b_ml.ml_line_lnum = 0; // no cached line buf->b_ml.ml_line_offset = 0; buf->b_ml.ml_chunksize = NULL; + buf->b_ml.ml_usedchunks = 0; if (cmdmod.noswapfile) { buf->b_p_swf = false; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 10a099eb47..e344bb5e77 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1725,7 +1725,9 @@ int op_delete(oparg_T *oap) (int)oap->line_count-1, n, deleted_bytes, 0, 0, 0, kExtmarkUndo); } - auto_format(false, true); + if (oap->op_type == OP_DELETE) { + auto_format(false, true); + } } msgmore(curbuf->b_ml.ml_line_count - old_lcount); @@ -2486,6 +2488,7 @@ int op_change(oparg_T *oap) xfree(ins_text); } } + auto_format(false, true); return retval; } diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 8b95178c84..843ecec1b1 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -1371,6 +1371,9 @@ static int read_compound(FILE *fd, slang_T *slang, int len) gap = &slang->sl_comppat; c = get2c(fd); // <comppatcount> + if (c < 0) { + return SP_TRUNCERROR; + } todo -= 2; ga_init(gap, sizeof(char_u *), c); ga_grow(gap, c); diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 61d48eb4bf..c63cdad098 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1467,6 +1467,7 @@ find_tags( help_save = curbuf->b_help; orgpat.pat = pat; + orgpat.regmatch.regprog = NULL; vimconv.vc_type = CONV_NONE; /* diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 8e2a145343..5a6824b5c1 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -599,13 +599,26 @@ endfunc func Test_cmdline_complete_user_func() call feedkeys(":func Test_cmdline_complete_user\<Tab>\<Home>\"\<cr>", 'tx') - call assert_match('"func Test_cmdline_complete_user', @:) + call assert_match('"func Test_cmdline_complete_user_', @:) call feedkeys(":func s:ScriptL\<Tab>\<Home>\"\<cr>", 'tx') call assert_match('"func <SNR>\d\+_ScriptLocalFunction', @:) " g: prefix also works call feedkeys(":echo g:Test_cmdline_complete_user_f\<Tab>\<Home>\"\<cr>", 'tx') call assert_match('"echo g:Test_cmdline_complete_user_func', @:) + + " using g: prefix does not result in just "g:" matches from a lambda + let Fx = { a -> a } + call feedkeys(":echo g:\<Tab>\<Home>\"\<cr>", 'tx') + call assert_match('"echo g:[A-Z]', @:) + + " existence of script-local dict function does not break user function name + " completion + function s:a_dict_func() dict + endfunction + call feedkeys(":call Test_cmdline_complete_user\<Tab>\<Home>\"\<cr>", 'tx') + call assert_match('"call Test_cmdline_complete_user_', @:) + delfunction s:a_dict_func endfunc func Test_cmdline_complete_user_names() diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index ed2bb2c06b..2d01cbba83 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -313,6 +313,42 @@ func Test_confirm_write_ro() call delete('Xconfirm_write_ro') endfunc +func Test_confirm_write_partial_file() + CheckNotGui + CheckRunVimInTerminal + + call writefile(['a', 'b', 'c', 'd'], 'Xwrite_partial') + call writefile(['set nobackup ff=unix cmdheight=2', + \ 'edit Xwrite_partial'], 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {'rows': 20}) + + call term_sendkeys(buf, ":confirm 2,3w\n") + call WaitForAssert({-> assert_match('^Write partial file? *$', + \ term_getline(buf, 19))}, 1000) + call WaitForAssert({-> assert_match('^(Y)es, \[N\]o: *$', + \ term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, 'N') + call WaitForAssert({-> assert_match('.* All$', term_getline(buf, 20))}, 1000) + call assert_equal(['a', 'b', 'c', 'd'], readfile('Xwrite_partial')) + call delete('Xwrite_partial') + + call term_sendkeys(buf, ":confirm 2,3w\n") + call WaitForAssert({-> assert_match('^Write partial file? *$', + \ term_getline(buf, 19))}, 1000) + call WaitForAssert({-> assert_match('^(Y)es, \[N\]o: *$', + \ term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, 'Y') + call WaitForAssert({-> assert_match('^"Xwrite_partial" \[New\] 2L, 4B written *$', + \ term_getline(buf, 19))}, 1000) + call WaitForAssert({-> assert_match('^Press ENTER or type command to continue *$', + \ term_getline(buf, 20))}, 1000) + call assert_equal(['b', 'c'], readfile('Xwrite_partial')) + + call StopVimInTerminal(buf) + call delete('Xwrite_partial') + call delete('Xscript') +endfunc + " Test for the :winsize command func Test_winsize_cmd() call assert_fails('winsize 1', 'E465:') diff --git a/src/nvim/testdir/test_ins_complete_no_halt.vim b/src/nvim/testdir/test_ins_complete_no_halt.vim new file mode 100644 index 0000000000..e12925daa9 --- /dev/null +++ b/src/nvim/testdir/test_ins_complete_no_halt.vim @@ -0,0 +1,51 @@ +" Test insert mode completion does not get stuck when looping around. +" In a separate file to avoid the settings to leak to other test cases. + +set complete+=kspell +set completeopt+=menu +set completeopt+=menuone +set completeopt+=noselect +set completeopt+=noinsert +let g:autocompletion = v:true + +func Test_ins_complete_no_halt() + function! OpenCompletion() + if pumvisible() && (g:autocompletion == v:true) + call feedkeys("\<C-e>\<C-n>", "i") + return + endif + if ((v:char >= 'a' && v:char <= 'z') || (v:char >= 'A' && v:char <= 'Z')) && (g:autocompletion == v:true) + call feedkeys("\<C-n>", "i") + redraw + endif + endfunction + + autocmd InsertCharPre * noautocmd call OpenCompletion() + + setlocal spell! spelllang=en_us + + call feedkeys("iauto-complete-halt-test test test test test test test test test test test test test test test test test test test\<C-c>", "tx!") + call assert_equal(["auto-complete-halt-test test test test test test test test test test test test test test test test test test test"], getline(1, "$")) +endfunc + +func Test_auto_complete_backwards_no_halt() + function! OpenCompletion() + if pumvisible() && (g:autocompletion == v:true) + call feedkeys("\<C-e>\<C-p>", "i") + return + endif + if ((v:char >= 'a' && v:char <= 'z') || (v:char >= 'A' && v:char <= 'Z')) && (g:autocompletion == v:true) + call feedkeys("\<C-p>", "i") + redraw + endif + endfunction + + autocmd InsertCharPre * noautocmd call OpenCompletion() + + setlocal spell! spelllang=en_us + + call feedkeys("iauto-complete-halt-test test test test test test test test test test test test test test test test test test test\<C-c>", "tx!") + call assert_equal(["auto-complete-halt-test test test test test test test test test test test test test test test test test test test"], getline(1, "$")) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_number.vim b/src/nvim/testdir/test_number.vim index 92a1bf3c9a..d737ebe9f0 100644 --- a/src/nvim/testdir/test_number.vim +++ b/src/nvim/testdir/test_number.vim @@ -320,4 +320,15 @@ func Test_number_rightleft() bw! endfunc +" This used to cause a divide by zero +func Test_number_no_text_virtual_edit() + vnew + call setline(1, ['line one', 'line two']) + set number virtualedit=all + normal w + 4wincmd | + normal j + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index 29f0433954..bf0706a0c2 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -975,6 +975,13 @@ func Test_fo_a_w() exe "normal f4xx" call assert_equal(['1 2 5 6 7 ', '8 9'], getline(1, 2)) + " using "cw" leaves cursor in right spot + call setline(1, ['Now we g whether that nation, or', + \ 'any nation so conceived and,']) + set fo=tcqa tw=35 + exe "normal 2G0cwx\<Esc>" + call assert_equal(['Now we g whether that nation, or x', 'nation so conceived and,'], getline(1, 2)) + set tw=0 set fo& %bw! diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim index 2504fcb14e..aa7882d129 100644 --- a/src/nvim/testdir/test_writefile.vim +++ b/src/nvim/testdir/test_writefile.vim @@ -191,6 +191,14 @@ func Test_saveas() close! enew | only call delete('Xfile') + + " :saveas should detect and set the file type. + syntax on + saveas! Xsaveas.pl + call assert_equal('perl', &filetype) + syntax off + %bw! + call delete('Xsaveas.pl') endfunc func Test_write_errors() diff --git a/src/nvim/window.c b/src/nvim/window.c index 75ecf90b14..4bbdaefd1f 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -64,6 +64,14 @@ # define ROWS_AVAIL (Rows - p_ch - tabline_height()) +/// flags for win_enter_ext() +typedef enum { + WEE_UNDO_SYNC = 0x01, + WEE_CURWIN_INVALID = 0x02, + WEE_TRIGGER_NEW_AUTOCMDS = 0x04, + WEE_TRIGGER_ENTER_AUTOCMDS = 0x08, + WEE_TRIGGER_LEAVE_AUTOCMDS = 0x10, +} wee_flags_T; static char *m_onlyone = N_("Already only one window"); @@ -1397,10 +1405,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // Keep same changelist position in new window. wp->w_changelistidx = oldwin->w_changelistidx; - /* - * make the new window the current window - */ - win_enter_ext(wp, false, false, true, true, true); + // make the new window the current window + win_enter_ext(wp, WEE_TRIGGER_NEW_AUTOCMDS | WEE_TRIGGER_ENTER_AUTOCMDS + | WEE_TRIGGER_LEAVE_AUTOCMDS); if (flags & WSP_VERT) { p_wiw = i; } else { @@ -1712,21 +1719,10 @@ static void win_exchange(long Prenum) curwin->w_vsep_width = wp->w_vsep_width; wp->w_vsep_width = temp; - /* If the windows are not in the same frame, exchange the sizes to avoid - * messing up the window layout. Otherwise fix the frame sizes. */ - if (curwin->w_frame->fr_parent != wp->w_frame->fr_parent) { - temp = curwin->w_height; - curwin->w_height = wp->w_height; - wp->w_height = temp; - temp = curwin->w_width; - curwin->w_width = wp->w_width; - wp->w_width = temp; - } else { - frame_fix_height(curwin); - frame_fix_height(wp); - frame_fix_width(curwin); - frame_fix_width(wp); - } + frame_fix_height(curwin); + frame_fix_height(wp); + frame_fix_width(curwin); + frame_fix_width(wp); (void)win_comp_pos(); // recompute window positions @@ -2620,7 +2616,8 @@ int win_close(win_T *win, bool free_buf) } if (close_curwin) { - win_enter_ext(wp, false, true, false, true, true); + win_enter_ext(wp, WEE_CURWIN_INVALID | WEE_TRIGGER_ENTER_AUTOCMDS + | WEE_TRIGGER_LEAVE_AUTOCMDS); if (other_buffer) { // careful: after this wp and win may be invalid! apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf); @@ -4005,11 +4002,12 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a tabpage_check_windows(old_curtab); } - /* We would like doing the TabEnter event first, but we don't have a - * valid current window yet, which may break some commands. - * This triggers autocommands, thus may make "tp" invalid. */ - win_enter_ext(tp->tp_curwin, false, true, false, - trigger_enter_autocmds, trigger_leave_autocmds); + // We would like doing the TabEnter event first, but we don't have a + // valid current window yet, which may break some commands. + // This triggers autocommands, thus may make "tp" invalid. + win_enter_ext(tp->tp_curwin, WEE_CURWIN_INVALID + | (trigger_enter_autocmds ? WEE_TRIGGER_ENTER_AUTOCMDS : 0) + | (trigger_leave_autocmds ? WEE_TRIGGER_LEAVE_AUTOCMDS : 0)); prevwin = next_prevwin; last_status(false); // status line may appear or disappear @@ -4462,26 +4460,25 @@ static void win_goto_hor(bool left, long count) /// win_valid(wp). void win_enter(win_T *wp, bool undo_sync) { - win_enter_ext(wp, undo_sync, false, false, true, true); + win_enter_ext(wp, (undo_sync ? WEE_UNDO_SYNC : 0) + | WEE_TRIGGER_ENTER_AUTOCMDS | WEE_TRIGGER_LEAVE_AUTOCMDS); } -/// Make window wp the current window. +/// Make window "wp" the current window. /// -/// @param curwin_invalid curwin has just been closed and -/// isn't valid when true. -static void win_enter_ext(win_T *wp, bool undo_sync, bool curwin_invalid, bool trigger_new_autocmds, - bool trigger_enter_autocmds, bool trigger_leave_autocmds) +/// @param flags if contains WEE_CURWIN_INVALID, it means curwin has just been +/// closed and isn't valid. +static void win_enter_ext(win_T *const wp, const int flags) { bool other_buffer = false; + const bool curwin_invalid = (flags & WEE_CURWIN_INVALID); if (wp == curwin && !curwin_invalid) { // nothing to do return; } - if (!curwin_invalid && trigger_leave_autocmds) { - /* - * Be careful: If autocommands delete the window, return now. - */ + if (!curwin_invalid && (flags & WEE_TRIGGER_LEAVE_AUTOCMDS)) { + // Be careful: If autocommands delete the window, return now. if (wp->w_buffer != curbuf) { apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf); other_buffer = true; @@ -4500,7 +4497,7 @@ static void win_enter_ext(win_T *wp, bool undo_sync, bool curwin_invalid, bool t } // sync undo before leaving the current buffer - if (undo_sync && curbuf != wp->w_buffer) { + if ((flags & WEE_UNDO_SYNC) && curbuf != wp->w_buffer) { u_sync(false); } @@ -4561,10 +4558,10 @@ static void win_enter_ext(win_T *wp, bool undo_sync, bool curwin_invalid, bool t shorten_fnames(true); } - if (trigger_new_autocmds) { + if (flags & WEE_TRIGGER_NEW_AUTOCMDS) { apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); } - if (trigger_enter_autocmds) { + if (flags & WEE_TRIGGER_ENTER_AUTOCMDS) { apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); if (other_buffer) { apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf); @@ -4926,10 +4923,6 @@ static void frame_remove(frame_T *frp) frp->fr_prev->fr_next = frp->fr_next; } else { frp->fr_parent->fr_child = frp->fr_next; - // special case: topframe->fr_child == frp - if (topframe->fr_child == frp) { - topframe->fr_child = frp->fr_next; - } } if (frp->fr_next != NULL) { frp->fr_next->fr_prev = frp->fr_prev; |