aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/ex_getln.c114
-rw-r--r--src/nvim/globals.h8
-rw-r--r--src/nvim/screen.c6
-rw-r--r--src/nvim/testdir/test_search.vim57
-rw-r--r--test/functional/ui/inccommand_spec.lua2
5 files changed, 170 insertions, 17 deletions
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 8ef1540737..47bfc89bdf 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -271,9 +271,68 @@ static void init_incsearch_state(incsearch_state_T *s)
}
// Return true when 'incsearch' highlighting is to be done.
-static bool do_incsearch_highlighting(int firstc)
+// 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)
{
- return p_is && !cmd_silent && (firstc == '/' || firstc == '?');
+ *skiplen = 0;
+ *patlen = ccline.cmdlen;
+
+ if (p_is && !cmd_silent) {
+ // by default search all lines
+ search_first_line = 0;
+ search_last_line = MAXLNUM;
+
+ if (firstc == '/' || firstc == '?') {
+ return true;
+ }
+ if (firstc == ':') {
+ char_u *cmd = skip_range(ccline.cmdbuff, NULL);
+ char_u *p;
+ int delim;
+ char_u *end;
+
+ if (*cmd == 's' || *cmd == 'g' || *cmd == 'v') {
+ // Skip over "substitute" to find the pattern separator.
+ for (p = cmd; ASCII_ISALPHA(*p); p++) {}
+ if (*p != NUL) {
+ delim = *p++;
+ end = skip_regexp(p, delim, p_magic, NULL);
+ if (end > p) {
+ char_u *dummy;
+ exarg_T ea;
+ pos_T save_cursor = curwin->w_cursor;
+
+ // found a non-empty pattern
+ *skiplen = (int)(p - ccline.cmdbuff);
+ *patlen = (int)(end - p);
+
+ // parse the address range
+ memset(&ea, 0, sizeof(ea));
+ ea.line1 = 1;
+ ea.line2 = 1;
+ ea.cmd = ccline.cmdbuff;
+ ea.addr_type = ADDR_LINES;
+ parse_cmd_address(&ea, &dummy);
+ curwin->w_cursor = s->search_start;
+ if (ea.addr_count > 0) {
+ search_first_line = ea.line1;
+ search_last_line = ea.line2;
+ } else if (*cmd == 's') {
+ // :s defaults to the current line
+ search_first_line = curwin->w_cursor.lnum;
+ search_last_line = curwin->w_cursor.lnum;
+ }
+
+ curwin->w_cursor = save_cursor;
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
}
// May do 'incsearch' highlighting if desired.
@@ -283,8 +342,10 @@ static void may_do_incsearch_highlighting(int firstc, long count,
pos_T end_pos;
proftime_T tm;
searchit_arg_T sia;
+ int skiplen, patlen;
+ char_u c;
- if (!do_incsearch_highlighting(firstc)) {
+ if (!do_incsearch_highlighting(firstc, s, &skiplen, &patlen)) {
return;
}
@@ -294,12 +355,20 @@ static void may_do_incsearch_highlighting(int firstc, long count,
return;
}
s->incsearch_postponed = false;
- curwin->w_cursor = s->search_start; // start at old position
+
+ if (search_first_line == 0) {
+ // start at the original cursor position
+ curwin->w_cursor = s->search_start;
+ } else {
+ // start at the first line in the range
+ curwin->w_cursor.lnum = search_first_line;
+ curwin->w_cursor.col = 0;
+ }
save_last_search_pattern();
int i;
// If there is no command line, don't do anything
- if (ccline.cmdlen == 0) {
+ if (patlen == 0) {
i = 0;
set_no_hlsearch(true); // turn off previous highlight
redraw_all_later(SOME_VALID);
@@ -313,11 +382,21 @@ static void may_do_incsearch_highlighting(int firstc, long count,
if (!p_hls) {
search_flags += SEARCH_KEEP;
}
+ c = ccline.cmdbuff[skiplen + patlen];
+ ccline.cmdbuff[skiplen + patlen] = NUL;
memset(&sia, 0, sizeof(sia));
sia.sa_tm = &tm;
- i = do_search(NULL, firstc, ccline.cmdbuff, count,
+ i = do_search(NULL, firstc == ':' ? '/' : firstc,
+ ccline.cmdbuff + skiplen, count,
search_flags, &sia);
+ ccline.cmdbuff[skiplen + patlen] = c;
emsg_off--;
+ if (curwin->w_cursor.lnum < search_first_line
+ || curwin->w_cursor.lnum > search_last_line) {
+ // match outside of address range
+ i = 0;
+ }
+
// if interrupted while searching, behave like it failed
if (got_int) {
(void)vpeekc(); // remove <C-C> from input stream
@@ -357,9 +436,12 @@ static void may_do_incsearch_highlighting(int firstc, long count,
// Disable 'hlsearch' highlighting if the pattern matches
// everything. Avoids a flash when typing "foo\|".
+ c = ccline.cmdbuff[skiplen + patlen];
+ ccline.cmdbuff[skiplen + patlen] = NUL;
if (empty_pattern(ccline.cmdbuff)) {
set_no_hlsearch(true);
}
+ ccline.cmdbuff[skiplen + patlen] = c;
validate_cursor();
// May redraw the status line to show the cursor position.
@@ -385,8 +467,9 @@ static void may_do_incsearch_highlighting(int firstc, long count,
// Return OK when calling command_line_not_changed.
static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s)
{
+ int skiplen, patlen;
// Add a character from under the cursor for 'incsearch'
- if (!do_incsearch_highlighting(firstc)) {
+ if (!do_incsearch_highlighting(firstc, s, &skiplen, &patlen)) {
return FAIL;
}
@@ -398,7 +481,7 @@ static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s)
// command line has no uppercase characters, convert
// the character to lowercase
if (p_ic && p_scs
- && !pat_has_uppercase(ccline.cmdbuff)) {
+ && !pat_has_uppercase(ccline.cmdbuff + skiplen)) {
*c = mb_tolower(*c);
}
if (*c != NUL) {
@@ -1246,10 +1329,11 @@ static int may_do_command_line_next_incsearch(int firstc, long count,
incsearch_state_T *s,
bool next_match)
{
- if (!do_incsearch_highlighting(firstc)) {
+ int skiplen, patlen;
+ if (!do_incsearch_highlighting(firstc, s, &skiplen, &patlen)) {
return OK;
}
- if (ccline.cmdlen == 0) {
+ if (patlen == 0 && ccline.cmdbuff[skiplen] == NUL) {
return FAIL;
}
@@ -1259,12 +1343,13 @@ static int may_do_command_line_next_incsearch(int firstc, long count,
pos_T t;
char_u *pat;
int search_flags = SEARCH_NOOF;
+ char_u save;
- if (firstc == ccline.cmdbuff[0]) {
+ if (firstc == ccline.cmdbuff[skiplen]) {
pat = last_search_pattern();
} else {
- pat = ccline.cmdbuff;
+ pat = ccline.cmdbuff + skiplen;
}
save_last_search_pattern();
@@ -1284,17 +1369,20 @@ static int may_do_command_line_next_incsearch(int firstc, long count,
search_flags += SEARCH_KEEP;
}
emsg_off++;
+ save = pat[patlen];
+ pat[patlen] = NUL;
int found = searchit(curwin, curbuf, &t, NULL,
next_match ? FORWARD : BACKWARD,
pat, count, search_flags,
RE_SEARCH, NULL);
emsg_off--;
+ pat[patlen] = save;
ui_busy_stop();
if (found) {
s->search_start = s->match_start;
s->match_end = t;
s->match_start = t;
- if (!next_match && firstc == '/') {
+ if (!next_match && firstc != '?') {
// move just before the current match, so that
// when nv_search finishes the cursor will be
// put back on the match
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 91a0eab947..205be4b811 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -353,9 +353,11 @@ EXTERN int t_colors INIT(= 256); // int value of T_CCO
// position. Search_match_lines is the number of lines after the match (0 for
// a match within one line), search_match_endcol the column number of the
// character just after the match in the last line.
-EXTERN int highlight_match INIT(= false); // show search match pos
-EXTERN linenr_T search_match_lines; // lines of of matched string
-EXTERN colnr_T search_match_endcol; // col nr of match end
+EXTERN bool highlight_match INIT(= false); // show search match pos
+EXTERN linenr_T search_match_lines; // lines of of matched string
+EXTERN colnr_T search_match_endcol; // col nr of match end
+EXTERN linenr_T search_first_line INIT(= 0); // for :{FIRST},{last}s/pat
+EXTERN linenr_T search_last_line INIT(= MAXLNUM); // for :{first},{LAST}s/pat
EXTERN int no_smartcase INIT(= false); // don't use 'smartcase' once
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 69de1de6b2..4292ed2865 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -5918,6 +5918,12 @@ next_search_hl (
long nmatched = 0;
int save_called_emsg = called_emsg;
+ // for :{range}s/pat only highlight inside the range
+ if (lnum < search_first_line || lnum > search_last_line) {
+ shl->lnum = 0;
+ return;
+ }
+
if (shl->lnum != 0) {
// Check for three situations:
// 1. If the "lnum" is below a previous match, start a new search.
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index 8036dea29f..087a261cca 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -367,6 +367,63 @@ func Test_search_cmdline3()
bw!
endfunc
+func Cmdline3_prep()
+ throw 'skipped: Nvim does not support test_override()'
+ " need to disable char_avail,
+ " so that expansion of commandline works
+ call test_override("char_avail", 1)
+ new
+ call setline(1, [' 1', ' 2 the~e', ' 3 the theother'])
+ set incsearch
+endfunc
+
+func Cmdline3_cleanup()
+ throw 'skipped: Nvim does not support test_override()'
+ set noincsearch
+ call test_override("char_avail", 0)
+ bw!
+endfunc
+
+func Test_search_cmdline3s()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ call Cmdline3_prep()
+ 1
+ call feedkeys(":%s/the\<c-l>/xxx\<cr>", 'tx')
+ call assert_equal(' 2 xxxe', getline('.'))
+
+ call Cmdline3_cleanup()
+endfunc
+
+func Test_search_cmdline3g()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ call Cmdline3_prep()
+ 1
+ call feedkeys(":g/the\<c-l>/d\<cr>", 'tx')
+ call assert_equal(' 3 the theother', getline(2))
+
+ call Cmdline3_cleanup()
+endfunc
+
+func Test_search_cmdline3v()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ call Cmdline3_prep()
+ 1
+ call feedkeys(":v/the\<c-l>/d\<cr>", 'tx')
+ call assert_equal(1, line('$'))
+ call assert_equal(' 2 the~e', getline(1))
+
+ call Cmdline3_cleanup()
+endfunc
+
func Test_search_cmdline4()
" See test/functional/legacy/search_spec.lua
throw 'skipped: Nvim does not support test_override()'
diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua
index afb0c9cfa6..74e85212c8 100644
--- a/test/functional/ui/inccommand_spec.lua
+++ b/test/functional/ui/inccommand_spec.lua
@@ -162,7 +162,7 @@ describe(":substitute, 'inccommand' preserves", function()
insert(default_text)
feed_command("set inccommand=" .. case)
- local delims = { '/', '#', ';', '%', ',', '@', '!', ''}
+ local delims = { '/', '#', ';', '%', ',', '@', '!' }
for _,delim in pairs(delims) do
feed_command("%s"..delim.."lines"..delim.."LINES"..delim.."g")
expect([[