Skip to content

Add the way to get search stat information when 'set shortmess-=S'#4446

Closed
tyru wants to merge 20 commits intovim:masterfrom
tyru:add-searchstat-var
Closed

Add the way to get search stat information when 'set shortmess-=S'#4446
tyru wants to merge 20 commits intovim:masterfrom
tyru:add-searchstat-var

Conversation

@tyru
Copy link
Copy Markdown

@tyru tyru commented May 28, 2019

As of 8.1.1270, vim can show current match position and count like [1/20] at command-line.
It is useful but cannot get the status from Vim script, without using execute(':unsilent :norm! n') as test script does.
But I don't want to execute n to know the status, because it throws CursorMoved, and changes jumplist, and so on.

So I add a v:searchstat variable which can be referenced by Vim script without running any command.


EDIT: added a function (searchcount()) instead of a variable.

@tyru
Copy link
Copy Markdown
Author

tyru commented May 28, 2019

Usecase

  • Show the status in statusline.

Problem

As of 8.1.1270, vim can show current match position and count like [1/20] at command-line.
It is useful but cannot get the status from Vim script, without using execute(':unsilent :norm! n') as test script does.
But I don't want to execute n to know the status, because it throws CursorMoved, and changes jumplist, and so on.

Current specification has another problem.
If user has mappings of n, N,

nnoremap n nzz
nnoremap N Nzz

zz redraws the screen and [1/20] message disappears.

If user can get the status by variable, user can :echo the status at any timing.

set shortmess-=S

" zz removes search status information
"nnoremap n nzz
"nnoremap N Nzz

nnoremap n nzz:echo printf('[%d/%d]', v:searchstat.current, v:searchstat.count)<CR>
nnoremap N Nzz:echo printf('[%d/%d]', v:searchstat.current, v:searchstat.count)<CR>

@mattn
Copy link
Copy Markdown
Member

mattn commented May 28, 2019

Please add test for empty status. Also I'm interesting whether the stat must be reset somewhere.

@chrisbra
Copy link
Copy Markdown
Member

I think this needs some ifdef FEAT_EVAL for the tiny builds. Also I'd also be interested how long the v:searchstat variable is valid and when it will be reset.

@tyru
Copy link
Copy Markdown
Author

tyru commented May 28, 2019

Okay. >empty status, ifdef

Currently no change (cleared, updated) is made until the next n, N, /, ... commands run AND shortmess does not contain S.

I'm also wondering that

  • when the variable is changed (updated, cleared)
  • how long the variable is valid

Personally I think

  • set shortmess+=S should make the variable empty
  • otherwise, the variable does not change until the next n, N, /, ... commands

How do you think?

@brammool
Copy link
Copy Markdown
Contributor

brammool commented May 28, 2019 via email

@chrisbra
Copy link
Copy Markdown
Member

I wonder, if you show the stats in the statusline, shouldn't there be a way to not show them on the command line?

After thinking some more about the use case, I also wondered about that. Once we have v:searchstat, I guess many people wouldn't want to have it displayed in the command line anymore, but let the plugins make use of it. Probably would need another option.

@tyru
Copy link
Copy Markdown
Author

tyru commented May 29, 2019

I wonder, if you show the stats in the statusline, shouldn't there be a way to not show them on the command line?

Ah, that's true.

v:searchstat should probably be cleared when editing another file. Perhaps also when switching to another window.

Yes. That's right.

Probably would need another option.

You mean another interface (function, option, ...) is better?
I think a variable is good to make use of the stat because it's simple, and fast to reference the stat from statusline (function is a little bit slow?).
But please tell me if you have any idea.

@brammool
Copy link
Copy Markdown
Contributor

brammool commented May 29, 2019 via email

@mattn
Copy link
Copy Markdown
Member

mattn commented May 29, 2019

How about to add new identify of the status for statusline? ex %t
And add new function to get values of the identiy. ex getstatuslineinfo("%t") ?

@tyru
Copy link
Copy Markdown
Author

tyru commented Jun 1, 2019

How about to add new identify of the status for statusline? ex %t
And add new function to get values of the identiy. ex getstatuslineinfo("%t") ?

It also has lifecycle issue yet.

If statusline has %t, the [1/20] stat appears in current window.
There should be many options when it disappears (after n seconds, next search command run, ...).

