mirror of
https://github.com/vim/vim.git
synced 2026-05-28 00:21:37 +02:00
patch 9.2.0374: c_CTRL-{G,T} does not handle offset
Problem: c_CTRL-{G,T} does not handle offset, when cycling between
matches
Solution: Refactor parsing logic into parse_search_pattern_offset() and
handle offsets, note: highlighting does not handle offsets
yet (Barrett Ruth).
fixes: #19991
closes: #19998
Signed-off-by: Barrett Ruth <br.barrettruth@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
49e8630a28
commit
c62342e5cf
@@ -1,4 +1,4 @@
|
||||
*cmdline.txt* For Vim version 9.2. Last change: 2026 Mar 17
|
||||
*cmdline.txt* For Vim version 9.2. Last change: 2026 Apr 20
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -466,14 +466,15 @@ CTRL-L A match is done on the pattern in front of the cursor. If
|
||||
*c_CTRL-G* */_CTRL-G*
|
||||
CTRL-G When 'incsearch' is set, entering a search pattern for "/" or
|
||||
"?" and the current match is displayed then CTRL-G will move
|
||||
to the next match (does not take |search-offset| into account)
|
||||
to the next match. The |search-offset| is applied when <CR>
|
||||
is pressed, but does not affect the match highlighting.
|
||||
Use CTRL-T to move to the previous match. Hint: on a regular
|
||||
keyboard G is below T.
|
||||
*c_CTRL-T* */_CTRL-T*
|
||||
CTRL-T When 'incsearch' is set, entering a search pattern for "/" or
|
||||
"?" and the current match is displayed then CTRL-T will move
|
||||
to the previous match (does not take |search-offset| into
|
||||
account).
|
||||
to the previous match. The |search-offset| is applied when
|
||||
<CR> is pressed, but does not affect the match highlighting.
|
||||
Use CTRL-G to move to the next match. Hint: on a regular
|
||||
keyboard T is above G.
|
||||
|
||||
|
||||
+48
-37
@@ -614,14 +614,18 @@ may_adjust_incsearch_highlighting(
|
||||
incsearch_state_T *is_state,
|
||||
int c)
|
||||
{
|
||||
int skiplen, patlen;
|
||||
pos_T t;
|
||||
char_u *pat;
|
||||
int search_flags = SEARCH_NOOF;
|
||||
int i;
|
||||
int save;
|
||||
int bslsh = FALSE;
|
||||
int search_delim;
|
||||
int skiplen, patlen;
|
||||
pos_T t;
|
||||
char_u *pat;
|
||||
char_u *dircp = NULL;
|
||||
char_u *searchstr;
|
||||
char_u *strcopy = NULL;
|
||||
size_t searchstrlen;
|
||||
size_t patlen_s;
|
||||
soffset_T offset;
|
||||
int search_flags = SEARCH_NOOF;
|
||||
int i;
|
||||
int search_delim;
|
||||
|
||||
// Parsing range may already set the last search pattern.
|
||||
// NOTE: must call restore_last_search_pattern() before returning!
|
||||
@@ -639,31 +643,16 @@ may_adjust_incsearch_highlighting(
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (search_delim == ccline.cmdbuff[skiplen])
|
||||
{
|
||||
pat = last_search_pattern();
|
||||
if (pat == NULL)
|
||||
{
|
||||
restore_last_search_pattern();
|
||||
return FAIL;
|
||||
}
|
||||
skiplen = 0;
|
||||
patlen = (int)last_search_pattern_len();
|
||||
}
|
||||
else
|
||||
pat = ccline.cmdbuff + skiplen;
|
||||
pat = ccline.cmdbuff + skiplen;
|
||||
searchstr = pat;
|
||||
searchstrlen = (size_t)patlen;
|
||||
patlen_s = (size_t)(ccline.cmdlen - skiplen);
|
||||
|
||||
// do not search for the search end delimiter,
|
||||
// unless it is part of the pattern
|
||||
if (patlen > 2 && firstc == pat[patlen - 1])
|
||||
{
|
||||
patlen--;
|
||||
if (pat[patlen - 1] == '\\')
|
||||
{
|
||||
pat[patlen - 1] = firstc;
|
||||
bslsh = TRUE;
|
||||
}
|
||||
}
|
||||
(void)parse_search_pattern_offset(&pat, &patlen_s, search_delim,
|
||||
SEARCH_OPT, &strcopy, &searchstr,
|
||||
&searchstrlen, &dircp, &offset);
|
||||
|
||||
cursor_off();
|
||||
out_flush();
|
||||
@@ -681,18 +670,39 @@ may_adjust_incsearch_highlighting(
|
||||
if (!p_hls)
|
||||
search_flags += SEARCH_KEEP;
|
||||
++emsg_off;
|
||||
save = pat[patlen];
|
||||
pat[patlen] = NUL;
|
||||
i = searchit(curwin, curbuf, &t, NULL,
|
||||
c == Ctrl_G ? FORWARD : BACKWARD,
|
||||
pat, patlen, count, search_flags, RE_SEARCH, NULL);
|
||||
searchstr, searchstrlen, count, search_flags, RE_SEARCH, NULL);
|
||||
--emsg_off;
|
||||
pat[patlen] = save;
|
||||
if (bslsh)
|
||||
pat[patlen - 1] = '\\';
|
||||
if (dircp != NULL)
|
||||
*dircp = search_delim;
|
||||
if (i)
|
||||
{
|
||||
is_state->search_start = is_state->match_start;
|
||||
pos_T match_start = is_state->match_start;
|
||||
pos_T match_end = is_state->match_end;
|
||||
long off = offset.off;
|
||||
|
||||
is_state->search_start = match_start;
|
||||
if (!offset.line && (offset.end || off != 0))
|
||||
{
|
||||
if (offset.end)
|
||||
{
|
||||
is_state->search_start = match_end;
|
||||
(void)decl(&is_state->search_start);
|
||||
}
|
||||
while (off > 0)
|
||||
{
|
||||
if (incl(&is_state->search_start) == -1)
|
||||
break;
|
||||
--off;
|
||||
}
|
||||
while (off < 0)
|
||||
{
|
||||
if (decl(&is_state->search_start) == -1)
|
||||
break;
|
||||
++off;
|
||||
}
|
||||
}
|
||||
is_state->match_end = t;
|
||||
is_state->match_start = t;
|
||||
if (c == Ctrl_T && firstc != '?')
|
||||
@@ -733,6 +743,7 @@ may_adjust_incsearch_highlighting(
|
||||
}
|
||||
else
|
||||
vim_beep(BO_ERROR);
|
||||
vim_free(strcopy);
|
||||
restore_last_search_pattern();
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ void set_last_search_pat(char_u *s, int idx, int magic, int setlast);
|
||||
void last_pat_prog(regmmatch_T *regmatch);
|
||||
int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, int dir, char_u *pat, size_t patlen, long count, int options, int pat_use, searchit_arg_T *extra_arg);
|
||||
void set_search_direction(int cdir);
|
||||
int parse_search_pattern_offset(char_u **pat, size_t *patlen, int search_delim, int options, char_u **strcopy, char_u **searchstr, size_t *searchstrlen, char_u **dircp, soffset_T *offset);
|
||||
int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, size_t patlen, long count, int options, searchit_arg_T *sia);
|
||||
int search_for_exact_line(buf_T *buf, pos_T *pos, int dir, char_u *pat);
|
||||
int searchc(cmdarg_T *cap, int t_cmd);
|
||||
|
||||
+106
-61
@@ -1215,6 +1215,109 @@ first_submatch(regmmatch_T *rp)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Parse a search pattern followed by an optional offset (e.g. "pat/e+1").
|
||||
* On entry "*pat" points at the start of the pattern and "*patlen" is its
|
||||
* length. Updates the in/out parameters:
|
||||
* *pat / *patlen - moved past the pattern and offset
|
||||
* *strcopy - allocated copy if "\?" or "\/" was unescaped
|
||||
* (caller must vim_free() it)
|
||||
* *searchstr and *searchstrlen - pointer/length of the search pattern only
|
||||
* *dircp - location of the trailing delimiter that was
|
||||
* replaced with NUL (or NULL); caller may restore
|
||||
* it
|
||||
* *offset - parsed offset (line/end/off)
|
||||
*
|
||||
* Returns the length of the parsed pattern + offset (used by get_address()
|
||||
* to know how much of the command line was consumed).
|
||||
*/
|
||||
int
|
||||
parse_search_pattern_offset(
|
||||
char_u **pat,
|
||||
size_t *patlen,
|
||||
int search_delim,
|
||||
int options,
|
||||
char_u **strcopy,
|
||||
char_u **searchstr,
|
||||
size_t *searchstrlen,
|
||||
char_u **dircp,
|
||||
soffset_T *offset)
|
||||
{
|
||||
int cmdlen = 0;
|
||||
char_u *p;
|
||||
char_u *ps;
|
||||
|
||||
if (*pat == NULL || **pat == NUL)
|
||||
return 0;
|
||||
|
||||
ps = *strcopy;
|
||||
*searchstr = *pat;
|
||||
*searchstrlen = *patlen;
|
||||
*dircp = NULL;
|
||||
|
||||
/*
|
||||
* Find end of regular expression.
|
||||
* If there is a matching '/' or '?', toss it.
|
||||
*/
|
||||
p = skip_regexp_ex(*pat, search_delim, magic_isset(),
|
||||
strcopy, NULL, NULL);
|
||||
if (*strcopy != ps)
|
||||
{
|
||||
size_t len = STRLEN(*strcopy);
|
||||
// made a copy of "pat" to change "\?" to "?"
|
||||
cmdlen += (int)(*patlen - len);
|
||||
*pat = *strcopy;
|
||||
*patlen = len;
|
||||
*searchstr = *strcopy;
|
||||
*searchstrlen = len;
|
||||
}
|
||||
if (*p == search_delim)
|
||||
{
|
||||
*searchstrlen = p - *pat;
|
||||
*dircp = p; // remember where we put the NUL
|
||||
*p++ = NUL;
|
||||
}
|
||||
|
||||
offset->line = FALSE;
|
||||
offset->end = FALSE;
|
||||
offset->off = 0;
|
||||
/*
|
||||
* Check for a line offset or a character offset.
|
||||
* For get_address (echo off) we don't check for a character
|
||||
* offset, because it is meaningless and the 's' could be a
|
||||
* substitute command.
|
||||
*/
|
||||
if (*p == '+' || *p == '-' || VIM_ISDIGIT(*p))
|
||||
offset->line = TRUE;
|
||||
else if ((options & SEARCH_OPT)
|
||||
&& (*p == 'e' || *p == 's' || *p == 'b'))
|
||||
{
|
||||
if (*p == 'e') // end
|
||||
offset->end = SEARCH_END;
|
||||
++p;
|
||||
}
|
||||
if (VIM_ISDIGIT(*p) || *p == '+' || *p == '-') // got an offset
|
||||
{
|
||||
// 'nr' or '+nr' or '-nr'
|
||||
if (VIM_ISDIGIT(*p) || VIM_ISDIGIT(*(p + 1)))
|
||||
offset->off = atol((char *)p);
|
||||
else if (*p == '-') // single '-'
|
||||
offset->off = -1;
|
||||
else // single '+'
|
||||
offset->off = 1;
|
||||
++p;
|
||||
while (VIM_ISDIGIT(*p)) // skip number
|
||||
++p;
|
||||
}
|
||||
|
||||
// compute length of search command for get_address()
|
||||
cmdlen += (int)(p - *pat);
|
||||
*patlen -= p - *pat;
|
||||
*pat = p; // put pat after search command
|
||||
|
||||
return cmdlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Highest level string search function.
|
||||
* Search for the 'count'th occurrence of pattern 'pat' in direction 'dirc'
|
||||
@@ -1257,7 +1360,6 @@ do_search(
|
||||
long c;
|
||||
char_u *dircp;
|
||||
char_u *strcopy = NULL;
|
||||
char_u *ps;
|
||||
int show_search_stats;
|
||||
char_u *msgbuf = NULL;
|
||||
size_t msgbuflen = 0;
|
||||
@@ -1369,66 +1471,9 @@ do_search(
|
||||
|
||||
if (pat != NULL && *pat != NUL) // look for (new) offset
|
||||
{
|
||||
/*
|
||||
* Find end of regular expression.
|
||||
* If there is a matching '/' or '?', toss it.
|
||||
*/
|
||||
ps = strcopy;
|
||||
p = skip_regexp_ex(pat, search_delim, magic_isset(),
|
||||
&strcopy, NULL, NULL);
|
||||
if (strcopy != ps)
|
||||
{
|
||||
size_t len = STRLEN(strcopy);
|
||||
// made a copy of "pat" to change "\?" to "?"
|
||||
searchcmdlen += (int)(patlen - len);
|
||||
pat = strcopy;
|
||||
patlen = len;
|
||||
searchstr = strcopy;
|
||||
searchstrlen = len;
|
||||
}
|
||||
if (*p == search_delim)
|
||||
{
|
||||
searchstrlen = p - pat;
|
||||
dircp = p; // remember where we put the NUL
|
||||
*p++ = NUL;
|
||||
}
|
||||
spats[0].off.line = FALSE;
|
||||
spats[0].off.end = FALSE;
|
||||
spats[0].off.off = 0;
|
||||
/*
|
||||
* Check for a line offset or a character offset.
|
||||
* For get_address (echo off) we don't check for a character
|
||||
* offset, because it is meaningless and the 's' could be a
|
||||
* substitute command.
|
||||
*/
|
||||
if (*p == '+' || *p == '-' || VIM_ISDIGIT(*p))
|
||||
spats[0].off.line = TRUE;
|
||||
else if ((options & SEARCH_OPT)
|
||||
&& (*p == 'e' || *p == 's' || *p == 'b'))
|
||||
{
|
||||
if (*p == 'e') // end
|
||||
spats[0].off.end = SEARCH_END;
|
||||
++p;
|
||||
}
|
||||
if (VIM_ISDIGIT(*p) || *p == '+' || *p == '-') // got an offset
|
||||
{
|
||||
// 'nr' or '+nr' or '-nr'
|
||||
if (VIM_ISDIGIT(*p) || VIM_ISDIGIT(*(p + 1)))
|
||||
spats[0].off.off = atol((char *)p);
|
||||
else if (*p == '-') // single '-'
|
||||
spats[0].off.off = -1;
|
||||
else // single '+'
|
||||
spats[0].off.off = 1;
|
||||
++p;
|
||||
while (VIM_ISDIGIT(*p)) // skip number
|
||||
++p;
|
||||
}
|
||||
|
||||
// compute length of search command for get_address()
|
||||
searchcmdlen += (int)(p - pat);
|
||||
|
||||
patlen -= p - pat;
|
||||
pat = p; // put pat after search command
|
||||
searchcmdlen += parse_search_pattern_offset(&pat, &patlen,
|
||||
search_delim, options, &strcopy, &searchstr,
|
||||
&searchstrlen, &dircp, &spats[0].off);
|
||||
}
|
||||
|
||||
show_search_stats = FALSE;
|
||||
|
||||
@@ -700,6 +700,27 @@ func Test_search_cmdline7()
|
||||
call feedkeys("//e\<c-g>\<cr>", 'tx')
|
||||
call assert_equal('1 bbvimb', getline('.'))
|
||||
call assert_equal(4, col('.'))
|
||||
call cursor(1, 1)
|
||||
call feedkeys("//+1\<c-g>\<cr>", 'tx')
|
||||
call assert_equal(' 2 bbvimb', getline('.'))
|
||||
call assert_equal([0, 2, 1, 0], getpos('.'))
|
||||
call setline(1, 'blah blah blah')
|
||||
call feedkeys("gg0/blah/e\<C-G>\<CR>", 'tx')
|
||||
call assert_equal([0, 1, 9, 0], getpos('.'))
|
||||
call feedkeys("gg0/blah/e\<C-G>\<C-G>\<CR>", 'tx')
|
||||
call assert_equal([0, 1, 14, 0], getpos('.'))
|
||||
call feedkeys("gg0/blah/e\<C-G>\<C-G>\<C-T>\<CR>", 'tx')
|
||||
call assert_equal([0, 1, 9, 0], getpos('.'))
|
||||
call cursor(1, col('$'))
|
||||
call feedkeys("?blah?e\<C-G>\<CR>", 'tx')
|
||||
call assert_equal([0, 1, 9, 0], getpos('.'))
|
||||
call feedkeys("gg0/blah/e+1\<C-G>\<CR>", 'tx')
|
||||
call assert_equal([0, 1, 10, 0], getpos('.'))
|
||||
call feedkeys("gg0/blah/e-2\<C-G>\<CR>", 'tx')
|
||||
call assert_equal([0, 1, 7, 0], getpos('.'))
|
||||
call setline(1, 'a/b a/b a/b')
|
||||
call feedkeys("gg0/a\\/b/e\<C-G>\<CR>", 'tx')
|
||||
call assert_equal([0, 1, 7, 0], getpos('.'))
|
||||
|
||||
set noincsearch
|
||||
call test_override("char_avail", 0)
|
||||
|
||||
@@ -734,6 +734,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
374,
|
||||
/**/
|
||||
373,
|
||||
/**/
|
||||
|
||||
Reference in New Issue
Block a user