diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/edit.c | 59 | ||||
-rw-r--r-- | src/nvim/fileio.c | 25 | ||||
-rw-r--r-- | src/nvim/globals.h | 1 | ||||
-rw-r--r-- | src/nvim/testdir/test_autocmd.vim | 70 | ||||
-rw-r--r-- | src/nvim/testdir/test_popup.vim | 32 |
5 files changed, 158 insertions, 29 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 4d22d427e4..49bc2ab2f0 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -137,16 +137,18 @@ typedef struct compl_S compl_T; struct compl_S { compl_T *cp_next; compl_T *cp_prev; - char_u *cp_str; /* matched text */ - char cp_icase; /* TRUE or FALSE: ignore case */ - char_u *(cp_text[CPT_COUNT]); /* text for the menu */ - char_u *cp_fname; /* file containing the match, allocated when - * cp_flags has FREE_FNAME */ - int cp_flags; /* ORIGINAL_TEXT, CONT_S_IPOS or FREE_FNAME */ - int cp_number; /* sequence number */ + char_u *cp_str; // matched text + char cp_icase; // TRUE or FALSE: ignore case + char cp_equal; // TRUE or FALSE: ins_compl_equal always ok + char_u *(cp_text[CPT_COUNT]); // text for the menu + char_u *cp_fname; // file containing the match, allocated when + // cp_flags has FREE_FNAME + int cp_flags; // ORIGINAL_TEXT, CONT_S_IPOS or FREE_FNAME + int cp_number; // sequence number }; -#define ORIGINAL_TEXT (1) /* the original text when the expansion begun */ +// flags for ins_compl_add() +#define ORIGINAL_TEXT (1) // the original text when the expansion begun #define FREE_FNAME (2) /* @@ -2035,14 +2037,14 @@ static bool ins_compl_accept_char(int c) return vim_iswordc(c); } -/* - * This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the - * case of the originally typed text is used, and the case of the completed - * text is inferred, ie this tries to work out what case you probably wanted - * the rest of the word to be in -- webb - */ -int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int dir, int flags) +// This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the +// case of the originally typed text is used, and the case of the completed +// text is inferred, ie this tries to work out what case you probably wanted +// the rest of the word to be in -- webb +int ins_compl_add_infercase(char_u *str_arg, int len, int icase, char_u *fname, + int dir, int flags) { + char_u *str = str_arg; int i, c; int actual_len; /* Take multi-byte characters */ int actual_compl_length; /* into account. */ @@ -2171,10 +2173,10 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int xfree(wca); - return ins_compl_add(IObuff, len, icase, fname, NULL, false, dir, flags, - false); + str = IObuff; } - return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags, false); + return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags, + false, false); } /// Add a match to the list of matches @@ -2191,6 +2193,7 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int /// cptext itself will not be freed. /// @param[in] cdir Completion direction. /// @param[in] adup True if duplicate matches are to be accepted. +/// @param[in] equal Match is always accepted by ins_compl_equal. /// /// @return NOTDONE if the given string is already in the list of completions, /// otherwise it is added to the list and OK is returned. FAIL will be @@ -2199,7 +2202,8 @@ static int ins_compl_add(char_u *const str, int len, const bool icase, char_u *const fname, char_u *const *const cptext, const bool cptext_allocated, - const Direction cdir, int flags, const bool adup) + const Direction cdir, int flags, const bool adup, + int equal) FUNC_ATTR_NONNULL_ARG(1) { compl_T *match; @@ -2251,6 +2255,7 @@ static int ins_compl_add(char_u *const str, int len, match->cp_number = 0; match->cp_str = vim_strnsave(str, len); match->cp_icase = icase; + match->cp_equal = equal; /* match-fname is: * - compl_curr_match->cp_fname if it is a string equal to fname. @@ -2324,6 +2329,9 @@ static int ins_compl_add(char_u *const str, int len, static bool ins_compl_equal(compl_T *match, char_u *str, size_t len) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { + if (match->cp_equal) { + return true; + } if (match->cp_icase) { return STRNICMP(match->cp_str, str, len) == 0; } @@ -2397,7 +2405,8 @@ static void ins_compl_add_matches(int num_matches, char_u **matches, int icase) for (i = 0; i < num_matches && add_r != FAIL; i++) if ((add_r = ins_compl_add(matches[i], -1, icase, - NULL, NULL, false, dir, 0, false)) == OK) { + NULL, NULL, false, dir, 0, false, + false)) == OK) { // If dir was BACKWARD then honor it just once. dir = FORWARD; } @@ -2466,7 +2475,7 @@ void set_completion(colnr_T startcol, list_T *list) compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col, compl_length); if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, - ORIGINAL_TEXT, false) != OK) { + ORIGINAL_TEXT, false, false) != OK) { return; } @@ -3759,6 +3768,7 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) bool icase = false; bool adup = false; bool aempty = false; + bool aequal = false; char *(cptext[CPT_COUNT]); if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { @@ -3773,6 +3783,9 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) icase = (bool)tv_dict_get_number(tv->vval.v_dict, "icase"); adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); aempty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); + if (tv_dict_get_string(tv->vval.v_dict, "equal", false) != NULL) { + aequal = tv_dict_get_number(tv->vval.v_dict, "equal"); + } } else { word = (const char *)tv_get_string_chk(tv); memset(cptext, 0, sizeof(cptext)); @@ -3784,7 +3797,7 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) return FAIL; } return ins_compl_add((char_u *)word, -1, icase, NULL, - (char_u **)cptext, true, dir, 0, adup); + (char_u **)cptext, true, dir, 0, adup, aequal); } /* @@ -4946,7 +4959,7 @@ static int ins_complete(int c, bool enable_pum) xfree(compl_orig_text); compl_orig_text = vim_strnsave(line + compl_col, compl_length); if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, - ORIGINAL_TEXT, false) != OK) { + ORIGINAL_TEXT, false, false) != OK) { xfree(compl_pattern); compl_pattern = NULL; xfree(compl_orig_text); diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 53b945d983..b2840c9402 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -6061,17 +6061,30 @@ void do_autocmd(char_u *arg_in, int forceit) for (size_t i = 0; i < 2; i++) { if (*cmd != NUL) { // Check for "++once" flag. - if (!once && STRNCMP(cmd, "++once", 6) == 0 && ascii_iswhite(cmd[6])) { + if (STRNCMP(cmd, "++once", 6) == 0 && ascii_iswhite(cmd[6])) { + if (once) { + EMSG2(_(e_duparg2), "++once"); + } once = true; cmd = skipwhite(cmd + 6); } + // Check for "++nested" flag. - if (!nested - && ((STRNCMP(cmd, "++nested", 8) == 0 && ascii_iswhite(cmd[8])) - // Deprecated form (without "++"). - || (STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])))) { + if ((STRNCMP(cmd, "++nested", 8) == 0 && ascii_iswhite(cmd[8]))) { + if (nested) { + EMSG2(_(e_duparg2), "++nested"); + } + nested = true; + cmd = skipwhite(cmd + 8); + } + + // Check for the old (deprecated) "nested" flag. + if (STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])) { + if (nested) { + EMSG2(_(e_duparg2), "nested"); + } nested = true; - cmd = skipwhite(cmd + ('+' == cmd[0] ? 8 : 6)); + cmd = skipwhite(cmd + 6); } } } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index a4d31c0c08..b529357dea 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -931,6 +931,7 @@ EXTERN char_u e_interr[] INIT(= N_("Interrupted")); EXTERN char_u e_invaddr[] INIT(= N_("E14: Invalid address")); EXTERN char_u e_invarg[] INIT(= N_("E474: Invalid argument")); EXTERN char_u e_invarg2[] INIT(= N_("E475: Invalid argument: %s")); +EXTERN char_u e_duparg2[] INIT(= N_("E983: Duplicate argument: %s")); EXTERN char_u e_invexpr2[] INIT(= N_("E15: Invalid expression: %s")); EXTERN char_u e_invrange[] INIT(= N_("E16: Invalid range")); EXTERN char_u e_invcmd[] INIT(= N_("E476: Invalid command")); diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 7a6c9478ca..6d4c54bbf9 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1308,3 +1308,73 @@ func Test_Changed_FirstTime() call delete('Xchanged.txt') bwipe! endfunc + +func Test_autocmd_nested() + let g:did_nested = 0 + augroup Testing + au WinNew * edit somefile + au BufNew * let g:did_nested = 1 + augroup END + split + call assert_equal(0, g:did_nested) + close + bwipe! somefile + + " old nested argument still works + augroup Testing + au! + au WinNew * nested edit somefile + au BufNew * let g:did_nested = 1 + augroup END + split + call assert_equal(1, g:did_nested) + close + bwipe! somefile + + " New ++nested argument works + augroup Testing + au! + au WinNew * ++nested edit somefile + au BufNew * let g:did_nested = 1 + augroup END + split + call assert_equal(1, g:did_nested) + close + bwipe! somefile + + augroup Testing + au! + augroup END + + call assert_fails('au WinNew * ++nested ++nested echo bad', 'E983:') + call assert_fails('au WinNew * nested nested echo bad', 'E983:') +endfunc + +func Test_autocmd_once() + " Without ++once WinNew triggers twice + let g:did_split = 0 + augroup Testing + au WinNew * let g:did_split += 1 + augroup END + split + split + call assert_equal(2, g:did_split) + call assert_true(exists('#WinNew')) + close + close + + " With ++once WinNew triggers once + let g:did_split = 0 + augroup Testing + au! + au WinNew * ++once let g:did_split += 1 + augroup END + split + split + call assert_equal(1, g:did_split) + call assert_false(exists('#WinNew')) + close + close + + call assert_fails('au WinNew * ++once ++once echo bad', 'E983:') +endfunc diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 41c27a68a6..96f4bfc71b 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -275,6 +275,38 @@ func Test_noinsert_complete() iunmap <F5> endfunc +func Test_complete_no_filter() + func! s:complTest1() abort + call complete(1, [{'word': 'foobar'}]) + return '' + endfunc + func! s:complTest2() abort + call complete(1, [{'word': 'foobar', 'equal': 1}]) + return '' + endfunc + + let completeopt = &completeopt + + " without equal=1 + new + set completeopt=menuone,noinsert,menu + inoremap <F5> <C-R>=s:complTest1()<CR> + call feedkeys("i\<F5>z\<CR>\<CR>\<ESC>.", 'tx') + call assert_equal('z', getline(1)) + bwipe! + + " with equal=1 + new + set completeopt=menuone,noinsert,menu + inoremap <F5> <C-R>=s:complTest2()<CR> + call feedkeys("i\<F5>z\<CR>\<CR>\<ESC>.", 'tx') + call assert_equal('foobar', getline(1)) + bwipe! + + let &completeopt = completeopt + iunmap <F5> +endfunc + func Test_compl_vim_cmds_after_register_expr() func! s:test_func() return 'autocmd ' |