buffer heartbeats in memory and send to wakatime-cli every 10 seconds

This commit is contained in:
Alan Hamlett 2017-04-22 02:09:01 -07:00
parent d350a97b72
commit 8a72498e13

View file

@ -45,6 +45,8 @@ let s:VERSION = '4.0.15'
let s:is_debug_mode_on = s:false let s:is_debug_mode_on = s:false
let s:local_cache_expire = 10 " seconds between reading s:data_file let s:local_cache_expire = 10 " seconds between reading s:data_file
let s:last_heartbeat = [0, 0, ''] let s:last_heartbeat = [0, 0, '']
let s:heartbeats_buffer = []
let s:last_sent = 0
" For backwards compatibility, rename wakatime.conf to wakatime.cfg " For backwards compatibility, rename wakatime.conf to wakatime.cfg
if !filereadable(s:config_file) if !filereadable(s:config_file)
@ -176,7 +178,6 @@ let s:VERSION = '4.0.15'
endif endif
endif endif
call writefile(output, s:config_file) call writefile(output, s:config_file)
return
endfunction endfunction
function! s:GetCurrentFile() function! s:GetCurrentFile()
@ -187,6 +188,10 @@ let s:VERSION = '4.0.15'
return substitute(shellescape(a:arg), '!', '\\!', '') return substitute(shellescape(a:arg), '!', '\\!', '')
endfunction endfunction
function! s:JsonEscape(str)
return substitute(a:str, '"', '\\"', 'g')
endfunction
function! s:JoinArgs(args) function! s:JoinArgs(args)
let safeArgs = [] let safeArgs = []
for arg in a:args for arg in a:args
@ -202,51 +207,117 @@ let s:VERSION = '4.0.15'
return s:false return s:false
endfunction endfunction
function! s:SendHeartbeat(file, time, is_write, last) function! s:AppendHeartbeat(file, now, is_write, last)
let file = a:file let file = a:file
if file == '' if file == ''
let file = a:last[2] let file = a:last[2]
endif endif
if file != '' if file != ''
let python_bin = g:wakatime_PythonBinary let heartbeat = {}
if s:IsWindows() let heartbeat.entity = file
if python_bin == 'python' let heartbeat.time = reltimestr(reltime())
let python_bin = 'pythonw' let heartbeat.is_write = a:is_write
endif
endif
let cmd = [python_bin, '-W', 'ignore', s:cli_location]
let cmd = cmd + ['--entity', file]
let cmd = cmd + ['--plugin', printf('vim/%d vim-wakatime/%s', v:version, s:VERSION)]
if a:is_write
let cmd = cmd + ['--write']
endif
if !empty(&syntax) if !empty(&syntax)
let cmd = cmd + ['--language', &syntax] let heartbeat.language = &syntax
else else
if !empty(&filetype) if !empty(&filetype)
let cmd = cmd + ['--language', &filetype] let heartbeat.language = &filetype
endif endif
endif endif
let stdout = '' let s:heartbeats_buffer = s:heartbeats_buffer + [heartbeat]
if s:IsWindows() call s:SetLastHeartbeat(a:now, a:now, file)
if s:is_debug_mode_on if s:IsWindows() && !s:is_debug_mode_on
let stdout = system('(' . s:JoinArgs(cmd) . ')') " Windows doesn't play nice with system(), so can't pass input
" as STDIN and we are forced to send heartbeats individually.
call s:SendHeartbeats()
endif
endif
endfunction
function! s:SendHeartbeats()
if len(s:heartbeats_buffer) == 0
return
endif
let heartbeat = s:heartbeats_buffer[0]
let s:heartbeats_buffer = s:heartbeats_buffer[1:-1]
if len(s:heartbeats_buffer) > 0
let extra_heartbeats = s:GetHeartbeatsJson()
else
let extra_heartbeats = ''
endif
let python_bin = g:wakatime_PythonBinary
if s:IsWindows()
if python_bin == 'python'
let python_bin = 'pythonw'
endif
endif
let cmd = [python_bin, '-W', 'ignore', s:cli_location]
let cmd = cmd + ['--entity', heartbeat.entity]
let cmd = cmd + ['--time', heartbeat.time]
let cmd = cmd + ['--plugin', printf('vim/%d vim-wakatime/%s', v:version, s:VERSION)]
if heartbeat.is_write
let cmd = cmd + ['--write']
endif
if has_key(heartbeat, 'language')
let cmd = cmd + ['--language', heartbeat.language]
endif
if extra_heartbeats != ''
let cmd = cmd + ['--extra-heartbeats']
endif
let stdout = ''
if s:IsWindows()
if s:is_debug_mode_on
if extra_heartbeats != ''
let stdout = system('(' . s:JoinArgs(cmd) . ')', extra_heartbeats)
else else
exec 'silent !start /min cmd /c "' . s:JoinArgs(cmd) . '"' let stdout = system('(' . s:JoinArgs(cmd) . ')')
endif endif
else else
if s:is_debug_mode_on exec 'silent !start /min cmd /c "' . s:JoinArgs(cmd) . '"'
endif
else
if s:is_debug_mode_on
if extra_heartbeats != ''
let stdout = system(s:JoinArgs(cmd), extra_heartbeats)
else
let stdout = system(s:JoinArgs(cmd)) let stdout = system(s:JoinArgs(cmd))
endif
else
if extra_heartbeats != ''
let stdout = system(s:JoinArgs(cmd) . ' &', extra_heartbeats)
else else
let stdout = system(s:JoinArgs(cmd) . ' &') let stdout = system(s:JoinArgs(cmd) . ' &')
endif endif
endif endif
call s:SetLastHeartbeat(a:time, a:time, file)
if s:is_debug_mode_on && stdout != ''
echo '[WakaTime] Heartbeat Command: ' . s:JoinArgs(cmd)
echo '[WakaTime] Error: ' . stdout
endif
endif endif
let s:last_sent = localtime()
redraw! " need to repaint in case a key was pressed while sending
if s:is_debug_mode_on && stdout != ''
echo '[WakaTime] Heartbeat Command: ' . s:JoinArgs(cmd) . "\n[WakaTime] Error: " . stdout
endif
endfunction
function! s:GetHeartbeatsJson()
let arr = []
for heartbeat in s:heartbeats_buffer
let heartbeat_str = '{"entity": "' . s:JsonEscape(heartbeat.entity) . '", '
let heartbeat_str = heartbeat_str . '"timestamp": ' . heartbeat.time . ', '
let heartbeat_str = heartbeat_str . '"is_write": '
if heartbeat.is_write
let heartbeat_str = heartbeat_str . 'true'
else
let heartbeat_str = heartbeat_str . 'false'
endif
if has_key(heartbeat, 'language')
let heartbeat_str = heartbeat_str . ', "language": "' . s:JsonEscape(heartbeat.language) . '"'
endif
let heartbeat_str = heartbeat_str . '}'
let arr = arr + [heartbeat_str]
endfor
let s:heartbeats_buffer = []
return '[' . join(arr, ',') . ']'
endfunction endfunction
function! s:GetLastHeartbeat() function! s:GetLastHeartbeat()
@ -262,12 +333,12 @@ let s:VERSION = '4.0.15'
return s:last_heartbeat return s:last_heartbeat
endfunction endfunction
function! s:SetLastHeartbeatLocally(time, last_update, file) function! s:SetLastHeartbeatInMemory(time, last_update, file)
let s:last_heartbeat = [a:time, a:last_update, a:file] let s:last_heartbeat = [a:time, a:last_update, a:file]
endfunction endfunction
function! s:SetLastHeartbeat(time, last_update, file) function! s:SetLastHeartbeat(time, last_update, file)
call s:SetLastHeartbeatLocally(a:time, a:last_update, a:file) call s:SetLastHeartbeatInMemory(a:time, a:last_update, a:file)
call writefile([substitute(printf('%d', a:time), ',', '.', ''), substitute(printf('%d', a:last_update), ',', '.', ''), a:file], s:data_file) call writefile([substitute(printf('%d', a:time), ',', '.', ''), substitute(printf('%d', a:last_update), ',', '.', ''), a:file], s:data_file)
endfunction endfunction
@ -300,25 +371,27 @@ let s:VERSION = '4.0.15'
let s:is_debug_mode_on = s:false let s:is_debug_mode_on = s:false
endfunction endfunction
" }}} function! s:InitAndHandleActivity(is_write)
" Event Handlers {{{
function! s:handleActivity(is_write)
call s:SetupDebugMode() call s:SetupDebugMode()
call s:SetupConfigFile() call s:SetupConfigFile()
call s:HandleActivity(a:is_write)
endfunction
function! s:HandleActivity(is_write)
let file = s:GetCurrentFile() let file = s:GetCurrentFile()
let now = localtime()
let last = s:GetLastHeartbeat()
if !empty(file) && file !~ "-MiniBufExplorer-" && file !~ "--NO NAME--" && file !~ "^term:" if !empty(file) && file !~ "-MiniBufExplorer-" && file !~ "--NO NAME--" && file !~ "^term:"
let last = s:GetLastHeartbeat()
let now = localtime()
if a:is_write || s:EnoughTimePassed(now, last) || file != last[2] if a:is_write || s:EnoughTimePassed(now, last) || file != last[2]
call s:SendHeartbeat(file, now, a:is_write, last) call s:AppendHeartbeat(file, now, a:is_write, last)
else else
if now - s:last_heartbeat[0] > s:local_cache_expire if now - s:last_heartbeat[0] > s:local_cache_expire
call s:SetLastHeartbeatLocally(now, last[1], last[2]) call s:SetLastHeartbeatInMemory(now, last[1], last[2])
endif endif
endif endif
if now - s:last_sent > 10
call s:SendHeartbeats()
endif
endif endif
endfunction endfunction
@ -328,11 +401,10 @@ let s:VERSION = '4.0.15'
" Autocommand Events {{{ " Autocommand Events {{{
augroup Wakatime augroup Wakatime
autocmd! autocmd BufEnter,VimEnter * call s:InitAndHandleActivity(s:false)
autocmd BufEnter * call s:handleActivity(s:false) autocmd CursorMoved,CursorMovedI * call s:HandleActivity(s:false)
autocmd VimEnter * call s:handleActivity(s:false) autocmd BufWritePost * call s:HandleActivity(s:true)
autocmd BufWritePost * call s:handleActivity(s:true) autocmd QuitPre * call s:SendHeartbeats()
autocmd CursorMoved,CursorMovedI * call s:handleActivity(s:false)
augroup END augroup END
" }}} " }}}