# HG changeset patch # User Bram Moolenaar # Date 1556997305 -7200 # Node ID 5e25171e0e75a3526be4e6538ef7e27616d54fb4 # Parent 582612b678181f4b960d196d07e3d56aaf602bc2 patch 8.1.1270: cannot see current match position commit https://github.com/vim/vim/commit/9dfa3139198b38b28673e251a3756430065914e9 Author: Bram Moolenaar Date: Sat May 4 21:08:40 2019 +0200 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 #4317) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1789,7 +1789,7 @@ A jump table for the options with a shor 'scrolloff' + 0 no scroll offset 'shelltemp' - {unchanged} {set vim default only on resetting 'cp'} 'shiftround' + off indent not rounded to shiftwidth - 'shortmess' & "" no shortening of messages + 'shortmess' & "S" no shortening of messages 'showcmd' & off command characters not shown 'showmode' & off current mode not shown 'sidescrolloff' + 0 cursor moves to edge of screen in scroll @@ -6563,8 +6563,8 @@ A jump table for the options with a shor function to get the effective shiftwidth value. *'shortmess'* *'shm'* -'shortmess' 'shm' string (Vim default "filnxtToO", Vi default: "", - POSIX default: "A") +'shortmess' 'shm' string (Vim default "filnxtToOS", Vi default: "S", + POSIX default: "AS") global 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. @@ -6604,6 +6604,8 @@ A jump table for the options with a shor q use "recording" instead of "recording @a" F don't give the file info when editing a file, like `:silent` 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 requires you to hit , but still gives as useful a message as diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt --- a/runtime/doc/pattern.txt +++ b/runtime/doc/pattern.txt @@ -152,6 +152,17 @@ use to abandon the search. 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. +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 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 @@ -301,6 +312,14 @@ triggered. In most cases this should be it's used again it fails. Usually this means there is something wrong with the pattern. + *E956* +In very rare cases a regular expression is used recursively. This can happen +when executing a pattern takes a long time and when checking for messages on +channels a callback is invoked that also uses a pattern or an autocommand is +triggered. In most cases this should be fine, but if a pattern is in use when +it's used again it fails. Usually this means there is something wrong with +the pattern. + ============================================================================== 2. The definition of a pattern *search-pattern* *pattern* *[pattern]* *regular-expression* *regexp* *Pattern* diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -2449,7 +2449,7 @@ static struct vimoption options[] = {(char_u *)8L, (char_u *)0L} SCTX_INIT}, {"shortmess", "shm", P_STRING|P_VIM|P_FLAGLIST, (char_u *)&p_shm, PV_NONE, - {(char_u *)"", (char_u *)"filnxtToO"} + {(char_u *)"S", (char_u *)"filnxtToOS"} SCTX_INIT}, {"shortname", "sn", P_BOOL|P_VI_DEF, (char_u *)&p_sn, PV_SN, @@ -3311,7 +3311,7 @@ set_init_1(int clean_arg) if (mch_getenv((char_u *)"VIM_POSIX") != NULL) { set_string_default("cpo", (char_u *)CPO_ALL); - set_string_default("shm", (char_u *)"A"); + set_string_default("shm", (char_u *)SHM_POSIX); } /* diff --git a/src/option.h b/src/option.h --- a/src/option.h +++ b/src/option.h @@ -183,27 +183,29 @@ #define COCU_ALL "nvic" /* flags for 'concealcursor' */ /* characters for p_shm option: */ -#define SHM_RO 'r' /* readonly */ -#define SHM_MOD 'm' /* modified */ -#define SHM_FILE 'f' /* (file 1 of 2) */ -#define SHM_LAST 'i' /* last line incomplete */ -#define SHM_TEXT 'x' /* tx instead of textmode */ -#define SHM_LINES 'l' /* "L" instead of "lines" */ -#define SHM_NEW 'n' /* "[New]" instead of "[New file]" */ -#define SHM_WRI 'w' /* "[w]" instead of "written" */ -#define SHM_A "rmfixlnw" /* represented by 'a' flag */ -#define SHM_WRITE 'W' /* don't use "written" at all */ -#define SHM_TRUNC 't' /* truncate file messages */ -#define SHM_TRUNCALL 'T' /* truncate all messages */ -#define SHM_OVER 'o' /* overwrite file messages */ -#define SHM_OVERALL 'O' /* overwrite more messages */ -#define SHM_SEARCH 's' /* no search hit bottom messages */ -#define SHM_ATTENTION 'A' /* no ATTENTION messages */ -#define SHM_INTRO 'I' /* intro messages */ -#define SHM_COMPLETIONMENU 'c' /* completion menu messages */ -#define SHM_RECORDING 'q' /* short recording message */ -#define SHM_FILEINFO 'F' /* no file info messages */ -#define SHM_ALL "rmfixlnwaWtToOsAIcqF" /* all possible flags for 'shm' */ +#define SHM_RO 'r' // readonly +#define SHM_MOD 'm' // modified +#define SHM_FILE 'f' // (file 1 of 2) +#define SHM_LAST 'i' // last line incomplete +#define SHM_TEXT 'x' // tx instead of textmode +#define SHM_LINES 'l' // "L" instead of "lines" +#define SHM_NEW 'n' // "[New]" instead of "[New file]" +#define SHM_WRI 'w' // "[w]" instead of "written" +#define SHM_A "rmfixlnw" // represented by 'a' flag +#define SHM_WRITE 'W' // don't use "written" at all +#define SHM_TRUNC 't' // truncate file messages +#define SHM_TRUNCALL 'T' // truncate all messages +#define SHM_OVER 'o' // overwrite file messages +#define SHM_OVERALL 'O' // overwrite more messages +#define SHM_SEARCH 's' // no search hit bottom messages +#define SHM_ATTENTION 'A' // no ATTENTION messages +#define SHM_INTRO 'I' // intro messages +#define SHM_COMPLETIONMENU 'c' // completion menu messages +#define SHM_RECORDING 'q' // short recording message +#define SHM_FILEINFO 'F' // no file info messages +#define SHM_SEARCHCOUNT 'S' // search stats: '[1/10]' +#define SHM_POSIX "AS" // POSIX value +#define SHM_ALL "rmfixlnwaWtToOsAIcqFS" // all possible flags for 'shm' /* characters for p_go: */ #define GO_TERMINAL '!' /* use terminal for system commands */ diff --git a/src/search.c b/src/search.c --- a/src/search.c +++ b/src/search.c @@ -26,6 +26,7 @@ static void show_pat_in_path(char_u *, i #ifdef FEAT_VIMINFO static void wvsp_one(FILE *fp, int idx, char *s, int sc); #endif +static void search_stat(int dirc, pos_T *pos, char_u *msgbuf); /* * This file contains various searching-related routines. These fall into @@ -1216,6 +1217,8 @@ do_search( char_u *dircp; char_u *strcopy = NULL; char_u *ps; + char_u *msgbuf = NULL; + size_t len; /* * A line offset is not remembered, this is vi compatible. @@ -1374,28 +1377,51 @@ do_search( if ((options & SEARCH_ECHO) && messaging() && !cmd_silent && msg_silent == 0) { - char_u *msgbuf; char_u *trunc; + // Compute msg_row early. + msg_start(); + if (*searchstr == NUL) p = spats[0].pat; else p = searchstr; - msgbuf = alloc((unsigned)(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 = alloc((int)len); if (msgbuf != NULL) { + vim_memset(msgbuf, ' ', len); msgbuf[0] = dirc; + msgbuf[len - 1] = NUL; + if (enc_utf8 && 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] = ' '; - STRCPY(msgbuf + 2, p); + STRNCPY(msgbuf + 2, p, STRLEN(p)); } else - STRCPY(msgbuf + 1, p); + STRNCPY(msgbuf + 1, p, STRLEN(p)); if (spats[0].off.line || spats[0].off.end || spats[0].off.off) { - p = msgbuf + STRLEN(msgbuf); + p = msgbuf + STRLEN(p) + 1; *p++ = dirc; if (spats[0].off.end) *p++ = 'e'; @@ -1404,45 +1430,50 @@ do_search( if (spats[0].off.off > 0 || spats[0].off.line) *p++ = '+'; if (spats[0].off.off != 0 || spats[0].off.line) - sprintf((char *)p, "%ld", spats[0].off.off); - else - *p = NUL; + { + int l = 0; + l = sprintf((char *)p, "%ld", spats[0].off.off); + p[l] = ' '; // remove NUL from sprintf + } } - msg_start(); trunc = msg_strtrunc(msgbuf, FALSE); + if (trunc != NULL) + { + vim_free(msgbuf); + msgbuf = trunc; + } #ifdef FEAT_RIGHTLEFT - /* The search pattern could be shown on the right in rightleft - * mode, but the 'ruler' and 'showcmd' area use it too, thus - * it would be blanked out again very soon. Show it on the - * left, but do reverse the text. */ + // The search pattern could be shown on the right in rightleft + // mode, but the 'ruler' and 'showcmd' area use it too, thus + // it would be blanked out again very soon. Show it on the + // left, but do reverse the text. if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { char_u *r; - r = reverse_text(trunc != NULL ? trunc : msgbuf); + r = reverse_text(msgbuf); if (r != NULL) { - vim_free(trunc); - trunc = r; + vim_free(msgbuf); + msgbuf = r; + // move reversed text to beginning of buffer + while (*r != NUL && *r == ' ') + r++; + mch_memmove(msgbuf, r, msgbuf + STRLEN(msgbuf) - r); + // overwrite old text + vim_memset(r, ' ', msgbuf + STRLEN(msgbuf) - r); } } #endif - if (trunc != NULL) - { - msg_outtrans(trunc); - vim_free(trunc); - } - else - msg_outtrans(msgbuf); + msg_outtrans(msgbuf); msg_clr_eos(); msg_check(); - vim_free(msgbuf); gotocmdline(FALSE); out_flush(); - msg_nowait = TRUE; /* don't wait for this message */ + msg_nowait = TRUE; // don't wait for this message } } @@ -1488,7 +1519,13 @@ do_search( RE_LAST, (linenr_T)0, tm, timed_out); 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(pos, curwin->w_cursor)) + || (dirc == '?' && LT_POS(curwin->w_cursor, pos)))) + ui_delay(500L, FALSE); // leave some time for top_bot_msg + if (c == FAIL) { retval = 0; @@ -1537,6 +1574,15 @@ do_search( } } + // Show [1/15] if 'S' is not in 'shortmess'. + if ((options & SEARCH_ECHO) + && messaging() + && !(cmd_silent + msg_silent) + && c != FAIL + && !shortmess(SHM_SEARCHCOUNT) + && msgbuf != NULL) + search_stat(dirc, &pos, msgbuf); + /* * The search command can be followed by a ';' to do another search. * For example: "/pat/;/foo/+3;?bar" @@ -1567,6 +1613,7 @@ end_do_search: if ((options & SEARCH_KEEP) || cmdmod.keeppatterns) spats[0].off = old_off; vim_free(strcopy); + vim_free(msgbuf); return retval; } @@ -4857,6 +4904,124 @@ linewhite(linenr_T lnum) } #endif +/* + * 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; +#ifdef FEAT_RELTIME + proftime_T start; +#endif +#define OUT_OF_TIME 999 + + wraparound = ((dirc == '?' && LT_POS(lastpos, p)) + || (dirc == '/' && LT_POS(p, lastpos))); + + // If anything relevant changed the count has to be recomputed. + // MB_STRNICMP ignores case, but we should not ignore case. + // Unfortunately, there is no MB_STRNICMP function. + if (!(chgtick == CHANGEDTICK(curbuf) + && MB_STRNICMP(lastpat, spats[last_idx].pat, STRLEN(lastpat)) == 0 + && STRLEN(lastpat) == STRLEN(spats[last_idx].pat) + && EQUAL_POS(lastpos, curwin->w_cursor) + && lbuf == curbuf) || wraparound || cur < 0 || cur > 99) + { + cur = 0; + cnt = 0; + CLEAR_POS(&lastpos); + lbuf = curbuf; + } + + if (EQUAL_POS(lastpos, curwin->w_cursor) && !wraparound + && (dirc == '/' ? cur < cnt : cur > 0)) + cur += dirc == '/' ? 1 : -1; + else + { + p_ws = FALSE; +#ifdef FEAT_RELTIME + profile_setlimit(20L, &start); +#endif + while (!got_int && searchit(curwin, curbuf, &lastpos, NULL, + FORWARD, NULL, 1, SEARCH_PEEK + SEARCH_KEEP, + RE_LAST, (linenr_T)0, NULL, NULL) != FAIL) + { +#ifdef FEAT_RELTIME + // Stop after passing the time limit. + if (profile_passed_limit(&start)) + { + cnt = OUT_OF_TIME; + cur = OUT_OF_TIME; + break; + } +#endif + cnt++; + if (LTOREQ_POS(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] = ""; + +#ifdef FEAT_RIGHTLEFT + 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 +#endif + { + 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; + + vim_free(lastpat); + lastpat = vim_strsave(spats[last_idx].pat); + chgtick = CHANGEDTICK(curbuf); + lbuf = curbuf; + lastpos = p; + + // keep the message even after redraw + give_warning(msgbuf, FALSE); + } + p_ws = save_ws; +} + #if defined(FEAT_FIND_ID) || defined(PROTO) /* * Find identifiers or defines in included files. diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -219,6 +219,7 @@ NEW_TESTS = \ test_scroll_opt \ test_scrollbind \ test_search \ + test_search_stat \ test_searchpos \ test_set \ test_sha256 \ @@ -388,6 +389,7 @@ NEW_TESTS_RES = \ test_scriptnames.res \ test_scrollbind.res \ test_search.res \ + test_search_stat.res \ test_shortpathname.res \ test_signals.res \ test_signs.res \ diff --git a/src/testdir/test_search_stat.vim b/src/testdir/test_search_stat.vim new file mode 100644 --- /dev/null +++ b/src/testdir/test_search_stat.vim @@ -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 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -768,6 +768,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1270, +/**/ 1269, /**/ 1268,