1

Based on the accepted answer to this previous question, I put the code below in my .vimrc, which changes the color of my statusline depending on whether my search (started with / or ?) yields results or not.

Unfortunately, I don't understand vimscript, so I cannot fix a simple problem with this code:

When I press / and then immediately cancel the search with <Esc>, the cursor jumps to the first match of whatever previous search term was used.

For example, assume I do /foo<CR>. I move around for a while and think I want to search again, press / but then remember I need to fix something where I am and press <Esc>. This takes me back to foo instead of leaving me where I was.

The problem with the code is also apparent in the fact that the status line immediately turns red or green when I press /, depending on whether the previous search was successful or not.

How can I change the code to 'forget' about the previous search?

" Define an autocmd to call the HighLightSearch function when we enter the search command line
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 ctermfg=" . 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, 'ctermfg=\zs[^ ]\+')
        execute("hi StatusLine ctermfg=" . originalBG)

        if exists("g:highlightTimer")
            call timer_stop(g:highlightTimer)
        endif
    endif
endfunction
" 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

2 Answers 2

1

I think the main problem is that the search() function is moving your cursor. You can fix this by passing the 'n' flag:

let newBG = search(searchString, 'n') != 0 ? "green" : "red"

Then you have the problem that when you first type / the code highlights the status line according to the previous search. The cleanest way to fix is this not to run the code if no search has yet been entered:

if !empty(searchString)
  let newBG = search(searchString, 'n') != 0 ? "green" : "red"
  execute("hi StatusLine ctermfg=" . newBG)
endif

Extra credit

If you really needed to clear the previous search for some reason, you could do so by setting the last search pattern register, "/, which contains the contents of the previous search. You can do so with either of the :let command or the setreg() function:

:let @/=''

:call setreg=('/', [])
0
if searchString != ""
    let newBG = search(searchString) != 0 ? "green" : "red"
    execute("hi StatusLine ctermfg=" . newBG)
endif

Explanation: It seems vim's search function is the culprit (note that the function in question continuously runs vim's search to update the color of the statusline). It automatically uses the previous search term if nothing is entered. This can be tested in a clean vim:

  1. vim --clean
  2. ia<CR>b<CR>a<esc> (enter three lines, each with a single letter, end at third line)
  3. /a<CR> (jumps to first line)
  4. /<CR> (jumps to third line, because it uses the search term a)

WARNING: This introduces another problem: The hlsearch-highlight skips one match now. What is highlighted before pressing Enter is not the closed match (which is what the search actually ends up presenting at pressing Enter).

6
  • 1
    An explanation to accompany this answer would be nice...
    – D. Ben Knoble
    Commented Dec 17, 2018 at 20:17
  • @DBenKnoble I have no idea how this answer works. Why would I jump to the previous search result if the searchString is empty?
    – Bananach
    Commented Dec 18, 2018 at 5:36
  • 1
    @DBenKnoble I came up with an explanation
    – Bananach
    Commented Dec 18, 2018 at 14:25
  • @DBenKnoble Unfortunately, I just noticed that my fix introduces another problem. The hlsearch-highlight always skip one match now. What is highlighted before I press Enter is not the closed match (and is also not what the search actually ends up with when I hit Enter)
    – Bananach
    Commented Dec 18, 2018 at 15:30
  • 1
    Thats strange... but also at this point I must mention I don’t see the use of this behavior (i use hlsearch, and vim gives an error if there are no results)
    – D. Ben Knoble
    Commented Dec 18, 2018 at 16:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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