I have a todo.txt file, and I think it'd be fun to be able to position my cursor on a todo item (a line in my todo.txt file), press a key combination, and have Vim start counting the minutes. Then, when I press another key combination, it would stop counting, and insert the number of minutes elapsed, like min:25. Is there a way to do that in vim?

What would also be awesome is if I pressed the key combination on a line already beginning min:, it would append the minutes to those existing minutes.


I played with it and came up with the following script. To make it reliable I had to make it handle the format of "minMM:SS" where MM and SS are minutes and seconds.

I suspect you will need to modify this somehow to fit your actual needs, but the script basically starts counting when you type, in normal mode, the key sequence: \sc

Then it will stop counting and append to the cursor position the above mentioned format when you type the key sequence: \ec

If the line already contains a time stamp matching the above format, it will add to it instead.

Note that if you have changed your mapleader you would use that instead of \ in the above key sequences.

function! s:Start()
    if exists('b:CountMinutesStart')
        echohl ERROR
        echomsg "Already counting."
        echohl NONE

    echohl TODO
    echomsg "Counting started."
    echohl NONE
    let b:CountMinutesStart = localtime()

function! s:Stop()
    if !exists('b:CountMinutesStart')
        echohl ERROR
        echomsg "Not counting."
        echohl NONE
        return -1

    let l:start = b:CountMinutesStart
    let l:end = localtime()
    unlet b:CountMinutesStart
    let l:elapsed = l:end - l:start

    echohl TODO
    echomsg "Elapsed time since start: " . s:Format(l:elapsed)
    echohl NONE

    return l:elapsed

function! s:Format(seconds)
    let l:minutes = a:seconds / 60
    let l:seconds = a:seconds % 60
    return printf('min%02d:%02d', l:minutes, l:seconds)

function! s:InsertTime()
    let l:seconds = s:Stop()
    if l:seconds == -1
    let l:line = getline('.')
    if l:line =~ 'min\d\{2}:\d\{2}'
        let l:tmp = split(substitute(l:line, '.*min\(\d\{2}\):\(\d\{2}\).*', '\1 \2', ''), ' ')
        let l:seconds = l:seconds + (l:tmp[0] * 60 + l:tmp[1])
        call setline('.', substitute(l:line, 'min\d\{2}:\d\{2}', s:Format(l:seconds), ''))
        exe 'normal a' . s:Format(l:seconds)

command! StartCounting call s:Start()
command! StopCounting call s:InsertTime()

nmap <silent> <leader>sc :StartCounting<cr>
nmap <silent> <leader>ec :StopCounting<cr>
  • Awesome. I had to change 'normal a' to 'normal A' . ' ', but otherwise it works like a charm! Thanks! – Jonathan Oct 6 '15 at 16:32

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.