And in most cases, users want to see the stat only in statusline of current window.
But Vim searches all statuslines of current tab page.


So, I want to simply add a function to get the stat.
Sorry for confusing @brammool and @chrisbra,
but just adding a function is simple and avoids lifecycle issue,
users / plugins can use it and determine when to delete the display, and so on.

One problem is the performance.
searching word count may become slow even max count is 99.

So the function should be able to get the cached value when shortmess does not contain S.
In the latest commit, searchcount() was added and if the arguments were omitted, it can get the cached value.
(Please forgive horrible code now...)

I wrote also PoC API in help:

diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 15ffed343..f8b7baafa 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2564,6 +2564,7 @@ screenrow()			Number	current cursor row
 screenstring({row}, {col})	String	characters at screen position
 search({pattern} [, {flags} [, {stopline} [, {timeout}]]])
 				Number	search for {pattern}
+searchcount([{dict}])		Dictionary	the search stats
 searchdecl({name} [, {global} [, {thisblock}]])
 				Number	search for variable declaration
 searchpair({start}, {middle}, {end} [, {flags} [, {skip} [...]]])
@@ -7910,6 +7911,31 @@ search({pattern} [, {flags} [, {stopline} [, {timeout}]]])	*search()*
 		The 'n' flag tells the function not to move the cursor.
 
 
+searchcount({dict})					*searchcount()*
+		The result is almost the same structure as {dict}.
+		Some keys can be specified by only arguments.
+		This function changes {dict} in-place and returns it.
+		If the keys of {dict} are omitted, default values were used.
+
+		 key		type	arg only	meaning ~
+		  dir		String	no	(default: "/" if |v:searchforward| is
+					no	non-zero, otherwise "?")
+		  lastpos	List	no	`[lnum, col, off]` . see |getpos()|
+					no	(default: the last position if
+					no	|'shortmess'| does not contain "S",
+					no	otherwise `[0, 0, 0]`)
+		  pattern	String	no	(default: |@/|)
+		  bufnr		Number	no	(default: current buffer)
+		  changedtick	Number	no	(default: |b:changedtick|)
+		  current	Number	no
+		  count		Number	no
+		  wrap		Number	no
+		  maxcount	Number	yes	Max number of count (default: 99)
+		  recompute	Number	yes	if specified and zero, the
+						last computed values when
+						|'shortmess'| has not "S" flag
+						(default: non-zero)
+
 searchdecl({name} [, {global} [, {thisblock}]])			*searchdecl()*
 		Search for the declaration of {name}.
 

@tyru
Copy link
Copy Markdown
Author

tyru commented Jun 1, 2019

To get the cached value (when set shortmess-=S):

:echo searchcount({'recompute': 0})
{'bufnr': 1, 'pattern': '\<Vim\>', 'dir': '/', 'wrap': 0, 'changedtick': 4, 'lastpos': [65, 17, 0], 'current': 3, 'recompute': 0, 'count': 10}

@tyru tyru changed the title Add v:searchstat variable Add the way to get search stat information when 'set shortmess-=S' Jun 4, 2019
@brammool
Copy link
Copy Markdown
Contributor

A function avoids the need for tricks with updating the variable, I like that.
The function you define is rather complex. Why not just return a dict with the info, without passing in anything? And I don't see a good reason to also include things already available in another way.

@brammool
Copy link
Copy Markdown
Contributor

Are you planning to make the suggested changes?

@tyru
Copy link
Copy Markdown
Author

tyru commented Sep 13, 2019

sorry, I missed that.
I'll do it.

@tyru
Copy link
Copy Markdown
Author

tyru commented Jan 13, 2020

Why not just return a dict with the info, without passing in anything? And I don't see a good reason to also include things already available in another way.

Done it.
Sorry for not responding for a long time.
Please see the help and tests for the detailed function behavior.

@brammool
I also want to add options to change the max count & timeout of n, N (SEARCH_STAT_DEF_MAX_COUNT, SEARCH_STAT_DEF_TIMEOUT).
How do you think?

@codecov-io
Copy link
Copy Markdown

codecov-io commented Jan 13, 2020

Codecov Report

