Compare commits
21 Commits
Author | SHA1 | Date |
---|---|---|
Alan Hamlett | c100213351 | |
Alan Hamlett | 43237a05c5 | |
Alan Hamlett | c34eccad4a | |
Alan Hamlett | 5077c5e290 | |
Carlos Henrique Guardão Gandarez | a387c08b44 | |
Carlos Henrique Guardão Gandarez | 3c2947cf79 | |
Alan Hamlett | 0744d7209b | |
Alan Hamlett | 54e6772a80 | |
Alan Hamlett | b576dfafe6 | |
Alan Hamlett | 5299efd6fa | |
Alan Hamlett | 74583a6845 | |
Alan Hamlett | 31f1f8ecdc | |
Alan Hamlett | c1f58fd05d | |
Alan Hamlett | 28063e3ac4 | |
Alan Hamlett | 4d56aca1a1 | |
Alan Hamlett | e15c514ef3 | |
Alan Hamlett | fe582c84b9 | |
Alan Hamlett | d7ee8675d8 | |
Alan Hamlett | da17125b97 | |
Alan Hamlett | 847223cdce | |
Alan Hamlett | b405f99cff |
36
HISTORY.rst
36
HISTORY.rst
|
@ -3,6 +3,42 @@ History
|
|||
-------
|
||||
|
||||
|
||||
11.1.0 (2022-11-12)
|
||||
++++++++++++++++++
|
||||
|
||||
- Support for api key vault cmd config.
|
||||
`#117 <https://github.com/wakatime/sublime-wakatime/pull/117>`_
|
||||
|
||||
|
||||
11.0.8 (2022-08-23)
|
||||
++++++++++++++++++
|
||||
|
||||
- Bugfix to prevent using empty selection object.
|
||||
`#116 <https://github.com/wakatime/sublime-wakatime/issues/116>`_
|
||||
|
||||
|
||||
11.0.7 (2022-06-25)
|
||||
++++++++++++++++++
|
||||
|
||||
- Check wakatime-cli versions in background thread.
|
||||
`#115 <https://github.com/wakatime/sublime-wakatime/issues/115>`_
|
||||
|
||||
|
||||
11.0.6 (2022-06-08)
|
||||
++++++++++++++++++
|
||||
|
||||
- Fix call to log helper.
|
||||
`#113 <https://github.com/wakatime/sublime-wakatime/issues/113>`_
|
||||
|
||||
|
||||
11.0.5 (2022-04-29)
|
||||
++++++++++++++++++
|
||||
|
||||
- Bugfix to not overwrite global urlopener in embedded Python.
|
||||
`#110 <https://github.com/wakatime/sublime-wakatime/issues/110>`_
|
||||
- Chmod wakatime-cli to be executable after updating on non-Windows platforms.
|
||||
|
||||
|
||||
11.0.4 (2022-01-06)
|
||||
++++++++++++++++++
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
[WakaTime][wakatime] is an open source Sublime Text plugin for metrics, insights, and time tracking automatically generated from your programming activity.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
1. Install [Package Control](https://packagecontrol.io/installation).
|
||||
|
@ -19,12 +18,10 @@
|
|||
|
||||
6. Use Sublime and your coding activity will be displayed on your [WakaTime dashboard](https://wakatime.com).
|
||||
|
||||
|
||||
## Screen Shots
|
||||
|
||||
![Project Overview](https://wakatime.com/static/img/ScreenShots/Screen-Shot-2016-03-21.png)
|
||||
|
||||
|
||||
## Unresponsive Plugin Warning
|
||||
|
||||
In Sublime Text 2, if you get a warning message:
|
||||
|
@ -35,7 +32,6 @@ To fix this, go to `Preferences → Settings - User` then add the following sett
|
|||
|
||||
`"detect_slow_plugins": false`
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
First, turn on debug mode in your `WakaTime.sublime-settings` file.
|
||||
|
|
192
WakaTime.py
192
WakaTime.py
|
@ -8,7 +8,7 @@ Website: https://wakatime.com/
|
|||
==========================================================="""
|
||||
|
||||
|
||||
__version__ = '11.0.4'
|
||||
__version__ = '11.1.0'
|
||||
|
||||
|
||||
import sublime
|
||||
|
@ -40,9 +40,9 @@ try:
|
|||
except ImportError:
|
||||
from configparser import ConfigParser, Error as ConfigParserError
|
||||
try:
|
||||
from urllib2 import urlopen, urlretrieve, ProxyHandler, build_opener, install_opener, HTTPError
|
||||
from urllib2 import Request, urlopen, HTTPError
|
||||
except ImportError:
|
||||
from urllib.request import urlopen, urlretrieve, ProxyHandler, build_opener, install_opener
|
||||
from urllib.request import Request, urlopen
|
||||
from urllib.error import HTTPError
|
||||
|
||||
|
||||
|
@ -186,6 +186,7 @@ class ApiKey(object):
|
|||
self._key = key
|
||||
return self._key
|
||||
|
||||
configs = None
|
||||
try:
|
||||
configs = parseConfigFile(CONFIG_FILE)
|
||||
if configs:
|
||||
|
@ -197,8 +198,38 @@ class ApiKey(object):
|
|||
except:
|
||||
pass
|
||||
|
||||
key = self.api_key_from_vault_cmd(configs)
|
||||
if key:
|
||||
self._key = key
|
||||
return self._key
|
||||
|
||||
return self._key
|
||||
|
||||
def api_key_from_vault_cmd(self, configs):
|
||||
vault_cmd = SETTINGS.get('api_key_vault_cmd')
|
||||
if not vault_cmd and configs:
|
||||
try:
|
||||
if configs.has_option('settings', 'api_key_vault_cmd'):
|
||||
vault_cmd = configs.get('settings', 'api_key_vault_cmd')
|
||||
except:
|
||||
pass
|
||||
|
||||
if not vault_cmd or not vault_cmd.strip():
|
||||
return None
|
||||
|
||||
try:
|
||||
process = Popen(vault_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
||||
stdout, stderr = process.communicate()
|
||||
retcode = process.poll()
|
||||
if retcode:
|
||||
log(ERROR, 'Vault command error ({retcode}): {stderr}'.format(retcode=retcode, stderr=u(stderr)))
|
||||
return None
|
||||
return stdout.strip() or None
|
||||
except:
|
||||
log(ERROR, traceback.format_exc())
|
||||
|
||||
return None
|
||||
|
||||
def write(self, key):
|
||||
global SETTINGS
|
||||
self._key = key
|
||||
|
@ -422,7 +453,7 @@ def append_heartbeat(entity, timestamp, is_write, view, project, folders):
|
|||
'lines_in_file': view.rowcol(view.size())[0] + 1,
|
||||
}
|
||||
selections = view.sel()
|
||||
if selections:
|
||||
if selections and len(selections) > 0:
|
||||
rowcol = view.rowcol(selections[0].begin())
|
||||
row, col = rowcol[0] + 1, rowcol[1] + 1
|
||||
heartbeat['lineno'] = row
|
||||
|
@ -571,12 +602,12 @@ class SendHeartbeatsThread(threading.Thread):
|
|||
process = Popen(cmd, stdin=stdin, stdout=PIPE, stderr=STDOUT)
|
||||
output, _err = process.communicate(input=inp)
|
||||
retcode = process.poll()
|
||||
if (not retcode or retcode == 102) and not output:
|
||||
if (not retcode or retcode == 102 or retcode == 112) and not output:
|
||||
self.sent()
|
||||
else:
|
||||
update_status_bar('Error')
|
||||
if retcode:
|
||||
log(DEBUG if retcode == 102 else ERROR, 'wakatime-core exited with status: {0}'.format(retcode))
|
||||
log(DEBUG if retcode == 102 or retcode == 112 else ERROR, 'wakatime-core exited with status: {0}'.format(retcode))
|
||||
if output:
|
||||
log(ERROR, u('wakatime-core output: {0}').format(output))
|
||||
except:
|
||||
|
@ -594,9 +625,7 @@ def plugin_loaded():
|
|||
log(INFO, 'Initializing WakaTime plugin v%s' % __version__)
|
||||
update_status_bar('Initializing...')
|
||||
|
||||
if not isCliLatest():
|
||||
thread = DownloadCLI()
|
||||
thread.start()
|
||||
UpdateCLI().start()
|
||||
|
||||
after_loaded()
|
||||
|
||||
|
@ -632,13 +661,19 @@ class WakatimeDashboardCommand(sublime_plugin.ApplicationCommand):
|
|||
webbrowser.open_new_tab('https://wakatime.com/dashboard')
|
||||
|
||||
|
||||
class DownloadCLI(threading.Thread):
|
||||
class UpdateCLI(threading.Thread):
|
||||
"""Non-blocking thread for downloading latest wakatime-cli from GitHub.
|
||||
"""
|
||||
|
||||
def run(self):
|
||||
if isCliLatest():
|
||||
return
|
||||
|
||||
log(INFO, 'Downloading wakatime-cli...')
|
||||
|
||||
if os.path.isdir(os.path.join(RESOURCES_FOLDER, 'wakatime-cli')):
|
||||
shutil.rmtree(os.path.join(RESOURCES_FOLDER, 'wakatime-cli'))
|
||||
|
||||
if not os.path.exists(RESOURCES_FOLDER):
|
||||
os.makedirs(RESOURCES_FOLDER)
|
||||
|
||||
|
@ -844,83 +879,98 @@ def reportMissingPlatformSupport(osname, arch):
|
|||
|
||||
|
||||
def request(url, last_modified=None):
|
||||
req = Request(url)
|
||||
req.add_header('User-Agent', 'github.com/wakatime/sublime-wakatime')
|
||||
|
||||
proxy = SETTINGS.get('proxy')
|
||||
if proxy:
|
||||
opener = build_opener(ProxyHandler({
|
||||
'http': proxy,
|
||||
'https': proxy,
|
||||
}))
|
||||
else:
|
||||
opener = build_opener()
|
||||
req.set_proxy(proxy, 'https')
|
||||
|
||||
headers = [('User-Agent', 'github.com/wakatime/sublime-wakatime')]
|
||||
if last_modified:
|
||||
headers.append(('If-Modified-Since', last_modified))
|
||||
|
||||
opener.addheaders = headers
|
||||
|
||||
install_opener(opener)
|
||||
req.add_header('If-Modified-Since', last_modified)
|
||||
|
||||
try:
|
||||
resp = urlopen(url)
|
||||
resp = urlopen(req)
|
||||
headers = dict(resp.getheaders()) if is_py2 else resp.headers
|
||||
return headers, resp.read(), resp.getcode()
|
||||
except HTTPError as err:
|
||||
if err.code == 304:
|
||||
return None, None, 304
|
||||
if is_py2:
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
try:
|
||||
resp = urlopen(url)
|
||||
headers = dict(resp.getheaders()) if is_py2 else resp.headers
|
||||
return headers, resp.read(), resp.getcode()
|
||||
except HTTPError as err2:
|
||||
if err2.code == 304:
|
||||
return None, None, 304
|
||||
log(DEBUG, err.read().decode())
|
||||
log(DEBUG, err2.read().decode())
|
||||
raise
|
||||
except IOError:
|
||||
raise
|
||||
with SSLCertVerificationDisabled():
|
||||
try:
|
||||
resp = urlopen(req)
|
||||
headers = dict(resp.getheaders()) if is_py2 else resp.headers
|
||||
return headers, resp.read(), resp.getcode()
|
||||
except HTTPError as err2:
|
||||
if err2.code == 304:
|
||||
return None, None, 304
|
||||
log(DEBUG, err.read().decode())
|
||||
log(DEBUG, err2.read().decode())
|
||||
raise
|
||||
except IOError:
|
||||
raise
|
||||
log(DEBUG, err.read().decode())
|
||||
raise
|
||||
except IOError:
|
||||
if is_py2:
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
try:
|
||||
resp = urlopen(url)
|
||||
headers = dict(resp.getheaders()) if is_py2 else resp.headers
|
||||
return headers, resp.read(), resp.getcode()
|
||||
except HTTPError as err:
|
||||
if err.code == 304:
|
||||
return None, None, 304
|
||||
log(DEBUG, err.read().decode())
|
||||
raise
|
||||
except IOError:
|
||||
raise
|
||||
with SSLCertVerificationDisabled():
|
||||
try:
|
||||
resp = urlopen(url)
|
||||
headers = dict(resp.getheaders()) if is_py2 else resp.headers
|
||||
return headers, resp.read(), resp.getcode()
|
||||
except HTTPError as err:
|
||||
if err.code == 304:
|
||||
return None, None, 304
|
||||
log(DEBUG, err.read().decode())
|
||||
raise
|
||||
except IOError:
|
||||
raise
|
||||
raise
|
||||
|
||||
|
||||
def download(url, filePath):
|
||||
req = Request(url)
|
||||
req.add_header('User-Agent', 'github.com/wakatime/sublime-wakatime')
|
||||
|
||||
proxy = SETTINGS.get('proxy')
|
||||
if proxy:
|
||||
opener = build_opener(ProxyHandler({
|
||||
'http': proxy,
|
||||
'https': proxy,
|
||||
}))
|
||||
else:
|
||||
opener = build_opener()
|
||||
opener.addheaders = [('User-Agent', 'github.com/wakatime/sublime-wakatime')]
|
||||
req.set_proxy(proxy, 'https')
|
||||
|
||||
install_opener(opener)
|
||||
|
||||
try:
|
||||
urlretrieve(url, filePath)
|
||||
except IOError:
|
||||
if is_py2:
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
urlretrieve(url, filePath)
|
||||
raise
|
||||
with open(filePath, 'wb') as fh:
|
||||
try:
|
||||
resp = urlopen(req)
|
||||
fh.write(resp.read())
|
||||
except HTTPError as err:
|
||||
if err.code == 304:
|
||||
return None, None, 304
|
||||
if is_py2:
|
||||
with SSLCertVerificationDisabled():
|
||||
try:
|
||||
resp = urlopen(req)
|
||||
fh.write(resp.read())
|
||||
return
|
||||
except HTTPError as err2:
|
||||
log(DEBUG, err.read().decode())
|
||||
log(DEBUG, err2.read().decode())
|
||||
raise
|
||||
except IOError:
|
||||
raise
|
||||
log(DEBUG, err.read().decode())
|
||||
raise
|
||||
except IOError:
|
||||
if is_py2:
|
||||
with SSLCertVerificationDisabled():
|
||||
try:
|
||||
resp = urlopen(url)
|
||||
fh.write(resp.read())
|
||||
return
|
||||
except HTTPError as err:
|
||||
log(DEBUG, err.read().decode())
|
||||
raise
|
||||
except IOError:
|
||||
raise
|
||||
raise
|
||||
|
||||
|
||||
def is_symlink(path):
|
||||
|
@ -942,5 +992,17 @@ def createSymlink():
|
|||
except:
|
||||
try:
|
||||
shutil.copy2(getCliLocation(), link)
|
||||
if not is_win:
|
||||
os.chmod(link, 509) # 755
|
||||
except:
|
||||
log(traceback.format_exc())
|
||||
log(WARNING, traceback.format_exc())
|
||||
|
||||
|
||||
class SSLCertVerificationDisabled(object):
|
||||
|
||||
def __enter__(self):
|
||||
self.original_context = ssl._create_default_https_context
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
|
||||
def __exit__(self, *args, **kwargs):
|
||||
ssl._create_default_https_context = self.original_context
|
||||
|
|
Loading…
Reference in New Issue