vim-patch:8.1.1270: cannot see current match position

Problem:    Cannot see current match position.
Solution:   Show "3/44" when using the "n" command and "S" is not in
            'shortmess'. (Christian Brabandt, closes vim/vim#4317)
9dfa313919
This commit is contained in:
erw7
2019-05-27 12:26:32 +09:00
parent 485972dd64
commit 777c2a25ce
7 changed files with 323 additions and 45 deletions

View File

@@ -5254,7 +5254,7 @@ A jump table for the options with a short description can be found at |Q_op|.
function to get the effective shiftwidth value. function to get the effective shiftwidth value.
*'shortmess'* *'shm'* *'shortmess'* *'shm'*
'shortmess' 'shm' string (Vim default "filnxtToOF", Vi default: "") 'shortmess' 'shm' string (Vim default "filnxtToOFS", Vi default: "S")
global global
This option helps to avoid all the |hit-enter| prompts caused by file This option helps to avoid all the |hit-enter| prompts caused by file
messages, for example with CTRL-G, and to avoid some other messages. messages, for example with CTRL-G, and to avoid some other messages.
@@ -5294,6 +5294,8 @@ A jump table for the options with a short description can be found at |Q_op|.
q use "recording" instead of "recording @a" q use "recording" instead of "recording @a"
F don't give the file info when editing a file, like `:silent` F don't give the file info when editing a file, like `:silent`
was used for the command was used for the command
S do not show search count message when searching, e.g.
"[1/5]"
This gives you the opportunity to avoid that a change between buffers This gives you the opportunity to avoid that a change between buffers
requires you to hit <Enter>, but still gives as useful a message as requires you to hit <Enter>, but still gives as useful a message as

View File

@@ -141,6 +141,17 @@ use <Esc> to abandon the search.
All matches for the last used search pattern will be highlighted if you set All matches for the last used search pattern will be highlighted if you set
the 'hlsearch' option. This can be suspended with the |:nohlsearch| command. the 'hlsearch' option. This can be suspended with the |:nohlsearch| command.
When 'shortmess' does not include the "S" flag, Vim will automatically show an
index, on which the cursor is. This can look like this: >
[1/5] Cursor is on first of 5 matches.
[1/>99] Cursor is on first of more than 99 matches.
[>99/>99] Cursor is after 99 match of more than 99 matches.
[?/??] Unknown how many matches exists, generating the
statistics was aborted because of search timeout.
Note: the count does not take offset into account.
When no match is found you get the error: *E486* Pattern not found When no match is found you get the error: *E486* Pattern not found
Note that for the |:global| command this behaves like a normal message, for Vi Note that for the |:global| command this behaves like a normal message, for Vi
compatibility. For the |:s| command the "e" flag can be used to avoid the compatibility. For the |:s| command the "e" flag can be used to avoid the

View File

@@ -315,7 +315,7 @@ static char_u SHM_ALL[] = {
SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI,
SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER, SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER,
SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU, SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU,
SHM_RECORDING, SHM_FILEINFO, SHM_RECORDING, SHM_FILEINFO, SHM_SEARCHCOUNT,
0, 0,
}; };

View File

@@ -173,6 +173,7 @@ enum {
SHM_COMPLETIONMENU = 'c', ///< Completion menu messages. SHM_COMPLETIONMENU = 'c', ///< Completion menu messages.
SHM_RECORDING = 'q', ///< Short recording message. SHM_RECORDING = 'q', ///< Short recording message.
SHM_FILEINFO = 'F', ///< No file info messages. SHM_FILEINFO = 'F', ///< No file info messages.
SHM_SEARCHCOUNT = 'S', ///< Search sats: '[1/10]'
}; };
/// Represented by 'a' flag. /// Represented by 'a' flag.
#define SHM_ALL_ABBREVIATIONS ((char_u[]) { \ #define SHM_ALL_ABBREVIATIONS ((char_u[]) { \

View File

