3

Is there a way to immediately get feedback about a search string with no matches?

At the moment I have to press Enter first to get Pattern not found. I'd like if the background (of the search line or of vim itself) turned red the moment there are no matches anymore.

The scenario that bugs me is the following: I type / and start entering a complicated string/regexp. Since I have set incsearch the screen starts jumping around until at some point it settles. At this point I have to scan my entire screen to see if something was found or not. This takes a while even with set hlsearch. I have attached a screen recording of an example where a search string doesn't give any results because of a typo (entering 5 instead of 6) but it takes me a second or so to realize that typo. Then I fix the search and it again takes me a second or so to realize that I did find something this time.

https://streamable.com/v5w31

  • I would expect it to be relatively clear with highlight search when it fails: there’s no highlights, even at a quick glance. – D. Ben Knoble Nov 21 '18 at 15:12
  • @D.BenKnoble did you check the video? I don't find the highlights very clear, depending on the syntax highlighting. – Bananach Nov 21 '18 at 16:55
  • 1
    I don't think this is currently possible. Perhaps this issue 453 would solve it – Christian Brabandt Nov 21 '18 at 18:17
3

I came up with a vimscript solution which seems to work (at least according to my quick tests). This is a workaround and it clearly has a lot of place for improvements.

  • It changes the color of the status line using the :highlight command so obviously you need to have set laststatus=2.
  • It uses some global variables, theirs scope could be limited and I think they could even be eliminated.
  • It also uses the autocommand events CmdlineEnter and CmdlineLeave which are included only in recent vim version.
  • I haven't tested the code with complex searches I don't know how well it behaves with complex regexes and escaped characters.

Anyway the idea was mainly to create a proof of concept, not a complete feature.

Add this to your vimrc (actually you should put the function in an autoload directory but that's not the point of the question):

function! HighlightSearch(timer)
    " When it is the first call to the function we save the current status of
    " the StatusLine HL group so that we can restore it when we are done searching
    if (g:firstCall)
        let g:originalStatusLineHLGroup = execute("hi StatusLine")
        let g:firstCall = 0
    endif

    if (exists("g:searching") && g:searching)
        " The variable g:searching is set to 1, we are in the search command line
        " make the highlighting and call the function again after a delay
        let searchString = escape(getcmdline(), ' \')
        let newBG = search(searchString) != 0 ? "green" : "red"
        execute("hi StatusLine ctermbg=" . newBG)
        let g:highlightTimer = timer_start(300, 'HighlightSearch')
    else
        " The variable g:searching is either not set or set to 0, we stopped searching
        " restore the hightlighting and stop calling the function
        let originalBG = matchstr(g:originalStatusLineHLGroup, 'ctermbg=\zs[^ ]\+')
        execute("hi StatusLine ctermbg=" . originalBG)

        if exists("g:highlightTimer")
            call timer_stop(g:highlightTimer)
        endif
    endif
endfunction

" Define an autocmd to call the HighLightSearch function when we enter the search command line
" And a second one to stop the function when we are done searching
augroup betterSeachHighlighting
    autocmd!
    autocmd CmdlineEnter * if (index(['?', '/'], getcmdtype()) >= 0) | let g:searching = 1 | let g:firstCall = 1 | call timer_start(1, 'HighlightSearch') | endif
    autocmd CmdlineLeave * let g:searching = 0
augroup END

The idea is to create two autocommands:

  • The first one is triggered when you enter the command line, it test if you are searching for something (/ or ? in opposition to :), it sets two variables used by the function and call the function.
  • The second one is triggered when you leave the command line, it is used to unset a variable which is used by the function.

The function does three things:

  • It keeps in memory the original highlighting group of the status line to be able to restore it (Using this answer).
  • If you are searching for something it looks for a match, updates the background of the status line accordingly and set a timer to call itself again a few milliseconds later to update the background again.
  • If you are not searching for something anymore it resets the status line highlighting.

Here are some relevant help topic (I'll try to upload a gif of the code in action when I can):

  • sorry for not being able to fix this myself, but now I get Error detected while processing function HighlightSearch: line 19: E417: missing argument: ctermbg= – Bananach Nov 22 '18 at 15:41
  • @Bananach Oh that might be a problem. Actually it is (probably) because you don't have a ctermbg defined in you highlighting group StatusLine. If you are using gVim, you can try to replace every occurences of ctermbg with guibg. Otherwise could you quit vim, restart it and give the output of :hi StatusLine? – statox Nov 22 '18 at 15:57
  • 1
    Thanks figured it out, added hi StatusLine ctermbg=white ctermfg=black. Works nicely. – Bananach Nov 22 '18 at 19:53
  • Since I normally don't use a statusline, I just tried to add set laststatus=2 and set laststatus=1 in the parts of your function corresponding to entering and leaving search mode. This gives the strangest behavior: vim then cycles automatically and perpetually through all the matches of the current search string, and with an empty search string it cycles through the matches of set laststatus= – Bananach Nov 23 '18 at 10:33
  • I fixed this by wrapping the set command in if (&laststatus!=2) statements – Bananach Nov 23 '18 at 10:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.