Here is a solution using Vim's recent +timers
feature (which falls back to a non-blinking temporary highlight like Damian Conway's original code if you are a using an older version of Vim that predates the +timers
feature).
It sets up two timers: one infinitely repeating timer that toggles the blink (s:blink_timer_id
), and a second to cancel the blinking (s:blink_stop_id
) after the specified time period (s:blink_length
) has elapsed. (Initially I tried doing this with a single timer that repeated the correct number of times, but that didn't work well.)
This means that Vim is not blocking while match is blinking, so you can enter new commands immediately. If you press n
again, it will reset the blink timers.
It also sets up two autocommands, to cancel the blinking immediately if you move the cursor or enter insert mode.
" Modified version of Damian Conway's Die Blinkënmatchen: highlight matches
"
" This is how long you want the blinking to last in milliseconds. If you're
" using an earlier Vim without the `+timers` feature, you need a much shorter
" blink time because Vim blocks while it waits for the blink to complete.
let s:blink_length = has("timers") ? 500 : 100
if has("timers")
" This is the length of each blink in milliseconds. If you just want an
" interruptible non-blinking highlight, set this to match s:blink_length
" by uncommenting the line below
let s:blink_freq = 50
"let s:blink_freq = s:blink_length
let s:blink_match_id = 0
let s:blink_timer_id = 0
let s:blink_stop_id = 0
" Toggle the blink highlight. This is called many times repeatedly in order
" to create the blinking effect.
function! BlinkToggle(timer_id)
if s:blink_match_id > 0
" Clear highlight
call BlinkClear()
else
" Set highlight
let s:blink_match_id = matchadd('ErrorMsg', s:target_pat, 101)
redraw
endif
endfunction
" Remove the blink highlight
function! BlinkClear()
call matchdelete(s:blink_match_id)
let s:blink_match_id = 0
redraw
endfunction
" Stop blinking
"
" Cancels all the timers and removes the highlight if necessary.
function! BlinkStop(timer_id)
" Cancel timers
if s:blink_timer_id > 0
call timer_stop(s:blink_timer_id)
let s:blink_timer_id = 0
endif
if s:blink_stop_id > 0
call timer_stop(s:blink_stop_id)
let s:blink_stop_id = 0
endif
" And clear blink highlight
if s:blink_match_id > 0
call BlinkClear()
endif
endfunction
augroup die_blinkmatchen
autocmd!
autocmd CursorMoved * call BlinkStop(0)
autocmd InsertEnter * call BlinkStop(0)
augroup END
endif
function! HLNext(blink_length, blink_freq)
let s:target_pat = '\c\%#'.@/
if has("timers")
" Reset any existing blinks
call BlinkStop(0)
" Start blinking. It is necessary to call this now so that the match is
" highlighted initially (in case of large values of a:blink_freq)
call BlinkToggle(0)
" Set up blink timers.
let s:blink_timer_id = timer_start(a:blink_freq, 'BlinkToggle', {'repeat': -1})
let s:blink_stop_id = timer_start(a:blink_length, 'BlinkStop')
else
" Vim doesn't have the +timers feature. Just use Conway's original
" code.
" Highlight the match
let ring = matchadd('ErrorMsg', s:target_pat, 101)
redraw
" Wait
exec 'sleep ' . a:blink_length . 'm'
" Remove the highlight
call matchdelete(ring)
redraw
endif
endfunction
" Set up maps for n and N that blink the match
execute printf("nnoremap <silent> n n:call HLNext(%d, %d)<cr>", s:blink_length, has("timers") ? s:blink_freq : s:blink_length)
execute printf("nnoremap <silent> N N:call HLNext(%d, %d)<cr>", s:blink_length, has("timers") ? s:blink_freq : s:blink_length)
sleep
instruction and I don't think it is possible to interrupt such an instruction.sleep
expires.getchar(1)
to see if there is a keypress waiting on the input buffer. @statox