aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/ex_docmd.c20
-rw-r--r--src/nvim/ex_getln.c28
-rw-r--r--src/nvim/memline.c1
-rw-r--r--src/nvim/quickfix.c11
-rw-r--r--src/nvim/regexp.c1
-rw-r--r--src/nvim/search.c44
-rw-r--r--src/nvim/testdir/test_quickfix.vim16
-rw-r--r--src/nvim/testdir/test_search.vim77
-rw-r--r--src/nvim/testdir/test_search_stat.vim30
9 files changed, 182 insertions, 46 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index dfebd13868..60b017be28 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -2228,17 +2228,19 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only)
continue;
case 't': if (checkforcmd(&p, "tab", 3)) {
- long tabnr = get_address(
- eap, &eap->cmd, ADDR_TABS, eap->skip, skip_only, false, 1);
+ if (!skip_only) {
+ long tabnr = get_address(
+ eap, &eap->cmd, ADDR_TABS, eap->skip, skip_only, false, 1);
- if (tabnr == MAXLNUM) {
- cmdmod.tab = tabpage_index(curtab) + 1;
- } else {
- if (tabnr < 0 || tabnr > LAST_TAB_NR) {
- *errormsg = (char_u *)_(e_invrange);
- return false;
+ if (tabnr == MAXLNUM) {
+ cmdmod.tab = tabpage_index(curtab) + 1;
+ } else {
+ if (tabnr < 0 || tabnr > LAST_TAB_NR) {
+ *errormsg = (char_u *)_(e_invrange);
+ return false;
+ }
+ cmdmod.tab = tabnr + 1;
}
- cmdmod.tab = tabnr + 1;
}
eap->cmd = p;
continue;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index c966c780a0..8f2d536e63 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -276,6 +276,7 @@ static void init_incsearch_state(incsearch_state_T *s)
// Sets search_first_line and search_last_line to the address range.
static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
int *skiplen, int *patlen)
+ FUNC_ATTR_NONNULL_ALL
{
char_u *cmd;
cmdmod_T save_cmdmod = cmdmod;
@@ -443,6 +444,10 @@ static void may_do_incsearch_highlighting(int firstc, long count,
if (search_first_line == 0) {
// start at the original cursor position
curwin->w_cursor = s->search_start;
+ } else if (search_first_line > curbuf->b_ml.ml_line_count) {
+ // start after the last line
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ curwin->w_cursor.col = MAXCOL;
} else {
// start at the first line in the range
curwin->w_cursor.lnum = search_first_line;
@@ -563,6 +568,7 @@ static void may_do_incsearch_highlighting(int firstc, long count,
// May set "*c" to the added character.
// Return OK when calling command_line_not_changed.
static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s)
+ FUNC_ATTR_NONNULL_ALL
{
int skiplen, patlen;
@@ -579,8 +585,8 @@ static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s)
if (s->did_incsearch) {
curwin->w_cursor = s->match_end;
- if (!equalpos(curwin->w_cursor, s->search_start)) {
- *c = gchar_cursor();
+ *c = gchar_cursor();
+ if (*c != NUL) {
// If 'ignorecase' and 'smartcase' are set and the
// command line has no uppercase characters, convert
// the character to lowercase
@@ -588,16 +594,14 @@ static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s)
&& !pat_has_uppercase(ccline.cmdbuff + skiplen)) {
*c = mb_tolower(*c);
}
- if (*c != NUL) {
- if (*c == firstc
- || vim_strchr((char_u *)(p_magic ? "\\~^$.*[" : "\\^$"), *c)
- != NULL) {
- // put a backslash before special characters
- stuffcharReadbuff(*c);
- *c = '\\';
- }
- return FAIL;
+ if (*c == firstc
+ || vim_strchr((char_u *)(p_magic ? "\\~^$.*[" : "\\^$"), *c)
+ != NULL) {
+ // put a backslash before special characters
+ stuffcharReadbuff(*c);
+ *c = '\\';
}
+ return FAIL;
}
}
return OK;
@@ -1444,6 +1448,7 @@ static int command_line_execute(VimState *state, int key)
static int may_do_command_line_next_incsearch(int firstc, long count,
incsearch_state_T *s,
bool next_match)
+ FUNC_ATTR_NONNULL_ALL
{
int skiplen, patlen;
@@ -1537,6 +1542,7 @@ static int may_do_command_line_next_incsearch(int firstc, long count,
save_viewstate(&s->old_viewstate);
update_screen(NOT_VALID);
redrawcmdline();
+ curwin->w_cursor = s->match_end;
} else {
vim_beep(BO_ERROR);
}
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index f9390bcb88..2a75f13cc2 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -1818,6 +1818,7 @@ ml_get_buf (
linenr_T lnum,
bool will_change // line will be changed
)
+ FUNC_ATTR_NONNULL_ALL
{
bhdr_T *hp;
DATA_BL *dp;
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index ddce1e922d..5754950745 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -901,6 +901,7 @@ static bool qf_list_has_valid_entries(qf_list_T *qfl)
/// Return a pointer to a list in the specified quickfix stack
static qf_list_T * qf_get_list(qf_info_T *qi, int idx)
+ FUNC_ATTR_NONNULL_ALL
{
return &qi->qf_lists[idx];
}
@@ -1230,6 +1231,7 @@ static char_u * qf_cmdtitle(char_u *cmd)
/// Return a pointer to the current list in the specified quickfix stack
static qf_list_T * qf_get_curlist(qf_info_T *qi)
+ FUNC_ATTR_NONNULL_ALL
{
return qf_get_list(qi, qi->qf_curlist);
}
@@ -4825,12 +4827,13 @@ static bool vgr_qflist_valid(win_T *wp, qf_info_T *qi, unsigned qfid,
/// Search for a pattern in all the lines in a buffer and add the matching lines
/// to a quickfix list.
static bool vgr_match_buflines(qf_info_T *qi, char_u *fname, buf_T *buf,
- regmmatch_T *regmatch, long tomatch,
+ regmmatch_T *regmatch, long *tomatch,
int duplicate_name, int flags)
+ FUNC_ATTR_NONNULL_ARG(1, 3, 4, 5)
{
bool found_match = false;
- for (long lnum = 1; lnum <= buf->b_ml.ml_line_count && tomatch > 0; lnum++) {
+ for (long lnum = 1; lnum <= buf->b_ml.ml_line_count && *tomatch > 0; lnum++) {
colnr_T col = 0;
while (vim_regexec_multi(regmatch, curwin, buf, lnum, col, NULL,
NULL) > 0) {
@@ -4856,7 +4859,7 @@ static bool vgr_match_buflines(qf_info_T *qi, char_u *fname, buf_T *buf,
break;
}
found_match = true;
- if (--tomatch == 0) {
+ if (--*tomatch == 0) {
break;
}
if ((flags & VGR_GLOBAL) == 0 || regmatch->endpos[0].lnum > 0) {
@@ -5030,7 +5033,7 @@ void ex_vimgrep(exarg_T *eap)
} else {
// Try for a match in all lines of the buffer.
// For ":1vimgrep" look for first match only.
- found_match = vgr_match_buflines(qi, fname, buf, &regmatch, tomatch,
+ found_match = vgr_match_buflines(qi, fname, buf, &regmatch, &tomatch,
duplicate_name, flags);
if (using_dummy) {
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index bcf02af4ef..a570328499 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -7387,6 +7387,7 @@ long vim_regexec_multi(
proftime_T *tm, // timeout limit or NULL
int *timed_out // flag is set when timeout limit reached
)
+ FUNC_ATTR_NONNULL_ARG(1)
{
regexec_T rex_save;
bool rex_in_use_save = rex_in_use;
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 23d84038d6..517db05a40 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -96,12 +96,8 @@ static int lastc_bytelen = 1; // >1 for multi-byte char
// copy of spats[], for keeping the search patterns while executing autocmds
static struct spat saved_spats[2];
-// copy of spats[RE_SEARCH], for keeping the search patterns while incremental
-// searching
-static struct spat saved_last_search_spat;
-static int did_save_last_search_spat = 0;
-static int saved_last_idx = 0;
-static bool saved_no_hlsearch = false;
+static int saved_spats_last_idx = 0;
+static bool saved_spats_no_hlsearch = false;
static char_u *mr_pattern = NULL; // pattern used by search_regcomp()
static int mr_pattern_alloced = false; // mr_pattern was allocated
@@ -268,8 +264,8 @@ void save_search_patterns(void)
saved_spats[1] = spats[1];
if (spats[1].pat != NULL)
saved_spats[1].pat = vim_strsave(spats[1].pat);
- saved_last_idx = last_idx;
- saved_no_hlsearch = no_hlsearch;
+ saved_spats_last_idx = last_idx;
+ saved_spats_no_hlsearch = no_hlsearch;
}
}
@@ -281,8 +277,8 @@ void restore_search_patterns(void)
set_vv_searchforward();
free_spat(&spats[1]);
spats[1] = saved_spats[1];
- last_idx = saved_last_idx;
- set_no_hlsearch(saved_no_hlsearch);
+ last_idx = saved_spats_last_idx;
+ set_no_hlsearch(saved_spats_no_hlsearch);
}
}
@@ -309,6 +305,13 @@ void free_search_patterns(void)
#endif
+// copy of spats[RE_SEARCH], for keeping the search patterns while incremental
+// searching
+static struct spat saved_last_search_spat;
+static int did_save_last_search_spat = 0;
+static int saved_last_idx = 0;
+static bool saved_no_hlsearch = false;
+
/// Save and restore the search pattern for incremental highlight search
/// feature.
///
@@ -317,10 +320,9 @@ void free_search_patterns(void)
/// cancelling incremental searching even if it's called inside user functions.
void save_last_search_pattern(void)
{
- if (did_save_last_search_spat != 0) {
- IEMSG("did_save_last_search_spat is not zero");
- } else {
- did_save_last_search_spat++;
+ if (++did_save_last_search_spat != 1) {
+ // nested call, nothing to do
+ return;
}
saved_last_search_spat = spats[RE_SEARCH];
@@ -333,11 +335,15 @@ void save_last_search_pattern(void)
void restore_last_search_pattern(void)
{
- if (did_save_last_search_spat != 1) {
- IEMSG("did_save_last_search_spat is not one");
+ if (--did_save_last_search_spat > 0) {
+ // nested call, nothing to do
+ return;
+ }
+ if (did_save_last_search_spat != 0) {
+ iemsg("restore_last_search_pattern() called more often than"
+ " save_last_search_pattern()");
return;
}
- did_save_last_search_spat--;
xfree(spats[RE_SEARCH].pat);
spats[RE_SEARCH] = saved_last_search_spat;
@@ -488,7 +494,7 @@ void set_last_search_pat(const char_u *s, int idx, int magic, int setlast)
saved_spats[idx].pat = NULL;
else
saved_spats[idx].pat = vim_strsave(spats[idx].pat);
- saved_last_idx = last_idx;
+ saved_spats_last_idx = last_idx;
}
/* If 'hlsearch' set and search pat changed: need redraw. */
if (p_hls && idx == last_idx && !no_hlsearch)
@@ -1179,7 +1185,7 @@ int do_search(
}
if (*searchstr == NUL) {
- p = spats[last_idx].pat;
+ p = spats[0].pat;
} else {
p = searchstr;
}
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 926103b69f..eeec5bd2c3 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -2450,6 +2450,22 @@ func Test_vimgrep()
call XvimgrepTests('l')
endfunc
+" Test for incsearch highlighting of the :vimgrep pattern
+" This test used to cause "E315: ml_get: invalid lnum" errors.
+func Test_vimgrep_incsearch()
+ throw 'skipped: Nvim does not support test_override()'
+ enew
+ set incsearch
+ call test_override("char_avail", 1)
+
+ call feedkeys(":2vimgrep assert test_quickfix.vim test_cdo.vim\<CR>", "ntx")
+ let l = getqflist()
+ call assert_equal(2, len(l))
+
+ call test_override("ALL", 0)
+ set noincsearch
+endfunc
+
func XfreeTests(cchar)
call s:setup_commands(a:cchar)
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index 767cf99be3..e3a0fce5a6 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -244,6 +244,10 @@ func Test_search_cmdline2()
" go to previous match (on line 2)
call feedkeys("/the\<C-G>\<C-G>\<C-G>\<C-T>\<C-T>\<C-T>\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
+ 1
+ " go to previous match (on line 2)
+ call feedkeys("/the\<C-G>\<C-R>\<C-W>\<cr>", 'tx')
+ call assert_equal('theother', @/)
" Test 2: keep the view,
" after deleting a character from the search cmd
@@ -255,7 +259,7 @@ func Test_search_cmdline2()
call assert_equal({'lnum': 10, 'leftcol': 0, 'col': 4, 'topfill': 0, 'topline': 6, 'coladd': 0, 'skipcol': 0, 'curswant': 4}, winsaveview())
" remove all history entries
- for i in range(10)
+ for i in range(11)
call histdel('/')
endfor
@@ -489,14 +493,14 @@ func Test_search_cmdline5()
" Do not call test_override("char_avail", 1) so that <C-g> and <C-t> work
" regardless char_avail.
new
- call setline(1, [' 1 the first', ' 2 the second', ' 3 the third'])
+ call setline(1, [' 1 the first', ' 2 the second', ' 3 the third', ''])
set incsearch
1
call feedkeys("/the\<c-g>\<c-g>\<cr>", 'tx')
call assert_equal(' 3 the third', getline('.'))
$
call feedkeys("?the\<c-t>\<c-t>\<c-t>\<cr>", 'tx')
- call assert_equal(' 2 the second', getline('.'))
+ call assert_equal(' 1 the first', getline('.'))
" clean up
set noincsearch
bw!
@@ -861,6 +865,21 @@ func Test_incsearch_with_change()
call delete('Xis_change_script')
endfunc
+func Test_incsearch_cmdline_modifier()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ call test_override("char_avail", 1)
+ new
+ call setline(1, ['foo'])
+ set incsearch
+ " Test that error E14 does not occur in parsing command modifier.
+ call feedkeys("V:tab", 'tx')
+
+ call Incsearch_cleanup()
+endfunc
+
func Test_incsearch_scrolling()
if !CanRunVimInTerminal()
return
@@ -982,6 +1001,40 @@ func Test_search_sentence()
/
endfunc
+" Test that there is no crash when there is a last search pattern but no last
+" substitute pattern.
+func Test_no_last_substitute_pat()
+ " Use viminfo to set the last search pattern to a string and make the last
+ " substitute pattern the most recent used and make it empty (NULL).
+ call writefile(['~MSle0/bar', '~MSle0~&'], 'Xviminfo')
+ rviminfo! Xviminfo
+ call assert_fails('normal n', 'E35:')
+
+ call delete('Xviminfo')
+endfunc
+
+func Test_search_Ctrl_L_combining()
+ " Make sure, that Ctrl-L works correctly with combining characters.
+ " It uses an artificial example of an 'a' with 4 combining chars:
+ " 'a' U+0061 Dec:97 LATIN SMALL LETTER A &#x61; /\%u61\Z "\u0061"
+ " ' ̀' U+0300 Dec:768 COMBINING GRAVE ACCENT &#x300; /\%u300\Z "\u0300"
+ " ' ́' U+0301 Dec:769 COMBINING ACUTE ACCENT &#x301; /\%u301\Z "\u0301"
+ " ' ̇' U+0307 Dec:775 COMBINING DOT ABOVE &#x307; /\%u307\Z "\u0307"
+ " ' ̣' U+0323 Dec:803 COMBINING DOT BELOW &#x323; /\%u323 "\u0323"
+ " Those should also appear on the commandline
+ if !has('multi_byte') || !exists('+incsearch')
+ return
+ endif
+ call Cmdline3_prep()
+ 1
+ let bufcontent = ['', 'Miạ̀́̇m']
+ call append('$', bufcontent)
+ call feedkeys("/Mi\<c-l>\<c-l>\<cr>", 'tx')
+ call assert_equal(5, line('.'))
+ call assert_equal(bufcontent[1], @/)
+ call Incsearch_cleanup()
+endfunc
+
func Test_large_hex_chars1()
" This used to cause a crash, the character becomes an NFA state.
try
@@ -1019,6 +1072,24 @@ func Test_one_error_msg()
call assert_fails('call search(" \\((\\v[[=P=]]){185}+ ")', 'E871:')
endfunc
+func Test_incsearch_add_char_under_cursor()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ set incsearch
+ new
+ call setline(1, ['find match', 'anything'])
+ 1
+ call test_override('char_avail', 1)
+ call feedkeys("fc/m\<C-L>\<C-L>\<C-L>\<C-L>\<C-L>\<CR>", 'tx')
+ call assert_equal('match', @/)
+ call test_override('char_avail', 0)
+
+ set incsearch&
+ bwipe!
+endfunc
+
" Test for the search() function with match at the cursor position
func Test_search_match_at_curpos()
new
diff --git a/src/nvim/testdir/test_search_stat.vim b/src/nvim/testdir/test_search_stat.vim
index cf36f3214a..fa620b4e00 100644
--- a/src/nvim/testdir/test_search_stat.vim
+++ b/src/nvim/testdir/test_search_stat.vim
@@ -4,6 +4,8 @@
" as test!
source shared.vim
+source screendump.vim
+source check.vim
func! Test_search_stat()
new
@@ -164,3 +166,31 @@ func! Test_search_stat()
set shortmess+=S
bwipe!
endfunc
+
+func Test_searchcount_in_statusline()
+ CheckScreendump
+
+ let lines =<< trim END
+ set shortmess-=S
+ call append(0, 'this is something')
+ function TestSearchCount() abort
+ let search_count = searchcount()
+ if !empty(search_count)
+ return '[' . search_count.current . '/' . search_count.total . ']'
+ else
+ return ''
+ endif
+ endfunction
+ set hlsearch
+ set laststatus=2 statusline+=%{TestSearchCount()}
+ END
+ call writefile(lines, 'Xsearchstatusline')
+ let buf = RunVimInTerminal('-S Xsearchstatusline', #{rows: 10})
+ call TermWait(buf)
+ call term_sendkeys(buf, "/something")
+ call VerifyScreenDump(buf, 'Test_searchstat_4', {})
+
+ call term_sendkeys(buf, "\<Esc>")
+ call StopVimInTerminal(buf)
+ call delete('Xsearchstatusline')
+endfunc