Merging #4446 into master will increase coverage by 2.42%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #4446      +/-   ##
==========================================
+ Coverage   80.45%   82.88%   +2.42%     
==========================================
  Files         110      134      +24     
  Lines      143354   148062    +4708     
==========================================
+ Hits       115335   122720    +7385     
+ Misses      28019    25342    -2677
Impacted Files Coverage Δ
src/libvterm/src/mouse.c 0% <0%> (-96.43%) ⬇️
src/indent.c 79.58% <0%> (-9.85%) ⬇️
src/libvterm/src/unicode.c 89.13% <0%> (-5.75%) ⬇️
src/ex_cmds2.c 83.84% <0%> (-4.22%) ⬇️
src/libvterm/src/parser.c 90.16% <0%> (-1.41%) ⬇️
src/libvterm/src/state.c 90% <0%> (-1.09%) ⬇️
src/regexp_nfa.c 90.95% <0%> (-0.82%) ⬇️
src/if_cscope.c 76.16% <0%> (-0.27%) ⬇️
src/beval.c 83.96% <0%> (-0.11%) ⬇️
src/charset.c 88.29% <0%> (-0.08%) ⬇️
... and 107 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update af7e979...23e01ed. Read the comment docs.

@tyru
Copy link
Copy Markdown
Author

tyru commented Jan 13, 2020

I also found the way to show search stat information on command-line
even n and N were mapped to nzz, Nzz and zz clears command-line (I did this).

nnoremap n nzz
nnoremap N Nzz

This is little hacky thing so that I hesitated to add this to vim help.
I publish the diff here however.

diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index f65d46fec..5e698c80e 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -6738,6 +6738,26 @@ A jump table for the options with a short description can be found at |Q_op|.
 	NOTE: This option is set to the Vi default value when 'compatible' is
 	set and to the Vim default value when 'compatible' is reset.
 
+							*shortmess-S-faq*
+	When you set `set shortmess-=S`, if you want to show the count message
+	even when you have mapped to |n| and |N| like this: >
+
+		" Centering screen after n, N.
+		" But zz also clears search count message!
+		nnoremap n nzz
+		nnoremap N Nzz
+<
+	You can do like this: >
+
+		nnoremap <silent> <SID>(cmd:n) :<C-u>let w:search_stat = execute('unsilent norm! n')->split('\n')->get(-1, '')<CR>
+		nnoremap <silent> <SID>(cmd:N) :<C-u>let w:search_stat = execute('unsilent norm! N')->split('\n')->get(-1, '')<CR>
+		nnoremap <silent> <SID>(echo-search-stat) :<C-u>redraw <Bar> echo '' <Bar> echo w:search_stat<CR>
+
+		nmap <script> n <SID>(cmd:n)zz<SID>(echo-search-stat)
+		nmap <script> N <SID>(cmd:N)zz<SID>(echo-search-stat)
+<
+	Or you can use |searchcount()| function for more further information.
+
 				 *'shortname'* *'sn'* *'noshortname'* *'nosn'*
 'shortname' 'sn'	boolean	(default off)
 			local to buffer

@tyru
Copy link
Copy Markdown
Author

tyru commented Jan 13, 2020

I also wrote detailed example to show the search stat in statusline. c4100e1

@tyru tyru force-pushed the add-searchstat-var branch from 9c799aa to 7762334 Compare January 13, 2020 14:13
@tyru
Copy link
Copy Markdown
Author

tyru commented Jan 13, 2020

  • 61f6881: Support exact_match of return value (could not know if cursor is on the matched text, or just after cursor position)
  • 25da18f: Rename timeout to incomplete. and support to check if search exceeded max count
    • incomplete == 0: search was fully completed
    • incomplete == 1: recomputing was timed out
    • incomplete == 2: max count exceeded

@tyru tyru force-pushed the add-searchstat-var branch from 0707327 to 93e5e79 Compare January 14, 2020 03:47
@tyru
Copy link
Copy Markdown
Author

tyru commented Jan 14, 2020

93e5e79: Fixed the build was broken. And add max_count property of return value (see example for help)

@brammool This should be the final for my second proposal. Please take a look if you are interested.

@brammool
Copy link
Copy Markdown
Contributor

brammool commented Jun 1, 2020

This function looks useful, but I'm not sure about the details. Let me include it as-is now, and we can further improve it after that.

