2013-06-25 07:57:15 +00:00
|
|
|
" ============================================================================
|
|
|
|
" File: wakatime.vim
|
2013-07-02 09:24:04 +00:00
|
|
|
" Description: Automatic time tracking for Vim.
|
2013-09-05 05:32:20 +00:00
|
|
|
" Maintainer: WakaTime <support@wakatime.com>
|
2014-03-13 00:32:05 +00:00
|
|
|
" License: BSD, see LICENSE.txt for more details.
|
2014-03-14 20:35:11 +00:00
|
|
|
" Website: https://wakatime.com/
|
2013-06-25 07:57:15 +00:00
|
|
|
" ============================================================================
|
|
|
|
|
2017-02-21 00:28:01 +00:00
|
|
|
let s:VERSION = '4.0.14'
|
2013-07-09 16:55:22 +00:00
|
|
|
|
2013-06-26 04:09:52 +00:00
|
|
|
|
2013-06-25 07:57:15 +00:00
|
|
|
" Init {{{
|
|
|
|
|
2013-07-02 09:24:04 +00:00
|
|
|
" Check Vim version
|
|
|
|
if v:version < 700
|
|
|
|
echoerr "This plugin requires vim >= 7."
|
|
|
|
finish
|
2013-06-25 07:57:15 +00:00
|
|
|
endif
|
2013-07-02 09:24:04 +00:00
|
|
|
|
2017-04-13 06:02:57 +00:00
|
|
|
" Use constants for truthy check to improve readability
|
|
|
|
let s:true = 1
|
|
|
|
let s:false = 0
|
|
|
|
|
2013-07-02 09:24:04 +00:00
|
|
|
" Only load plugin once
|
|
|
|
if exists("g:loaded_wakatime")
|
|
|
|
finish
|
|
|
|
endif
|
2017-04-13 06:02:57 +00:00
|
|
|
let g:loaded_wakatime = s:true
|
2013-06-25 07:57:15 +00:00
|
|
|
|
2013-07-02 09:24:04 +00:00
|
|
|
" Backup & Override cpoptions
|
|
|
|
let s:old_cpo = &cpo
|
|
|
|
set cpo&vim
|
2013-06-25 07:57:15 +00:00
|
|
|
|
2015-12-25 08:34:44 +00:00
|
|
|
" Script Globals
|
2015-05-21 03:15:23 +00:00
|
|
|
let s:cli_location = expand("<sfile>:p:h") . '/packages/wakatime/cli.py'
|
2015-05-01 23:08:02 +00:00
|
|
|
let s:config_file = expand("$HOME/.wakatime.cfg")
|
2015-12-25 08:34:44 +00:00
|
|
|
let s:data_file = expand("$HOME/.wakatime.data")
|
2017-04-13 06:02:57 +00:00
|
|
|
let s:config_file_already_setup = s:false
|
2017-04-13 07:13:48 +00:00
|
|
|
let s:debug_mode_already_setup = s:false
|
|
|
|
let s:is_debug_mode_on = s:false
|
2017-04-13 06:02:57 +00:00
|
|
|
let s:local_cache_expire = 10 " seconds between reading s:data_file
|
2015-12-25 08:34:44 +00:00
|
|
|
let s:last_heartbeat = [0, 0, '']
|
2015-05-01 23:08:02 +00:00
|
|
|
|
|
|
|
" For backwards compatibility, rename wakatime.conf to wakatime.cfg
|
|
|
|
if !filereadable(s:config_file)
|
|
|
|
if filereadable(expand("$HOME/.wakatime"))
|
|
|
|
exec "silent !mv" expand("$HOME/.wakatime") expand("$HOME/.wakatime.conf")
|
|
|
|
endif
|
|
|
|
if filereadable(expand("$HOME/.wakatime.conf"))
|
|
|
|
if !filereadable(s:config_file)
|
|
|
|
let contents = ['[settings]'] + readfile(expand("$HOME/.wakatime.conf"), '')
|
|
|
|
call writefile(contents, s:config_file)
|
|
|
|
call delete(expand("$HOME/.wakatime.conf"))
|
|
|
|
endif
|
2013-12-13 15:03:09 +00:00
|
|
|
endif
|
|
|
|
endif
|
2013-06-26 05:02:59 +00:00
|
|
|
|
2015-02-13 03:19:40 +00:00
|
|
|
" Set default python binary location
|
|
|
|
if !exists("g:wakatime_PythonBinary")
|
|
|
|
let g:wakatime_PythonBinary = 'python'
|
|
|
|
endif
|
|
|
|
|
|
|
|
" Set default heartbeat frequency in minutes
|
|
|
|
if !exists("g:wakatime_HeartbeatFrequency")
|
|
|
|
let g:wakatime_HeartbeatFrequency = 2
|
2013-08-12 10:30:17 +00:00
|
|
|
endif
|
|
|
|
|
2013-06-25 07:57:15 +00:00
|
|
|
" }}}
|
|
|
|
|
2013-06-26 04:09:52 +00:00
|
|
|
|
2013-06-25 07:57:15 +00:00
|
|
|
" Function Definitions {{{
|
|
|
|
|
2015-05-01 23:08:02 +00:00
|
|
|
function! s:StripWhitespace(str)
|
|
|
|
return substitute(a:str, '^\s*\(.\{-}\)\s*$', '\1', '')
|
|
|
|
endfunction
|
2017-02-14 07:39:06 +00:00
|
|
|
|
2015-01-20 04:36:23 +00:00
|
|
|
function! s:SetupConfigFile()
|
2015-05-01 23:08:02 +00:00
|
|
|
if !s:config_file_already_setup
|
|
|
|
|
2015-01-20 04:36:23 +00:00
|
|
|
" Create config file if does not exist
|
2015-05-01 23:08:02 +00:00
|
|
|
if !filereadable(s:config_file)
|
2015-01-20 04:36:23 +00:00
|
|
|
let key = input("[WakaTime] Enter your wakatime.com api key: ")
|
|
|
|
if key != ''
|
2015-05-06 16:18:54 +00:00
|
|
|
call writefile(['[settings]', 'debug = false', printf("api_key = %s", key), 'hidefilenames = false', 'ignore =', ' COMMIT_EDITMSG$', ' PULLREQ_EDITMSG$', ' MERGE_MSG$', ' TAG_EDITMSG$'], s:config_file)
|
2015-05-04 05:10:32 +00:00
|
|
|
echo "[WakaTime] Setup complete! Visit http://wakatime.com to view your logged time."
|
2015-05-01 23:08:02 +00:00
|
|
|
endif
|
2015-05-03 16:52:49 +00:00
|
|
|
|
|
|
|
" Make sure config file has api_key
|
|
|
|
else
|
2017-04-13 06:02:57 +00:00
|
|
|
let found_api_key = s:false
|
2017-04-13 07:13:48 +00:00
|
|
|
if s:GetIniSetting('settings', 'api_key') != '' || s:GetIniSetting('settings', 'apikey') != ''
|
|
|
|
let found_api_key = s:true
|
|
|
|
endif
|
2015-05-03 16:52:49 +00:00
|
|
|
if !found_api_key
|
|
|
|
let key = input("[WakaTime] Enter your wakatime.com api key: ")
|
|
|
|
let lines = lines + [join(['api_key', key], '=')]
|
|
|
|
call writefile(lines, s:config_file)
|
2015-05-04 05:10:32 +00:00
|
|
|
echo "[WakaTime] Setup complete! Visit http://wakatime.com to view your logged time."
|
2015-05-03 16:52:49 +00:00
|
|
|
endif
|
2015-01-20 04:36:23 +00:00
|
|
|
endif
|
2015-05-01 23:08:02 +00:00
|
|
|
|
2017-04-13 06:02:57 +00:00
|
|
|
let s:config_file_already_setup = s:true
|
2015-01-20 04:36:23 +00:00
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
2017-04-13 07:13:48 +00:00
|
|
|
function! s:SetupDebugMode()
|
|
|
|
if !s:debug_mode_already_setup
|
|
|
|
if s:GetIniSetting('settings', 'debug') == 'true'
|
|
|
|
let s:is_debug_mode_on = s:true
|
|
|
|
else
|
|
|
|
let s:is_debug_mode_on = s:false
|
|
|
|
endif
|
|
|
|
let s:debug_mode_already_setup = s:true
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:GetIniSetting(section, key)
|
|
|
|
if !filereadable(s:config_file)
|
|
|
|
return ''
|
|
|
|
endif
|
|
|
|
let lines = readfile(s:config_file)
|
|
|
|
let currentSection = ''
|
|
|
|
for line in lines
|
|
|
|
let line = s:StripWhitespace(line)
|
|
|
|
if matchstr(line, '^\[') != '' && matchstr(line, '\]$') != ''
|
|
|
|
let currentSection = substitute(line, '^\[\(.\{-}\)\]$', '\1', '')
|
|
|
|
else
|
|
|
|
if currentSection == a:section
|
|
|
|
let group = split(line, '=')
|
|
|
|
if len(group) == 2 && s:StripWhitespace(group[0]) == a:key
|
|
|
|
return s:StripWhitespace(group[1])
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
return ''
|
|
|
|
endfunction
|
|
|
|
|
2013-07-02 09:24:04 +00:00
|
|
|
function! s:GetCurrentFile()
|
|
|
|
return expand("%:p")
|
|
|
|
endfunction
|
|
|
|
|
2015-06-11 06:38:54 +00:00
|
|
|
function! s:EscapeArg(arg)
|
|
|
|
return substitute(shellescape(a:arg), '!', '\\!', '')
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:JoinArgs(args)
|
|
|
|
let safeArgs = []
|
|
|
|
for arg in a:args
|
|
|
|
let safeArgs = safeArgs + [s:EscapeArg(arg)]
|
|
|
|
endfor
|
|
|
|
return join(safeArgs, ' ')
|
|
|
|
endfunction
|
|
|
|
|
2017-04-13 07:19:37 +00:00
|
|
|
function! s:IsWindows()
|
|
|
|
if has('win32') || has('win64')
|
|
|
|
return s:true
|
|
|
|
endif
|
|
|
|
return s:false
|
|
|
|
endfunction
|
|
|
|
|
2015-12-29 17:43:16 +00:00
|
|
|
function! s:SendHeartbeat(file, time, is_write, last)
|
|
|
|
let file = a:file
|
|
|
|
if file == ''
|
|
|
|
let file = a:last[2]
|
2013-07-08 04:25:06 +00:00
|
|
|
endif
|
2015-12-29 17:43:16 +00:00
|
|
|
if file != ''
|
2015-02-13 03:19:40 +00:00
|
|
|
let python_bin = g:wakatime_PythonBinary
|
2017-04-13 07:19:37 +00:00
|
|
|
if s:IsWindows()
|
2015-02-13 03:19:40 +00:00
|
|
|
if python_bin == 'python'
|
|
|
|
let python_bin = 'pythonw'
|
|
|
|
endif
|
2014-05-27 05:39:37 +00:00
|
|
|
endif
|
2015-06-11 06:38:54 +00:00
|
|
|
let cmd = [python_bin, '-W', 'ignore', s:cli_location]
|
2016-04-28 22:16:26 +00:00
|
|
|
let cmd = cmd + ['--entity', file]
|
2015-06-11 06:38:54 +00:00
|
|
|
let cmd = cmd + ['--plugin', printf('vim/%d vim-wakatime/%s', v:version, s:VERSION)]
|
2013-07-08 04:25:06 +00:00
|
|
|
if a:is_write
|
2013-07-09 16:55:22 +00:00
|
|
|
let cmd = cmd + ['--write']
|
2013-07-08 04:25:06 +00:00
|
|
|
endif
|
2016-04-20 08:10:44 +00:00
|
|
|
if !empty(&syntax)
|
2017-02-21 00:23:13 +00:00
|
|
|
let cmd = cmd + ['--language', &syntax]
|
2016-04-19 10:52:30 +00:00
|
|
|
else
|
2016-04-20 08:10:44 +00:00
|
|
|
if !empty(&filetype)
|
2017-02-21 00:23:13 +00:00
|
|
|
let cmd = cmd + ['--language', &filetype]
|
2016-04-19 10:52:30 +00:00
|
|
|
endif
|
|
|
|
endif
|
2017-04-13 07:13:48 +00:00
|
|
|
if s:is_debug_mode_on
|
|
|
|
echo 'Sending Heartbeat with Command: ' . s:JoinArgs(cmd)
|
|
|
|
endif
|
2017-04-13 07:19:37 +00:00
|
|
|
if s:IsWindows()
|
2017-04-14 05:37:10 +00:00
|
|
|
if s:is_debug_mode_on
|
|
|
|
let stdout = system('(' . s:JoinArgs(cmd) . ')')
|
|
|
|
if stdout != ''
|
|
|
|
echo stdout
|
|
|
|
endif
|
|
|
|
else
|
|
|
|
exec 'silent !start /min cmd /c "' . s:JoinArgs(cmd) . '"'
|
|
|
|
endif
|
2014-05-27 05:39:37 +00:00
|
|
|
else
|
2016-09-17 15:08:58 +00:00
|
|
|
let stdout = system(s:JoinArgs(cmd) . ' &')
|
2017-04-14 05:37:10 +00:00
|
|
|
if s:is_debug_mode_on && stdout != ''
|
|
|
|
echo stdout
|
|
|
|
endif
|
2014-05-27 05:39:37 +00:00
|
|
|
endif
|
2015-12-29 17:43:16 +00:00
|
|
|
call s:SetLastHeartbeat(a:time, a:time, file)
|
2013-07-08 04:25:06 +00:00
|
|
|
endif
|
|
|
|
endfunction
|
2017-02-14 07:39:06 +00:00
|
|
|
|
2015-05-01 23:08:02 +00:00
|
|
|
function! s:GetLastHeartbeat()
|
2015-12-25 08:34:44 +00:00
|
|
|
if !s:last_heartbeat[0] || localtime() - s:last_heartbeat[0] > s:local_cache_expire
|
|
|
|
if !filereadable(s:data_file)
|
|
|
|
return [0, 0, '']
|
|
|
|
endif
|
|
|
|
let last = readfile(s:data_file, '', 3)
|
|
|
|
if len(last) == 3
|
|
|
|
let s:last_heartbeat = [s:last_heartbeat[0], last[1], last[2]]
|
|
|
|
endif
|
2013-07-02 09:24:04 +00:00
|
|
|
endif
|
2015-12-25 08:34:44 +00:00
|
|
|
return s:last_heartbeat
|
|
|
|
endfunction
|
2017-02-14 07:39:06 +00:00
|
|
|
|
2015-12-29 17:43:16 +00:00
|
|
|
function! s:SetLastHeartbeatLocally(time, last_update, file)
|
|
|
|
let s:last_heartbeat = [a:time, a:last_update, a:file]
|
2013-07-08 04:25:06 +00:00
|
|
|
endfunction
|
2017-02-14 07:39:06 +00:00
|
|
|
|
2015-12-29 17:43:16 +00:00
|
|
|
function! s:SetLastHeartbeat(time, last_update, file)
|
|
|
|
call s:SetLastHeartbeatLocally(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)
|
2013-07-02 09:24:04 +00:00
|
|
|
endfunction
|
|
|
|
|
2013-07-10 04:13:45 +00:00
|
|
|
function! s:EnoughTimePassed(now, last)
|
2015-12-25 08:34:44 +00:00
|
|
|
let prev = a:last[1]
|
2015-02-13 03:19:40 +00:00
|
|
|
if a:now - prev > g:wakatime_HeartbeatFrequency * 60
|
2017-04-13 06:02:57 +00:00
|
|
|
return s:true
|
2013-07-08 04:25:06 +00:00
|
|
|
endif
|
2017-04-13 06:02:57 +00:00
|
|
|
return s:false
|
2013-07-02 09:24:04 +00:00
|
|
|
endfunction
|
2017-02-14 07:39:06 +00:00
|
|
|
|
2013-06-25 07:57:15 +00:00
|
|
|
" }}}
|
|
|
|
|
2013-06-26 04:09:52 +00:00
|
|
|
|
2013-06-25 07:57:15 +00:00
|
|
|
" Event Handlers {{{
|
|
|
|
|
2015-05-01 23:08:02 +00:00
|
|
|
function! s:handleActivity(is_write)
|
2017-04-13 07:13:48 +00:00
|
|
|
call s:SetupDebugMode()
|
2015-01-20 04:36:23 +00:00
|
|
|
call s:SetupConfigFile()
|
2015-12-29 17:43:16 +00:00
|
|
|
let file = s:GetCurrentFile()
|
2013-12-16 10:07:28 +00:00
|
|
|
let now = localtime()
|
2015-05-01 23:08:02 +00:00
|
|
|
let last = s:GetLastHeartbeat()
|
2015-12-29 18:03:51 +00:00
|
|
|
if !empty(file) && file !~ "-MiniBufExplorer-" && file !~ "--NO NAME--" && file !~ "^term:"
|
2015-12-29 17:43:16 +00:00
|
|
|
if a:is_write || s:EnoughTimePassed(now, last) || file != last[2]
|
|
|
|
call s:SendHeartbeat(file, now, a:is_write, last)
|
2015-12-25 08:34:44 +00:00
|
|
|
else
|
|
|
|
if now - s:last_heartbeat[0] > s:local_cache_expire
|
|
|
|
call s:SetLastHeartbeatLocally(now, last[1], last[2])
|
|
|
|
endif
|
2015-05-06 16:49:33 +00:00
|
|
|
endif
|
2013-07-02 09:24:04 +00:00
|
|
|
endif
|
2013-07-08 04:25:06 +00:00
|
|
|
endfunction
|
2013-07-02 09:24:04 +00:00
|
|
|
|
2013-06-25 07:57:15 +00:00
|
|
|
" }}}
|
|
|
|
|
2013-06-26 04:09:52 +00:00
|
|
|
|
2013-06-25 07:57:15 +00:00
|
|
|
" Autocommand Events {{{
|
|
|
|
|
2013-07-02 09:24:04 +00:00
|
|
|
augroup Wakatime
|
|
|
|
autocmd!
|
2017-04-13 06:02:57 +00:00
|
|
|
autocmd BufEnter * call s:handleActivity(s:false)
|
|
|
|
autocmd VimEnter * call s:handleActivity(s:false)
|
|
|
|
autocmd BufWritePost * call s:handleActivity(s:true)
|
|
|
|
autocmd CursorMoved,CursorMovedI * call s:handleActivity(s:false)
|
2013-07-02 09:24:04 +00:00
|
|
|
augroup END
|
2013-06-25 07:57:15 +00:00
|
|
|
|
|
|
|
" }}}
|
|
|
|
|
2013-06-26 04:09:52 +00:00
|
|
|
|
2013-06-25 07:57:15 +00:00
|
|
|
" Restore cpoptions
|
|
|
|
let &cpo = s:old_cpo
|