@@ -2134,7 +2134,7 @@ return {
type='string', list='flags', scope={'global'}, type='string', list='flags', scope={'global'},
vim=true, vim=true,
varname='p_shm', varname='p_shm',
defaults={if_true={vi="", vim="filnxtToOF"}} defaults={if_true={vi="S", vim="filnxtToOFS"}}
}, },
{ {
full_name='showbreak', abbreviation='sbr', full_name='showbreak', abbreviation='sbr',

View File

@@ -14,6 +14,7 @@
#include "nvim/ascii.h" #include "nvim/ascii.h"
#include "nvim/vim.h" #include "nvim/vim.h"
#include "nvim/search.h" #include "nvim/search.h"
#include "nvim/buffer.h"
#include "nvim/charset.h" #include "nvim/charset.h"
#include "nvim/cursor.h" #include "nvim/cursor.h"
#include "nvim/edit.h" #include "nvim/edit.h"
@@ -997,6 +998,8 @@ int do_search(
char_u *dircp; char_u *dircp;
char_u *strcopy = NULL; char_u *strcopy = NULL;
char_u *ps; char_u *ps;
char_u *msgbuf = NULL;
size_t len;
/* /*
* A line offset is not remembered, this is vi compatible. * A line offset is not remembered, this is vi compatible.
@@ -1123,61 +1126,95 @@ int do_search(
if ((options & SEARCH_ECHO) && messaging() if ((options & SEARCH_ECHO) && messaging()
&& !cmd_silent && msg_silent == 0) { && !cmd_silent && msg_silent == 0) {
char_u *msgbuf;
char_u *trunc; char_u *trunc;
if (*searchstr == NUL) // Compute msg_row early.
msg_start();
if (*searchstr == NUL) {
p = spats[last_idx].pat; p = spats[last_idx].pat;
else } else {
p = searchstr; p = searchstr;
msgbuf = xmalloc(STRLEN(p) + 40); }
if (!shortmess(SHM_SEARCHCOUNT)) {
// Reserve enough space for the search pattern + offset +
// search stat.
if (msg_scrolled != 0) {
// Use all the columns.
len = (int)(Rows - msg_row) * Columns - 1;
} else {
// Use up to 'showcmd' column.
len = (int)(Rows - msg_row - 1) * Columns + sc_col - 1;
}
if (len < STRLEN(p) + 40 + 11) {
len = STRLEN(p) + 40 + 11;
}
} else {
// Reserve enough space for the search pattern + offset.
len = STRLEN(p) + 40;
}
msgbuf = xmalloc((int)len);
{ {
memset(msgbuf, ' ', len);
msgbuf[0] = dirc; msgbuf[0] = dirc;
msgbuf[len - 1] = NUL;
if (utf_iscomposing(utf_ptr2char(p))) { if (utf_iscomposing(utf_ptr2char(p))) {
// Use a space to draw the composing char on. // Use a space to draw the composing char on.
msgbuf[1] = ' '; msgbuf[1] = ' ';
STRCPY(msgbuf + 2, p); STRNCPY(msgbuf + 2, p, STRLEN(p));
} else } else {
STRCPY(msgbuf + 1, p); STRNCPY(msgbuf + 1, p, STRLEN(p));
}
if (spats[0].off.line || spats[0].off.end || spats[0].off.off) { if (spats[0].off.line || spats[0].off.end || spats[0].off.off) {
p = msgbuf + STRLEN(msgbuf); p = msgbuf + STRLEN(p) + 1;
*p++ = dirc; *p++ = dirc;
if (spats[0].off.end) if (spats[0].off.end) {
*p++ = 'e'; *p++ = 'e';
else if (!spats[0].off.line) } else if (!spats[0].off.line) {
*p++ = 's'; *p++ = 's';
if (spats[0].off.off > 0 || spats[0].off.line) }
if (spats[0].off.off > 0 || spats[0].off.line) {
*p++ = '+'; *p++ = '+';
if (spats[0].off.off != 0 || spats[0].off.line) }
sprintf((char *)p, "%" PRId64, (int64_t)spats[0].off.off); if (spats[0].off.off != 0 || spats[0].off.line) {
else int l = 0;
*p = NUL; l = sprintf((char *)p, "%ld", spats[0].off.off);
p[l] = ' '; // remove NUL from sprintf
}
} }
msg_start();
trunc = msg_strtrunc(msgbuf, FALSE); trunc = msg_strtrunc(msgbuf, FALSE);
if (trunc != NULL) {
xfree(msgbuf);
msgbuf = trunc;
}
/* The search pattern could be shown on the right in rightleft // The search pattern could be shown on the right in rightleft
* mode, but the 'ruler' and 'showcmd' area use it too, thus // mode, but the 'ruler' and 'showcmd' area use it too, thus
* it would be blanked out again very soon. Show it on the // it would be blanked out again very soon. Show it on the
* left, but do reverse the text. */ // left, but do reverse the text.
if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { if (curwin->w_p_rl && *curwin->w_p_rlc == 's') {
char_u *r = reverse_text(trunc != NULL ? trunc : msgbuf); char_u *r = reverse_text(trunc != NULL ? trunc : msgbuf);
xfree(trunc); xfree(msgbuf);
trunc = r; msgbuf = r;
// move reversed text to beginning of buffer
while (*r != NUL && *r == ' ') {
r++;
}
memmove(msgbuf, r, msgbuf + STRLEN(msgbuf) - r);
// overwrite old text
memset(r, ' ', msgbuf + STRLEN(msgbuf) - r);
} }
if (trunc != NULL) { msg_outtrans(msgbuf);
msg_outtrans(trunc);
xfree(trunc);
} else
msg_outtrans(msgbuf);
msg_clr_eos(); msg_clr_eos();
msg_check(); msg_check();
xfree(msgbuf);
gotocmdline(FALSE); gotocmdline(false);
ui_flush(); ui_flush();
msg_nowait = TRUE; /* don't wait for this message */ msg_nowait = true; // don't wait for this message
} }
} }
@@ -1217,8 +1254,16 @@ int do_search(
+ ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF)))), + ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF)))),
RE_LAST, (linenr_T)0, tm, timed_out); RE_LAST, (linenr_T)0, tm, timed_out);
if (dircp != NULL) if (dircp != NULL) {
*dircp = dirc; /* restore second '/' or '?' for normal_cmd() */ *dircp = dirc; // restore second '/' or '?' for normal_cmd()
}
if (!shortmess(SHM_SEARCH)
&& ((dirc == '/' && lt(pos, curwin->w_cursor))
|| (dirc == '?' && lt(curwin->w_cursor, pos)))) {
os_delay(500L, false); // leave some time for top_bot_msg
}
if (c == FAIL) { if (c == FAIL) {
retval = 0; retval = 0;
goto end_do_search; goto end_do_search;
@@ -1260,16 +1305,25 @@ int do_search(
} }
} }
/* // Show [1/15] if 'S' is not in 'shortmess'.
* The search command can be followed by a ';' to do another search. if ((options & SEARCH_ECHO)
* For example: "/pat/;/foo/+3;?bar" && messaging()
* This is like doing another search command, except: && !(cmd_silent + msg_silent)
* - The remembered direction '/' or '?' is from the first search. && c != FAIL
* - When an error happens the cursor isn't moved at all. && !shortmess(SHM_SEARCHCOUNT)
* Don't do this when called by get_address() (it handles ';' itself). && msgbuf != NULL) {
*/ search_stat(dirc, &pos, msgbuf);
if (!(options & SEARCH_OPT) || pat == NULL || *pat != ';') }
// The search command can be followed by a ';' to do another search.
// For example: "/pat/;/foo/+3;?bar"
// This is like doing another search command, except:
// - The remembered direction '/' or '?' is from the first search.
// - When an error happens the cursor isn't moved at all.
// Don't do this when called by get_address() (it handles ';' itself).
if (!(options & SEARCH_OPT) || pat == NULL || *pat != ';') {
break; break;
}
dirc = *++pat; dirc = *++pat;
if (dirc != '?' && dirc != '/') { if (dirc != '?' && dirc != '/') {
@@ -1288,7 +1342,7 @@ int do_search(
end_do_search: end_do_search:
if ((options & SEARCH_KEEP) || cmdmod.keeppatterns) if ((options & SEARCH_KEEP) || cmdmod.keeppatterns)
spats[0].off = old_off; spats[0].off = old_off;
xfree(strcopy); xfree(msgbuf);
return retval; return retval;
} }
@@ -4141,6 +4195,108 @@ int linewhite(linenr_T lnum)
return *p == NUL; return *p == NUL;
} }
// Add the search count "[3/19]" to "msgbuf".
static void search_stat(int dirc, pos_T *pos, char_u *msgbuf)
{
int save_ws = p_ws;
int wraparound = false;
pos_T p = (*pos);
static pos_T lastpos = { 0, 0, 0 };
static int cur = 0;
static int cnt = 0;
static int chgtick = 0;
static char_u *lastpat = NULL;
static buf_T *lbuf = NULL;
proftime_T start;
#define OUT_OF_TIME 999
wraparound = ((dirc == '?' && lt(lastpos, p))
|| (dirc == '/' && lt(p, lastpos)));
// If anything relevant changed the count has to be recomputed.
// STRNICMP ignores case, but we should not ignore case.
// Unfortunately, there is no STRNICMP function.
if (!(chgtick == buf_get_changedtick(curbuf)
&& STRNICMP(lastpat, spats[last_idx].pat, STRLEN(lastpat)) == 0
&& STRLEN(lastpat) == STRLEN(spats[last_idx].pat)
&& equalpos(lastpos, curwin->w_cursor)
&& lbuf == curbuf) || wraparound || cur < 0 || cur > 99) {
cur = 0;
cnt = 0;
clearpos(&lastpos);
lbuf = curbuf;
}
if (equalpos(lastpos, curwin->w_cursor) && !wraparound
&& (dirc == '/' ? cur < cnt : cur > 0)) {
cur += dirc == '/' ? 1 : -1;
} else {
p_ws = false;
start = profile_setlimit(20L);
while (!got_int && searchit(curwin, curbuf, &lastpos, NULL,
FORWARD, NULL, 1, SEARCH_PEEK + SEARCH_KEEP,
RE_LAST, (linenr_T)0, NULL, NULL) != FAIL) {
// Stop after passing the time limit.
if (profile_passed_limit(start)) {
cnt = OUT_OF_TIME;
cur = OUT_OF_TIME;
break;
}
cnt++;
if (ltoreq(lastpos, p)) {
cur++;
}
fast_breakcheck();
if (cnt > 99) {
break;
}
}
if (got_int) {
cur = -1; // abort
}
}
if (cur > 0) {
#define STAT_BUF_LEN 10
char t[STAT_BUF_LEN] = "";
if (curwin->w_p_rl && *curwin->w_p_rlc == 's') {
if (cur == OUT_OF_TIME) {
vim_snprintf(t, STAT_BUF_LEN, "[?/??]");
} else if (cnt > 99 && cur > 99) {
vim_snprintf(t, STAT_BUF_LEN, "[>99/>99]");
} else if (cnt > 99) {
vim_snprintf(t, STAT_BUF_LEN, "[>99/%d]", cur);
} else {
vim_snprintf(t, STAT_BUF_LEN, "[%d/%d]", cnt, cur);
}
} else {
if (cur == OUT_OF_TIME) {
vim_snprintf(t, STAT_BUF_LEN, "[?/??]");
} else if (cnt > 99 && cur > 99) {
vim_snprintf(t, STAT_BUF_LEN, "[>99/>99]");
} else if (cnt > 99) {
vim_snprintf(t, STAT_BUF_LEN, "[%d/>99]", cur);
} else {
vim_snprintf(t, STAT_BUF_LEN, "[%d/%d]", cur, cnt);
}
}
STRNCPY(msgbuf + STRLEN(msgbuf) - STRLEN(t), t, STRLEN(t));
if (dirc == '?' && cur == 100) {
cur = -1;
}
xfree(lastpat);
lastpat = vim_strsave(spats[last_idx].pat);
chgtick = buf_get_changedtick(curbuf);
lbuf = curbuf;
lastpos = p;
// keep the message even after redraw
give_warning(msgbuf, false);
}
p_ws = save_ws;
}
/* /*
* Find identifiers or defines in included files. * Find identifiers or defines in included files.
* If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase. * If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase.

View File

@@ -0,0 +1,108 @@
" Tests for search_stats, when "S" is not in 'shortmess'
"
" This test is fragile, it might not work interactively, but it works when run
" as test!
func! Test_search_stat()
new
set shortmess-=S
call append(0, repeat(['foobar', 'foo', 'fooooobar', 'foba', 'foobar'], 10))
" 1) match at second line
call cursor(1, 1)
let @/ = 'fo*\(bar\?\)\?'
let g:a = execute(':unsilent :norm! n')
let stat = '\[2/50\]'
let pat = escape(@/, '()*?'). '\s\+'
call assert_match(pat .. stat, g:a)
" 2) Match at last line
call cursor(line('$')-2, 1)
let g:a = execute(':unsilent :norm! n')
let stat = '\[50/50\]'
call assert_match(pat .. stat, g:a)
" 3) No search stat
set shortmess+=S
call cursor(1, 1)
let stat = '\[2/50\]'
let g:a = execute(':unsilent :norm! n')
call assert_notmatch(pat .. stat, g:a)
set shortmess-=S
" 4) Many matches
call cursor(line('$')-2, 1)
let @/ = '.'
let pat = escape(@/, '()*?'). '\s\+'
let g:a = execute(':unsilent :norm! n')
let stat = '\[>99/>99\]'
call assert_match(pat .. stat, g:a)
" 5) Many matches
call cursor(1, 1)
let g:a = execute(':unsilent :norm! n')
let stat = '\[2/>99\]'
call assert_match(pat .. stat, g:a)
" 6) right-left
if exists("+rightleft")
set rl
call cursor(1,1)
let @/ = 'foobar'
let pat = 'raboof/\s\+'
let g:a = execute(':unsilent :norm! n')
let stat = '\[20/2\]'
call assert_match(pat .. stat, g:a)
set norl
endif
" 7) right-left bottom
if exists("+rightleft")
set rl
call cursor('$',1)
let pat = 'raboof?\s\+'
let g:a = execute(':unsilent :norm! N')
let stat = '\[20/20\]'
call assert_match(pat .. stat, g:a)
set norl
endif
" 8) right-left back at top
if exists("+rightleft")
set rl
call cursor('$',1)
let pat = 'raboof/\s\+'
let g:a = execute(':unsilent :norm! n')
let stat = '\[20/1\]'
call assert_match(pat .. stat, g:a)
call assert_match('search hit BOTTOM, continuing at TOP', g:a)
set norl
endif
" 9) normal, back at top
call cursor(1,1)
let @/ = 'foobar'
let pat = '?foobar\s\+'
let g:a = execute(':unsilent :norm! N')
let stat = '\[20/20\]'
call assert_match(pat .. stat, g:a)
call assert_match('search hit TOP, continuing at BOTTOM', g:a)
" 10) normal, no match
call cursor(1,1)
let @/ = 'zzzzzz'
let g:a = ''
try
let g:a = execute(':unsilent :norm! n')
catch /^Vim\%((\a\+)\)\=:E486/
let stat = ''
" error message is not redir'ed to g:a, it is empty
call assert_true(empty(g:a))
catch
call assert_false(1)
endtry
" close the window
set shortmess+=S
bwipe!
endfunc