@brammool brammool closed this in e8f5ec0 Jun 1, 2020
@tyru
Copy link
Copy Markdown
Author

tyru commented Jun 2, 2020

Thanks!

@lacygoill
Copy link
Copy Markdown

lacygoill commented Oct 2, 2020

I don't know whether it can be optimized, but the longer the line the slower searchcount() seems to be:

vim9script
@/ = 'x'
repeat('x', 100000)->setline(1)
nno <expr> n Func()
def g:Func(): string
    searchcount({'maxcount': 1000, 'timeout': 500})
    return 'n'
enddef

Keep pressing n for a few seconds, then stop: Vim still needs several seconds to process the keypresses.

This is not merely caused by the size of the buffer or the number of matches. Here is the exact same text, but splitted on 1000 lines:

vim9script
@/ = 'x'
range(1000)->map({i -> repeat('x', 100)->setline(i + 1)})
nno <expr> n Func()
def g:Func(): string
    searchcount({'maxcount': 1000, 'timeout': 500})
    return 'n'
enddef

Keep pressing n for a few seconds, then stop: Vim stops immediately.

The issue is really the length of the line.

@brammool
Copy link
Copy Markdown
Contributor

brammool commented Oct 2, 2020

This will require profiling to find out what the time is spent on.

shadmansaleh added a commit to shadmansaleh/neovim that referenced this pull request Apr 20, 2021
Problem:    Cannot get the search statistics.
Solution:   Add the searchcount() function. (Fujiwara Takuya, closes vim/vim#4446)
vim/vim@e8f5ec0
shadmansaleh added a commit to shadmansaleh/neovim that referenced this pull request Apr 20, 2021
Problem:    Cannot get the search statistics.
Solution:   Add the searchcount() function. (Fujiwara Takuya, closes vim/vim#4446)
vim/vim@e8f5ec0
shadmansaleh added a commit to shadmansaleh/neovim that referenced this pull request Apr 20, 2021
Problem:    Cannot get the search statistics.
Solution:   Add the searchcount() function. (Fujiwara Takuya, closes vim/vim#4446)
vim/vim@e8f5ec0
shadmansaleh added a commit to shadmansaleh/neovim that referenced this pull request Apr 20, 2021
Problem:    Cannot get the search statistics.
Solution:   Add the searchcount() function. (Fujiwara Takuya, closes vim/vim#4446)
vim/vim@e8f5ec0
shadmansaleh added a commit to shadmansaleh/neovim that referenced this pull request Apr 20, 2021
Problem:    Cannot get the search statistics.
Solution:   Add the searchcount() function. (Fujiwara Takuya, closes vim/vim#4446)
vim/vim@e8f5ec0
shadmansaleh added a commit to shadmansaleh/neovim that referenced this pull request Apr 21, 2021
Problem:    Cannot get the search statistics.
Solution:   Add the searchcount() function. (Fujiwara Takuya, closes vim/vim#4446)
vim/vim@e8f5ec0
shadmansaleh added a commit to shadmansaleh/neovim that referenced this pull request Apr 21, 2021
Problem:    Cannot get the search statistics.
Solution:   Add the searchcount() function. (Fujiwara Takuya, closes vim/vim#4446)
vim/vim@e8f5ec0
shadmansaleh added a commit to shadmansaleh/neovim that referenced this pull request May 8, 2021
Problem:    Cannot get the search statistics.
Solution:   Add the searchcount() function. (Fujiwara Takuya, closes vim/vim#4446)
vim/vim@e8f5ec0

Additional changes:
- Tests weren't passing because the test ran assuming the cursor was at
  start of buffer but append() left the cursor at end of buffer .
  So cursor is moved to start of buffer after append.
- searchcount() added to list of builtin functions.
shadmansaleh added a commit to shadmansaleh/neovim that referenced this pull request May 9, 2021
Problem:    Cannot get the search statistics.
Solution:   Add the searchcount() function. (Fujiwara Takuya, closes vim/vim#4446)
vim/vim@e8f5ec0

Additional changes:
- Tests weren't passing because the test ran assuming the cursor was at
  start of buffer but append() left the cursor at end of buffer .
  So cursor is moved to start of buffer after append.
- searchcount() added to list of builtin functions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants