There are a couple ways that come to mind. Here's one...
let g:inactivity_limit = 20 " max Insert mode inactivity before fail, in seconds
let g:check_frequency = 10 " seconds between checks
augroup monitor
au!
" when vim starts kick off the infinitely repeating calls to the monitor function
au VimEnter * call timer_start(g:check_frequency * 1000, 'MonitorActivity', {'repeat' : -1})
" when cursor moves in Insert mode update the last activity time
au CursorMovedI * let g:last_activity = reltime()
augroup END
func! MonitorActivity(timer_id)
" when we start we'll initialize the last activity time then return
" ...gives a little grace period at beginning
if ! exists('g:last_activity') || empty(g:last_activity)
let g:last_activity = reltime()
return
endif
" very handy function for our purposes, reltime
let l:diff = reltime(g:last_activity)[0]
if l:diff > g:inactivity_limit
" Not ringing a bell or anything. Up to OP to change this part.
echom "Failed! " . l:diff
let g:last_activity = []
endif
endfunc
This will check, every 10 seconds (configurable) whether the cursor has moved in Insert mode (thanks to CursorMovedI
auto-command/event) in the last 20 seconds (also configurable). The comments in the code have some more details.
IMPORTANT: I leave it to you to figure out what happens when the time limit is exceeded. You'll have to replace the line that just prints "Failed!".
Also, keep in mind that anyone with a fair amount of vim savvy can disable or otherwise mess with this. And if someone went into Insert mode and just lazily moved the cursor back and forth the whole time nothing would happen. But you're asking for free software so them's the breaks. ;)
Update: Okay, since I was curious here, at least, is a function that will ring the basic system bell, and perhaps flash the screen, once a second for # of times specified in the param. (if Vim has 'visualbell'
set or the system bell is otherwise disabled....<shrug>)
func! Alarm(bells)
let l:i = 0
while l:i < a:bells
let l:i += 1 | sleep 1 | norm <c-g>
endwhile
endfunc
Add call Alarm(5)
, for example, to the main function where the "Failed" message currently is.
CursorHold
andCursorHoldI
events. You can use them withautocmd
to echo a message, for example.