forked from luna/vim-rana-local
version 0.2.1. updated to new action api scheme.
This commit is contained in:
parent
61370df1ba
commit
0b07cb21bd
16 changed files with 3125 additions and 308 deletions
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Packages
|
||||||
|
*.egg
|
||||||
|
*.egg-info
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
eggs
|
||||||
|
parts
|
||||||
|
bin
|
||||||
|
var
|
||||||
|
sdist
|
||||||
|
develop-eggs
|
||||||
|
.installed.cfg
|
||||||
|
lib
|
||||||
|
lib64
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
.coverage
|
||||||
|
.tox
|
||||||
|
nosetests.xml
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
|
||||||
|
# Mr Developer
|
||||||
|
.mr.developer.cfg
|
||||||
|
.project
|
||||||
|
.pydevproject
|
|
@ -1,8 +1,9 @@
|
||||||
vim-wakatime 0.1.2
|
vim-wakatime 0.2.1
|
||||||
===========
|
==================
|
||||||
|
|
||||||
Automatic time tracking for Vim.
|
Automatic time tracking for Vim.
|
||||||
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -12,7 +13,7 @@ https://wakati.me
|
||||||
|
|
||||||
2) Run this shell command replacing KEY with your api key:
|
2) Run this shell command replacing KEY with your api key:
|
||||||
|
|
||||||
echo "api_key=KEY" >> ~/.wakatime
|
echo "api_key=KEY" >> ~/.wakatime.conf
|
||||||
|
|
||||||
3) Using [Vundle](https://github.com/gmarik/vundle), the Vim plugin manager:
|
3) Using [Vundle](https://github.com/gmarik/vundle), the Vim plugin manager:
|
||||||
|
|
||||||
|
@ -26,6 +27,7 @@ Then inside Vim, type `:BundleInstall` or run this shell command:
|
||||||
|
|
||||||
Visit https://wakati.me to view your time spent in each file.
|
Visit https://wakati.me to view your time spent in each file.
|
||||||
|
|
||||||
|
|
||||||
Screen Shots
|
Screen Shots
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
0
plugin/packages/wakatime/__init__.py
Normal file
0
plugin/packages/wakatime/__init__.py
Normal file
85
plugin/packages/wakatime/log.py
Normal file
85
plugin/packages/wakatime/log.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
wakatime.log
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Provides the configured logger for writing JSON to the log file.
|
||||||
|
|
||||||
|
:copyright: (c) 2013 Alan Hamlett.
|
||||||
|
:license: BSD, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
try:
|
||||||
|
from collections import OrderedDict
|
||||||
|
except ImportError:
|
||||||
|
from .packages.ordereddict import OrderedDict
|
||||||
|
|
||||||
|
|
||||||
|
class CustomEncoder(json.JSONEncoder):
|
||||||
|
|
||||||
|
def default(self, obj):
|
||||||
|
return super(CustomEncoder, self).default(obj)
|
||||||
|
|
||||||
|
|
||||||
|
class JsonFormatter(logging.Formatter):
|
||||||
|
|
||||||
|
def __init__(self, timestamp, endtime, isWrite, targetFile, version,
|
||||||
|
plugin, datefmt=None):
|
||||||
|
self.timestamp = timestamp
|
||||||
|
self.endtime = endtime
|
||||||
|
self.isWrite = isWrite
|
||||||
|
self.targetFile = targetFile
|
||||||
|
self.version = version
|
||||||
|
self.plugin = plugin
|
||||||
|
super(JsonFormatter, self).__init__(datefmt=datefmt)
|
||||||
|
|
||||||
|
def format(self, record):
|
||||||
|
data = OrderedDict([
|
||||||
|
('now', self.formatTime(record, self.datefmt)),
|
||||||
|
('version', self.version),
|
||||||
|
('plugin', self.plugin),
|
||||||
|
('time', self.timestamp),
|
||||||
|
('endtime', self.endtime),
|
||||||
|
('isWrite', self.isWrite),
|
||||||
|
('file', self.targetFile),
|
||||||
|
('level', record.levelname),
|
||||||
|
('message', record.msg),
|
||||||
|
])
|
||||||
|
if not self.endtime:
|
||||||
|
del data['endtime']
|
||||||
|
if not self.plugin:
|
||||||
|
del data['plugin']
|
||||||
|
if not self.isWrite:
|
||||||
|
del data['isWrite']
|
||||||
|
return CustomEncoder().encode(data)
|
||||||
|
|
||||||
|
def formatException(self, exc_info):
|
||||||
|
return exec_info[2].format_exc()
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logging(args, version):
|
||||||
|
logfile = args.logfile
|
||||||
|
if not logfile:
|
||||||
|
logfile = '~/.wakatime.log'
|
||||||
|
handler = logging.FileHandler(os.path.expanduser(logfile))
|
||||||
|
formatter = JsonFormatter(
|
||||||
|
timestamp=args.timestamp,
|
||||||
|
endtime=args.endtime,
|
||||||
|
isWrite=args.isWrite,
|
||||||
|
targetFile=args.targetFile,
|
||||||
|
version=version,
|
||||||
|
plugin=args.plugin,
|
||||||
|
datefmt='%Y-%m-%dT%H:%M:%SZ',
|
||||||
|
)
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.addHandler(handler)
|
||||||
|
level = logging.INFO
|
||||||
|
if args.verbose:
|
||||||
|
level = logging.DEBUG
|
||||||
|
logger.setLevel(level)
|
||||||
|
return logger
|
0
plugin/packages/wakatime/packages/__init__.py
Normal file
0
plugin/packages/wakatime/packages/__init__.py
Normal file
2362
plugin/packages/wakatime/packages/argparse.py
Normal file
2362
plugin/packages/wakatime/packages/argparse.py
Normal file
File diff suppressed because it is too large
Load diff
127
plugin/packages/wakatime/packages/ordereddict.py
Normal file
127
plugin/packages/wakatime/packages/ordereddict.py
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
# Copyright (c) 2009 Raymond Hettinger
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person
|
||||||
|
# obtaining a copy of this software and associated documentation files
|
||||||
|
# (the "Software"), to deal in the Software without restriction,
|
||||||
|
# including without limitation the rights to use, copy, modify, merge,
|
||||||
|
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
# and to permit persons to whom the Software is furnished to do so,
|
||||||
|
# subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
# OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
from UserDict import DictMixin
|
||||||
|
|
||||||
|
class OrderedDict(dict, DictMixin):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwds):
|
||||||
|
if len(args) > 1:
|
||||||
|
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||||
|
try:
|
||||||
|
self.__end
|
||||||
|
except AttributeError:
|
||||||
|
self.clear()
|
||||||
|
self.update(*args, **kwds)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.__end = end = []
|
||||||
|
end += [None, end, end] # sentinel node for doubly linked list
|
||||||
|
self.__map = {} # key --> [key, prev, next]
|
||||||
|
dict.clear(self)
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
if key not in self:
|
||||||
|
end = self.__end
|
||||||
|
curr = end[1]
|
||||||
|
curr[2] = end[1] = self.__map[key] = [key, curr, end]
|
||||||
|
dict.__setitem__(self, key, value)
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
dict.__delitem__(self, key)
|
||||||
|
key, prev, next = self.__map.pop(key)
|
||||||
|
prev[2] = next
|
||||||
|
next[1] = prev
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
end = self.__end
|
||||||
|
curr = end[2]
|
||||||
|
while curr is not end:
|
||||||
|
yield curr[0]
|
||||||
|
curr = curr[2]
|
||||||
|
|
||||||
|
def __reversed__(self):
|
||||||
|
end = self.__end
|
||||||
|
curr = end[1]
|
||||||
|
while curr is not end:
|
||||||
|
yield curr[0]
|
||||||
|
curr = curr[1]
|
||||||
|
|
||||||
|
def popitem(self, last=True):
|
||||||
|
if not self:
|
||||||
|
raise KeyError('dictionary is empty')
|
||||||
|
if last:
|
||||||
|
key = reversed(self).next()
|
||||||
|
else:
|
||||||
|
key = iter(self).next()
|
||||||
|
value = self.pop(key)
|
||||||
|
return key, value
|
||||||
|
|
||||||
|
def __reduce__(self):
|
||||||
|
items = [[k, self[k]] for k in self]
|
||||||
|
tmp = self.__map, self.__end
|
||||||
|
del self.__map, self.__end
|
||||||
|
inst_dict = vars(self).copy()
|
||||||
|
self.__map, self.__end = tmp
|
||||||
|
if inst_dict:
|
||||||
|
return (self.__class__, (items,), inst_dict)
|
||||||
|
return self.__class__, (items,)
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
return list(self)
|
||||||
|
|
||||||
|
setdefault = DictMixin.setdefault
|
||||||
|
update = DictMixin.update
|
||||||
|
pop = DictMixin.pop
|
||||||
|
values = DictMixin.values
|
||||||
|
items = DictMixin.items
|
||||||
|
iterkeys = DictMixin.iterkeys
|
||||||
|
itervalues = DictMixin.itervalues
|
||||||
|
iteritems = DictMixin.iteritems
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
if not self:
|
||||||
|
return '%s()' % (self.__class__.__name__,)
|
||||||
|
return '%s(%r)' % (self.__class__.__name__, self.items())
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return self.__class__(self)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fromkeys(cls, iterable, value=None):
|
||||||
|
d = cls()
|
||||||
|
for key in iterable:
|
||||||
|
d[key] = value
|
||||||
|
return d
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(other, OrderedDict):
|
||||||
|
if len(self) != len(other):
|
||||||
|
return False
|
||||||
|
for p, q in zip(self.items(), other.items()):
|
||||||
|
if p != q:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
return dict.__eq__(self, other)
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self == other
|
35
plugin/packages/wakatime/project.py
Normal file
35
plugin/packages/wakatime/project.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
wakatime.project
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Returns a project for the given file.
|
||||||
|
|
||||||
|
:copyright: (c) 2013 Alan Hamlett.
|
||||||
|
:license: BSD, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from .projects.base import BaseProject
|
||||||
|
from .projects.git import Git
|
||||||
|
from .projects.mercurial import Mercurial
|
||||||
|
from .projects.subversion import Subversion
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
PLUGINS = [
|
||||||
|
Git,
|
||||||
|
Mercurial,
|
||||||
|
Subversion,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def find_project(path):
|
||||||
|
for plugin in PLUGINS:
|
||||||
|
project = plugin(path)
|
||||||
|
if project.config:
|
||||||
|
return project
|
||||||
|
return BaseProject(path)
|
0
plugin/packages/wakatime/projects/__init__.py
Normal file
0
plugin/packages/wakatime/projects/__init__.py
Normal file
47
plugin/packages/wakatime/projects/base.py
Normal file
47
plugin/packages/wakatime/projects/base.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
wakatime.projects.base
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Base project for use when no other project can be found.
|
||||||
|
|
||||||
|
:copyright: (c) 2013 Alan Hamlett.
|
||||||
|
:license: BSD, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseProject():
|
||||||
|
|
||||||
|
def __init__(self, path):
|
||||||
|
self.path = path
|
||||||
|
self.config = self.findConfig(path)
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
base = self.base()
|
||||||
|
if base:
|
||||||
|
return os.path.basename(base)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def type(self):
|
||||||
|
type = self.__class__.__name__.lower()
|
||||||
|
if type == 'baseproject':
|
||||||
|
type = None
|
||||||
|
return type
|
||||||
|
|
||||||
|
def base(self):
|
||||||
|
if self.config:
|
||||||
|
return os.path.dirname(self.config)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def tags(self):
|
||||||
|
tags = []
|
||||||
|
return tags
|
||||||
|
|
||||||
|
def findConfig(self, path):
|
||||||
|
return ''
|
81
plugin/packages/wakatime/projects/git.py
Normal file
81
plugin/packages/wakatime/projects/git.py
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
wakatime.projects.git
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Information about the git project for a given file.
|
||||||
|
|
||||||
|
:copyright: (c) 2013 Alan Hamlett.
|
||||||
|
:license: BSD, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from .base import BaseProject
|
||||||
|
|
||||||
|
try:
|
||||||
|
from collections import OrderedDict
|
||||||
|
except ImportError:
|
||||||
|
from ..packages.ordereddict import OrderedDict
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Git(BaseProject):
|
||||||
|
|
||||||
|
def base(self):
|
||||||
|
if self.config:
|
||||||
|
return os.path.dirname(os.path.dirname(self.config))
|
||||||
|
return None
|
||||||
|
|
||||||
|
def tags(self):
|
||||||
|
tags = []
|
||||||
|
if self.config:
|
||||||
|
sections = self.parseConfig()
|
||||||
|
for section in sections:
|
||||||
|
if section.split(' ', 1)[0] == 'remote' and 'url' in sections[section]:
|
||||||
|
tags.append(sections[section]['url'])
|
||||||
|
return tags
|
||||||
|
|
||||||
|
def findConfig(self, path):
|
||||||
|
path = os.path.realpath(path)
|
||||||
|
if os.path.isfile(path):
|
||||||
|
path = os.path.split(path)[0]
|
||||||
|
if os.path.isfile(os.path.join(path, '.git', 'config')):
|
||||||
|
return os.path.join(path, '.git', 'config')
|
||||||
|
split_path = os.path.split(path)
|
||||||
|
if split_path[1] == '':
|
||||||
|
return None
|
||||||
|
return self.findConfig(split_path[0])
|
||||||
|
|
||||||
|
def parseConfig(self):
|
||||||
|
sections = {}
|
||||||
|
try:
|
||||||
|
f = open(self.config, 'r')
|
||||||
|
except IOError as e:
|
||||||
|
log.exception("Exception:")
|
||||||
|
else:
|
||||||
|
with f:
|
||||||
|
section = None
|
||||||
|
for line in f.readlines():
|
||||||
|
line = line.lstrip()
|
||||||
|
if len(line) > 0 and line[0] == '[':
|
||||||
|
section = line[1:].split(']', 1)[0]
|
||||||
|
temp = section.split(' ', 1)
|
||||||
|
section = temp[0].lower()
|
||||||
|
if len(temp) > 1:
|
||||||
|
section = ' '.join([section, temp[1]])
|
||||||
|
sections[section] = {}
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
(setting, value) = line.split('=', 1)
|
||||||
|
except ValueError:
|
||||||
|
setting = line.split('#', 1)[0].split(';', 1)[0]
|
||||||
|
value = 'true'
|
||||||
|
setting = setting.strip().lower()
|
||||||
|
value = value.split('#', 1)[0].split(';', 1)[0].strip()
|
||||||
|
sections[section][setting] = value
|
||||||
|
f.close()
|
||||||
|
return sections
|
28
plugin/packages/wakatime/projects/mercurial.py
Normal file
28
plugin/packages/wakatime/projects/mercurial.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
wakatime.projects.mercurial
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Information about the mercurial project for a given file.
|
||||||
|
|
||||||
|
:copyright: (c) 2013 Alan Hamlett.
|
||||||
|
:license: BSD, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from .base import BaseProject
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Mercurial(BaseProject):
|
||||||
|
|
||||||
|
def base(self):
|
||||||
|
return super(Mercurial, self).base()
|
||||||
|
|
||||||
|
def tags(self):
|
||||||
|
tags = []
|
||||||
|
return tags
|
28
plugin/packages/wakatime/projects/subversion.py
Normal file
28
plugin/packages/wakatime/projects/subversion.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
wakatime.projects.subversion
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Information about the svn project for a given file.
|
||||||
|
|
||||||
|
:copyright: (c) 2013 Alan Hamlett.
|
||||||
|
:license: BSD, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from .base import BaseProject
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Subversion(BaseProject):
|
||||||
|
|
||||||
|
def base(self):
|
||||||
|
return super(Subversion, self).base()
|
||||||
|
|
||||||
|
def tags(self):
|
||||||
|
tags = []
|
||||||
|
return tags
|
200
plugin/packages/wakatime/wakatime.py
Normal file
200
plugin/packages/wakatime/wakatime.py
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
wakatime
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
Action event appender for Wakati.Me, a time tracking api for text editors.
|
||||||
|
|
||||||
|
:copyright: (c) 2013 Alan Hamlett.
|
||||||
|
:license: BSD, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
__title__ = 'wakatime'
|
||||||
|
__version__ = '0.1.1'
|
||||||
|
__author__ = 'Alan Hamlett'
|
||||||
|
__license__ = 'BSD'
|
||||||
|
__copyright__ = 'Copyright 2013 Alan Hamlett'
|
||||||
|
|
||||||
|
|
||||||
|
# allow running script directly
|
||||||
|
if __name__ == '__main__' and __package__ is None:
|
||||||
|
import os, sys
|
||||||
|
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
sys.path.insert(0, parent_dir)
|
||||||
|
import wakatime
|
||||||
|
__package__ = 'wakatime'
|
||||||
|
del os, sys
|
||||||
|
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
import urllib2
|
||||||
|
|
||||||
|
from .log import setup_logging
|
||||||
|
from .project import find_project
|
||||||
|
|
||||||
|
try:
|
||||||
|
import argparse
|
||||||
|
except ImportError:
|
||||||
|
from .packages import argparse
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class FileAction(argparse.Action):
|
||||||
|
|
||||||
|
def __call__(self, parser, namespace, values, option_string=None):
|
||||||
|
values = os.path.realpath(values)
|
||||||
|
setattr(namespace, self.dest, values)
|
||||||
|
|
||||||
|
|
||||||
|
def parseArguments():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Wakati.Me event api appender')
|
||||||
|
parser.add_argument('--file', dest='targetFile', metavar='file',
|
||||||
|
action=FileAction, required=True,
|
||||||
|
help='absolute path to file for current action')
|
||||||
|
parser.add_argument('--time', dest='timestamp', metavar='time',
|
||||||
|
type=float,
|
||||||
|
help='optional floating-point unix epoch timestamp; '+
|
||||||
|
'uses current time by default')
|
||||||
|
parser.add_argument('--endtime', dest='endtime',
|
||||||
|
help='optional end timestamp turning this action into '+
|
||||||
|
'a duration; if a non-duration action occurs within a '+
|
||||||
|
'duration, the duration is ignored')
|
||||||
|
parser.add_argument('--write', dest='isWrite',
|
||||||
|
action='store_true',
|
||||||
|
help='note action was triggered from writing to a file')
|
||||||
|
parser.add_argument('--plugin', dest='plugin',
|
||||||
|
help='optional text editor plugin name and version '+
|
||||||
|
'for User-Agent header')
|
||||||
|
parser.add_argument('--key', dest='key',
|
||||||
|
help='your wakati.me api key; uses api_key from '+
|
||||||
|
'~/.wakatime.conf by default')
|
||||||
|
parser.add_argument('--logfile', dest='logfile',
|
||||||
|
help='defaults to ~/.wakatime.log')
|
||||||
|
parser.add_argument('--config', dest='config',
|
||||||
|
help='defaults to ~/.wakatime.conf')
|
||||||
|
parser.add_argument('--verbose', dest='verbose', action='store_true',
|
||||||
|
help='turns on debug messages in log file')
|
||||||
|
parser.add_argument('--version', action='version', version=__version__)
|
||||||
|
args = parser.parse_args()
|
||||||
|
if not args.timestamp:
|
||||||
|
args.timestamp = time.time()
|
||||||
|
if not args.key:
|
||||||
|
default_key = get_api_key(args.config)
|
||||||
|
if default_key:
|
||||||
|
args.key = default_key
|
||||||
|
else:
|
||||||
|
parser.error('Missing api key')
|
||||||
|
if args.endtime and args.endtime < args.timestamp:
|
||||||
|
tmp = args.timestamp
|
||||||
|
args.timestamp = args.endtime
|
||||||
|
args.endtime = tmp
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def get_api_key(configFile):
|
||||||
|
if not configFile:
|
||||||
|
configFile = '~/.wakatime.conf'
|
||||||
|
api_key = None
|
||||||
|
try:
|
||||||
|
cf = open(os.path.expanduser(configFile))
|
||||||
|
for line in cf:
|
||||||
|
line = line.split('=', 1)
|
||||||
|
if line[0] == 'api_key':
|
||||||
|
api_key = line[1].strip()
|
||||||
|
cf.close()
|
||||||
|
except IOError:
|
||||||
|
print('Error: Could not read from config file.')
|
||||||
|
return api_key
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_agent(plugin):
|
||||||
|
user_agent = 'wakatime/%s (%s)' % (__version__, platform.platform())
|
||||||
|
if plugin:
|
||||||
|
user_agent = user_agent+' '+plugin
|
||||||
|
return user_agent
|
||||||
|
|
||||||
|
|
||||||
|
def send_action(project=None, tags=None, key=None, targetFile=None,
|
||||||
|
timestamp=None, endtime=None, isWrite=None, plugin=None, **kwargs):
|
||||||
|
url = 'https://www.wakati.me/api/v1/actions'
|
||||||
|
log.debug('Sending action to api at %s' % url)
|
||||||
|
data = {
|
||||||
|
'time': timestamp,
|
||||||
|
'file': targetFile,
|
||||||
|
}
|
||||||
|
if endtime:
|
||||||
|
data['endtime'] = endtime
|
||||||
|
if isWrite:
|
||||||
|
data['isWrite'] = isWrite
|
||||||
|
if project:
|
||||||
|
data['project'] = project
|
||||||
|
if tags:
|
||||||
|
data['tags'] = tags
|
||||||
|
log.debug(data)
|
||||||
|
request = urllib2.Request(url=url, data=json.dumps(data))
|
||||||
|
user_agent = get_user_agent(plugin)
|
||||||
|
request.add_header('User-Agent', user_agent)
|
||||||
|
request.add_header('Content-Type', 'application/json')
|
||||||
|
request.add_header('Authorization', 'Basic %s' % base64.b64encode(key))
|
||||||
|
response = None
|
||||||
|
try:
|
||||||
|
response = urllib2.urlopen(request)
|
||||||
|
except urllib2.HTTPError as exc:
|
||||||
|
data = {
|
||||||
|
'response_code': exc.getcode(),
|
||||||
|
'response_content': exc.read(),
|
||||||
|
sys.exc_info()[0].__name__: str(sys.exc_info()[1]),
|
||||||
|
}
|
||||||
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
|
data['traceback'] = traceback.format_exc()
|
||||||
|
log.error(data)
|
||||||
|
except:
|
||||||
|
data = {
|
||||||
|
sys.exc_info()[0].__name__: str(sys.exc_info()[1]),
|
||||||
|
}
|
||||||
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
|
data['traceback'] = traceback.format_exc()
|
||||||
|
log.error(data)
|
||||||
|
else:
|
||||||
|
if response.getcode() >= 200 and response.getcode() < 300:
|
||||||
|
log.debug({
|
||||||
|
'response_code': response.getcode(),
|
||||||
|
'response_content': response.read(),
|
||||||
|
})
|
||||||
|
return True
|
||||||
|
log.error({
|
||||||
|
'response_code': response.getcode(),
|
||||||
|
'response_content': response.read(),
|
||||||
|
})
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parseArguments()
|
||||||
|
setup_logging(args, __version__)
|
||||||
|
if os.path.isfile(args.targetFile):
|
||||||
|
project = find_project(args.targetFile)
|
||||||
|
tags = project.tags()
|
||||||
|
if send_action(project=project.name(), tags=tags, **vars(args)):
|
||||||
|
return 0
|
||||||
|
return 102
|
||||||
|
else:
|
||||||
|
log.debug('File does not exist; ignoring this action.')
|
||||||
|
return 101
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
|
@ -1,185 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import argparse
|
|
||||||
import platform
|
|
||||||
import urllib2
|
|
||||||
import json
|
|
||||||
import base64
|
|
||||||
import uuid
|
|
||||||
import time
|
|
||||||
import re
|
|
||||||
from collections import OrderedDict
|
|
||||||
from httplib import BadStatusLine, IncompleteRead
|
|
||||||
from urllib2 import HTTPError, URLError
|
|
||||||
import logging as log
|
|
||||||
|
|
||||||
|
|
||||||
# Config
|
|
||||||
version = '0.1.3'
|
|
||||||
user_agent = 'vim-wakatime/%s (%s)' % (version, platform.platform())
|
|
||||||
|
|
||||||
|
|
||||||
def project_from_path(path):
|
|
||||||
project = git_project(path)
|
|
||||||
if project:
|
|
||||||
return project
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def tags_from_path(path):
|
|
||||||
tags = []
|
|
||||||
if os.path.exists(path):
|
|
||||||
tags.extend(git_tags(path))
|
|
||||||
tags.extend(mercurial_tags(path))
|
|
||||||
return list(set(tags))
|
|
||||||
|
|
||||||
|
|
||||||
def git_project(path):
|
|
||||||
config_file = find_git_config(path)
|
|
||||||
if config_file:
|
|
||||||
folder = os.path.split(os.path.split(os.path.split(config_file)[0])[0])[1]
|
|
||||||
if folder:
|
|
||||||
return folder
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def find_git_config(path):
|
|
||||||
path = os.path.realpath(path)
|
|
||||||
if os.path.isfile(path):
|
|
||||||
path = os.path.split(path)[0]
|
|
||||||
if os.path.isfile(os.path.join(path, '.git', 'config')):
|
|
||||||
return os.path.join(path, '.git', 'config')
|
|
||||||
split_path = os.path.split(path)
|
|
||||||
if split_path[1] == '':
|
|
||||||
return None
|
|
||||||
return find_git_config(split_path[0])
|
|
||||||
|
|
||||||
|
|
||||||
def parse_git_config(config):
|
|
||||||
sections = OrderedDict()
|
|
||||||
try:
|
|
||||||
f = open(config, 'r')
|
|
||||||
except IOError as e:
|
|
||||||
log.exception("Exception:")
|
|
||||||
else:
|
|
||||||
with f:
|
|
||||||
section = None
|
|
||||||
for line in f.readlines():
|
|
||||||
line = line.lstrip()
|
|
||||||
if len(line) > 0 and line[0] == '[':
|
|
||||||
section = line[1:].split(']', 1)[0]
|
|
||||||
temp = section.split(' ', 1)
|
|
||||||
section = temp[0].lower()
|
|
||||||
if len(temp) > 1:
|
|
||||||
section = ' '.join([section, temp[1]])
|
|
||||||
sections[section] = OrderedDict()
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
(setting, value) = line.split('=', 1)
|
|
||||||
except ValueError:
|
|
||||||
setting = line.split('#', 1)[0].split(';', 1)[0]
|
|
||||||
value = 'true'
|
|
||||||
setting = setting.strip().lower()
|
|
||||||
value = value.split('#', 1)[0].split(';', 1)[0].strip()
|
|
||||||
sections[section][setting] = value
|
|
||||||
f.close()
|
|
||||||
return sections
|
|
||||||
|
|
||||||
|
|
||||||
def git_tags(path):
|
|
||||||
tags = []
|
|
||||||
config_file = find_git_config(path)
|
|
||||||
if config_file:
|
|
||||||
sections = parse_git_config(config_file)
|
|
||||||
for section in sections:
|
|
||||||
if section.split(' ', 1)[0] == 'remote' and 'url' in sections[section]:
|
|
||||||
tags.append(sections[section]['url'])
|
|
||||||
return tags
|
|
||||||
|
|
||||||
|
|
||||||
def mercurial_tags(path):
|
|
||||||
tags = []
|
|
||||||
return tags
|
|
||||||
|
|
||||||
|
|
||||||
def svn_tags(path):
|
|
||||||
tags = []
|
|
||||||
return tags
|
|
||||||
|
|
||||||
|
|
||||||
def log_action(**kwargs):
|
|
||||||
kwargs['User-Agent'] = user_agent
|
|
||||||
log.info(json.dumps(kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
def send_action(key, instance, action, task, timestamp, project, tags):
|
|
||||||
url = 'https://www.wakati.me/api/v1/actions'
|
|
||||||
data = {
|
|
||||||
'type': action,
|
|
||||||
'task': os.path.realpath(task),
|
|
||||||
'time': timestamp,
|
|
||||||
'instance_id': instance,
|
|
||||||
'project': project,
|
|
||||||
'tags': tags,
|
|
||||||
}
|
|
||||||
request = urllib2.Request(url=url, data=json.dumps(data))
|
|
||||||
request.add_header('User-Agent', user_agent)
|
|
||||||
request.add_header('Content-Type', 'application/json')
|
|
||||||
request.add_header('Authorization', 'Basic %s' % base64.b64encode(key))
|
|
||||||
log_action(**data)
|
|
||||||
response = None
|
|
||||||
try:
|
|
||||||
response = urllib2.urlopen(request)
|
|
||||||
except HTTPError as ex:
|
|
||||||
log.error("%s:\ndata=%s\nresponse=%s" % (ex.getcode(), json.dumps(data), ex.read()))
|
|
||||||
if log.getLogger().isEnabledFor(log.DEBUG):
|
|
||||||
log.exception("Exception for %s:\n%s" % (data['time'], json.dumps(data)))
|
|
||||||
except (URLError, IncompleteRead, BadStatusLine) as ex:
|
|
||||||
log.error("%s:\ndata=%s\nmessage=%s" % (ex.__class__.__name__, json.dumps(data), ex))
|
|
||||||
if log.getLogger().isEnabledFor(log.DEBUG):
|
|
||||||
log.exception("Exception for %s:\n%s" % (data['time'], json.dumps(data)))
|
|
||||||
if response:
|
|
||||||
log.debug('response_code=%s response_content=%s' % (response.getcode(), response.read()))
|
|
||||||
if response and (response.getcode() == 200 or response.getcode() == 201):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args(argv):
|
|
||||||
parser = argparse.ArgumentParser(description='Log time to the wakati.me api')
|
|
||||||
parser.add_argument('--key', dest='key', required=True,
|
|
||||||
help='your wakati.me api key')
|
|
||||||
parser.add_argument('--action', dest='action', required=True,
|
|
||||||
choices=['open_file', 'ping', 'close_file', 'write_file', 'open_editor', 'quit_editor', 'minimize_editor', 'maximize_editor', 'start', 'stop'])
|
|
||||||
parser.add_argument('--task', dest='task', required=True,
|
|
||||||
help='path to file or named task')
|
|
||||||
parser.add_argument('--instance', dest='instance', required=True,
|
|
||||||
help='the UUID4 representing the current editor')
|
|
||||||
parser.add_argument('--time', dest='timestamp', metavar='time', type=float,
|
|
||||||
help='optional floating-point timestamp in seconds')
|
|
||||||
parser.add_argument('--verbose', dest='verbose', action='store_true',
|
|
||||||
help='turns on debug messages in logfile')
|
|
||||||
parser.add_argument('--version', action='version', version=version)
|
|
||||||
return parser.parse_args(argv)
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv):
|
|
||||||
args = parse_args(argv)
|
|
||||||
level = log.INFO
|
|
||||||
if args.verbose:
|
|
||||||
level = log.DEBUG
|
|
||||||
del args.verbose
|
|
||||||
if not args.timestamp:
|
|
||||||
args.timestamp = time.time()
|
|
||||||
log.basicConfig(filename=os.path.expanduser('~/.wakatime.log'), format='%(asctime)s vim-wakatime/'+version+' %(levelname)s %(message)s', datefmt='%Y-%m-%dT%H:%M:%SZ', level=level)
|
|
||||||
if os.path.isfile(os.path.realpath(args.task)):
|
|
||||||
tags = tags_from_path(args.task)
|
|
||||||
project = project_from_path(args.task)
|
|
||||||
send_action(project=project, tags=tags, **vars(args))
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.exit(main(sys.argv[1:]))
|
|
|
@ -2,7 +2,7 @@
|
||||||
" File: wakatime.vim
|
" File: wakatime.vim
|
||||||
" Description: Automatic time tracking for Vim.
|
" Description: Automatic time tracking for Vim.
|
||||||
" Maintainer: Wakati.Me <support@wakatime.com>
|
" Maintainer: Wakati.Me <support@wakatime.com>
|
||||||
" Version: 0.1.3
|
" Version: 0.2.1
|
||||||
" ============================================================================
|
" ============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,21 +20,6 @@
|
||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" Check for required user-defined settings
|
|
||||||
if !exists("g:wakatime_api_key")
|
|
||||||
if filereadable(expand("$HOME/.wakatime"))
|
|
||||||
for s:line in readfile(expand("$HOME/.wakatime"))
|
|
||||||
let s:setting = split(s:line, "=")
|
|
||||||
if s:setting[0] == "api_key"
|
|
||||||
let g:wakatime_api_key = s:setting[1]
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
endif
|
|
||||||
if !exists("g:wakatime_api_key")
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
" Only load plugin once
|
" Only load plugin once
|
||||||
if exists("g:loaded_wakatime")
|
if exists("g:loaded_wakatime")
|
||||||
finish
|
finish
|
||||||
|
@ -45,38 +30,24 @@
|
||||||
let s:old_cpo = &cpo
|
let s:old_cpo = &cpo
|
||||||
set cpo&vim
|
set cpo&vim
|
||||||
|
|
||||||
let s:plugin_directory = expand("<sfile>:p:h")
|
" Set default away minutes
|
||||||
|
if !exists("g:wakatime_AwayMinutes")
|
||||||
" Set default updatetime
|
let g:wakatime_AwayMinutes = 10
|
||||||
if !exists("g:wakatime_updatetime")
|
|
||||||
let g:wakatime_updatetime = 15 " 15 minutes
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" We are not away until getting a CursorHold event
|
" Globals
|
||||||
let s:away_start = 0
|
let s:plugin_directory = expand("<sfile>:p:h") . '/'
|
||||||
|
let s:last_action = 0
|
||||||
" Create logfile if does not exist
|
|
||||||
exec "silent !touch ~/.wakatime.log"
|
|
||||||
|
|
||||||
|
" Import things python needs
|
||||||
python import time
|
python import time
|
||||||
python import time
|
|
||||||
python import uuid
|
|
||||||
python import vim
|
python import vim
|
||||||
python instance_id = str(uuid.uuid4())
|
|
||||||
python vim.command('let s:instance_id = "%s"' % instance_id)
|
|
||||||
|
|
||||||
" }}}
|
" }}}
|
||||||
|
|
||||||
|
|
||||||
" Function Definitions {{{
|
" Function Definitions {{{
|
||||||
|
|
||||||
function! s:setUpdateTime()
|
|
||||||
if &updatetime < 60 * 1000 * 2
|
|
||||||
let &updatetime = g:wakatime_updatetime * 60 * 1000
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
call s:setUpdateTime()
|
|
||||||
|
|
||||||
function! s:GetCurrentFile()
|
function! s:GetCurrentFile()
|
||||||
return expand("%:p")
|
return expand("%:p")
|
||||||
endfunction
|
endfunction
|
||||||
|
@ -86,10 +57,41 @@
|
||||||
return current_time
|
return current_time
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:api(type, task, time)
|
function! s:api(targetFile, time, endtime, is_write, last)
|
||||||
if a:task != ''
|
let targetFile = a:targetFile
|
||||||
exec "silent !python " . s:plugin_directory . "/wakatime.py --key" g:wakatime_api_key "--instance" s:instance_id "--action" a:type "--task" shellescape(a:task) "--time" a:time . " &"
|
if targetFile == ''
|
||||||
|
let targetFile = a:last[1]
|
||||||
endif
|
endif
|
||||||
|
if targetFile != ''
|
||||||
|
let extras = ''
|
||||||
|
if a:is_write
|
||||||
|
let extras = extras . '--write'
|
||||||
|
endif
|
||||||
|
if a:endtime
|
||||||
|
let extras = extras . '--endtime ' . a:endtime
|
||||||
|
endif
|
||||||
|
exec "silent !python" s:plugin_directory . "packages/wakatime/wakatime.py --verbose --file" shellescape(targetFile) "--time" a:time extras . " &"
|
||||||
|
let time = a:time
|
||||||
|
if a:endtime && time < a:endtime
|
||||||
|
let time = a:endtime
|
||||||
|
endif
|
||||||
|
call s:SetLastAction(time, targetFile)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:GetLastAction()
|
||||||
|
if !filereadable(expand("$HOME/.wakatime.data"))
|
||||||
|
return [0, '']
|
||||||
|
endif
|
||||||
|
let last = readfile(expand("$HOME/.wakatime.data"), '', 2)
|
||||||
|
if len(last) != 2
|
||||||
|
return [0, '']
|
||||||
|
endif
|
||||||
|
return [str2float(last[0]), last[1]]
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:SetLastAction(time, targetFile)
|
||||||
|
call writefile([a:time, a:targetFile], expand("$HOME/.wakatime.data"))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:getchar()
|
function! s:getchar()
|
||||||
|
@ -100,22 +102,39 @@
|
||||||
return c
|
return c
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! Wakatime_isAway()
|
function! s:enoughTimePassed(now, prev)
|
||||||
return s:away_start
|
if a:now - a:prev >= 299
|
||||||
|
return 1
|
||||||
|
endif
|
||||||
|
return 0
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:allServersAway()
|
function! s:away(now, last)
|
||||||
if has('clientserver')
|
if a:last[0] < 1
|
||||||
let servers = split(serverlist())
|
|
||||||
for server in servers
|
|
||||||
if server != v:servername
|
|
||||||
if !remote_expr(server,'Wakatime_isAway()')
|
|
||||||
return 0
|
return 0
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
let duration = a:now - a:last[0]
|
||||||
|
if duration > g:wakatime_AwayMinutes * 60
|
||||||
|
let units = 'seconds'
|
||||||
|
if duration > 59
|
||||||
|
let duration = round(duration / 60)
|
||||||
|
let units = 'minutes'
|
||||||
endif
|
endif
|
||||||
endfor
|
if duration > 59
|
||||||
|
let duration = round(duration / 60)
|
||||||
|
let units = 'hours'
|
||||||
endif
|
endif
|
||||||
|
if duration > 24
|
||||||
|
let duration = round(duration / 24)
|
||||||
|
let units = 'days'
|
||||||
|
endif
|
||||||
|
let answer = input(printf("You were away %.f %s. Add time to current file? (y/n)", duration, units))
|
||||||
|
if answer != "y"
|
||||||
return 1
|
return 1
|
||||||
|
endif
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" }}}
|
" }}}
|
||||||
|
@ -123,66 +142,21 @@
|
||||||
|
|
||||||
" Event Handlers {{{
|
" Event Handlers {{{
|
||||||
|
|
||||||
function! s:bufenter()
|
function! s:normalAction()
|
||||||
call s:api("open_file", s:GetCurrentFile(), s:GetCurrentTime())
|
let targetFile = s:GetCurrentFile()
|
||||||
endfunction
|
let now = s:GetCurrentTime()
|
||||||
|
let last = s:GetLastAction()
|
||||||
function! s:bufleave()
|
if s:enoughTimePassed(now, last[0]) || targetFile != last[1]
|
||||||
call s:api("close_file", s:GetCurrentFile(), s:GetCurrentTime())
|
if s:away(now, last)
|
||||||
endfunction
|
call s:api(targetFile, last[0], now, 0, last)
|
||||||
|
|
||||||
function! s:vimenter()
|
|
||||||
call s:api("open_editor", s:GetCurrentFile(), s:GetCurrentTime())
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:vimleave()
|
|
||||||
call s:api("quit_editor", s:GetCurrentFile(), s:GetCurrentTime())
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:bufwrite()
|
|
||||||
call s:api("write_file", s:GetCurrentFile(), s:GetCurrentTime())
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:cursorhold()
|
|
||||||
let s:away_task = s:GetCurrentFile()
|
|
||||||
python vim.command("let s:away_start=%f" % (time.time() - (float(vim.eval("&updatetime")) / 1000.0)))
|
|
||||||
autocmd Wakatime CursorMoved,CursorMovedI * call s:cursormoved()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:cursormoved()
|
|
||||||
autocmd! Wakatime CursorMoved,CursorMovedI *
|
|
||||||
|
|
||||||
" Don't do anything unless all other Vim instances are also away
|
|
||||||
if !s:allServersAway()
|
|
||||||
let s:away_start = 0
|
|
||||||
call s:api("ping", s:away_task, s:GetCurrentTime())
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
python vim.command("let away_end=%f" % time.time())
|
|
||||||
let away_unit = "minutes"
|
|
||||||
let away_duration = (away_end - s:away_start) / 60
|
|
||||||
if away_duration < 2
|
|
||||||
call s:setUpdateTime()
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
if away_duration > 59
|
|
||||||
let away_duration = away_duration / 60
|
|
||||||
let away_unit = "hours"
|
|
||||||
endif
|
|
||||||
if away_duration > 59
|
|
||||||
let away_duration = away_duration / 60
|
|
||||||
let away_unit = "days"
|
|
||||||
endif
|
|
||||||
let answer = input(printf("You were away %.f %s. Add time to current file? (y/n)", away_duration, away_unit))
|
|
||||||
if answer != "y"
|
|
||||||
call s:api("minimize_editor", s:away_task, printf("%f", s:away_start))
|
|
||||||
call s:api("maximize_editor", s:away_task, printf("%f", away_end))
|
|
||||||
else
|
else
|
||||||
call s:api("ping", s:away_task, s:GetCurrentTime())
|
call s:api(targetFile, now, 0, 0, last)
|
||||||
endif
|
endif
|
||||||
let s:away_start = 0
|
endif
|
||||||
"redraw!
|
endfunction
|
||||||
|
|
||||||
|
function! s:writeAction()
|
||||||
|
call s:api(s:GetCurrentFile(), s:GetCurrentTime(), 0, 1, s:GetLastAction())
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" }}}
|
" }}}
|
||||||
|
@ -192,12 +166,10 @@
|
||||||
|
|
||||||
augroup Wakatime
|
augroup Wakatime
|
||||||
autocmd!
|
autocmd!
|
||||||
autocmd BufEnter * call s:bufenter()
|
autocmd BufEnter * call s:normalAction()
|
||||||
autocmd BufLeave * call s:bufleave()
|
autocmd VimEnter * call s:normalAction()
|
||||||
autocmd VimEnter * call s:vimenter()
|
autocmd BufWritePost * call s:writeAction()
|
||||||
autocmd VimLeave * call s:vimleave()
|
autocmd CursorMoved,CursorMovedI * call s:normalAction()
|
||||||
autocmd BufWritePost * call s:bufwrite()
|
|
||||||
autocmd CursorHold,CursorHoldI * call s:cursorhold()
|
|
||||||
augroup END
|
augroup END
|
||||||
|
|
||||||
" }}}
|
" }}}
|
||||||
|
|
Loading…
Reference in a new issue