mirror of
https://github.com/powerline/powerline.git
synced 2025-07-30 17:25:28 +02:00
Merge remote-tracking branch 'zyx-i/threaded' into develop
This commit is contained in:
commit
f0f1f3f85e
2
.local.vimrc
Normal file
2
.local.vimrc
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
setlocal noexpandtab
|
||||||
|
let g:syntastic_python_flake8_args = '--ignore=W191,E501,E121,E122,E123,E128'
|
@ -3,9 +3,10 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath('../..'))
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(os.getcwd()))))
|
||||||
|
sys.path.insert(0, os.path.abspath(os.getcwd()))
|
||||||
|
|
||||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
|
extensions = ['powerline_autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
|
||||||
source_suffix = '.rst'
|
source_suffix = '.rst'
|
||||||
master_doc = 'index'
|
master_doc = 'index'
|
||||||
project = u'Powerline'
|
project = u'Powerline'
|
||||||
|
78
docs/source/powerline_autodoc.py
Normal file
78
docs/source/powerline_autodoc.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
from sphinx.ext import autodoc
|
||||||
|
from sphinx.util.inspect import getargspec
|
||||||
|
from inspect import ArgSpec, getargspec, formatargspec
|
||||||
|
from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment
|
||||||
|
from itertools import count
|
||||||
|
|
||||||
|
try:
|
||||||
|
from __builtin__ import unicode
|
||||||
|
except ImportError:
|
||||||
|
unicode = lambda s, enc: s
|
||||||
|
|
||||||
|
|
||||||
|
def formatvalue(val):
|
||||||
|
if type(val) is str:
|
||||||
|
return '="' + unicode(val, 'utf-8').replace('"', '\\"').replace('\\', '\\\\') + '"'
|
||||||
|
else:
|
||||||
|
return '=' + repr(val)
|
||||||
|
|
||||||
|
|
||||||
|
class ThreadedDocumenter(autodoc.FunctionDocumenter):
|
||||||
|
'''Specialized documenter subclass for ThreadedSegment subclasses.'''
|
||||||
|
@classmethod
|
||||||
|
def can_document_member(cls, member, membername, isattr, parent):
|
||||||
|
return (isinstance(member, ThreadedSegment) or
|
||||||
|
super(ThreadedDocumenter, cls).can_document_member(member, membername, isattr, parent))
|
||||||
|
|
||||||
|
def format_args(self):
|
||||||
|
if isinstance(self.object, ThreadedSegment):
|
||||||
|
args = ['interval']
|
||||||
|
defaults = [getattr(self.object, 'interval', 1)]
|
||||||
|
methods = ['render', 'set_state']
|
||||||
|
if isinstance(self.object, KwThreadedSegment):
|
||||||
|
methods += ['key', 'render_one']
|
||||||
|
|
||||||
|
for method in methods:
|
||||||
|
if hasattr(self.object, method):
|
||||||
|
# Note: on <python-2.6 it may return simple tuple, not
|
||||||
|
# ArgSpec instance.
|
||||||
|
argspec = getargspec(getattr(self.object, method))
|
||||||
|
for i, arg in zip(count(-1, -1), reversed(argspec.args)):
|
||||||
|
if (arg == 'self' or
|
||||||
|
(arg == 'segment_info' and
|
||||||
|
getattr(self.object, 'powerline_requires_segment_info', None)) or
|
||||||
|
(method == 'render_one' and -i == len(argspec.args))):
|
||||||
|
continue
|
||||||
|
if argspec.defaults and len(argspec.defaults) >= -i:
|
||||||
|
default = argspec.defaults[i]
|
||||||
|
defaults.append(default)
|
||||||
|
args.append(arg)
|
||||||
|
else:
|
||||||
|
args.insert(0, arg)
|
||||||
|
argspec = ArgSpec(args=args, varargs=None, keywords=None, defaults=tuple(defaults))
|
||||||
|
else:
|
||||||
|
if hasattr(self.object, 'powerline_origin'):
|
||||||
|
obj = self.object.powerline_origin
|
||||||
|
else:
|
||||||
|
obj = self.object
|
||||||
|
|
||||||
|
argspec = getargspec(obj)
|
||||||
|
args = []
|
||||||
|
defaults = []
|
||||||
|
for i, arg in zip(count(-1, -1), reversed(argspec.args)):
|
||||||
|
if (arg == 'segment_info' and getattr(self.object, 'powerline_requires_segment_info', None)):
|
||||||
|
continue
|
||||||
|
if argspec.defaults and len(argspec.defaults) >= -i:
|
||||||
|
default = argspec.defaults[i]
|
||||||
|
defaults.append(default)
|
||||||
|
args.append(arg)
|
||||||
|
else:
|
||||||
|
args.insert(0, arg)
|
||||||
|
argspec = ArgSpec(args=args, varargs=argspec.varargs, keywords=argspec.keywords, defaults=tuple(defaults))
|
||||||
|
|
||||||
|
return formatargspec(*argspec, formatvalue=formatvalue).replace('\\', '\\\\')
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
autodoc.setup(app)
|
||||||
|
app.add_autodocumenter(ThreadedDocumenter)
|
@ -36,7 +36,7 @@ class Powerline(object):
|
|||||||
the package imported like this: ``powerline.renders.{render_module}``.
|
the package imported like this: ``powerline.renders.{render_module}``.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, ext, renderer_module=None):
|
def __init__(self, ext, renderer_module=None, run_once=False):
|
||||||
self.config_paths = self.get_config_paths()
|
self.config_paths = self.get_config_paths()
|
||||||
|
|
||||||
# Load main config file
|
# Load main config file
|
||||||
@ -58,6 +58,7 @@ class Powerline(object):
|
|||||||
'ext': ext,
|
'ext': ext,
|
||||||
'common_config': common_config,
|
'common_config': common_config,
|
||||||
'segment_info': self.get_segment_info(),
|
'segment_info': self.get_segment_info(),
|
||||||
|
'run_once': run_once,
|
||||||
}
|
}
|
||||||
local_themes = self.get_local_themes(ext_config.get('local_themes'))
|
local_themes = self.get_local_themes(ext_config.get('local_themes'))
|
||||||
|
|
||||||
@ -69,7 +70,8 @@ class Powerline(object):
|
|||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
sys.stderr.write('Error while importing renderer module: {0}\n'.format(e))
|
sys.stderr.write('Error while importing renderer module: {0}\n'.format(e))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
options = {'term_truecolor': common_config.get('term_truecolor', False),
|
options = {
|
||||||
|
'term_truecolor': common_config.get('term_truecolor', False),
|
||||||
'ambiwidth': common_config.get('ambiwidth', 1),
|
'ambiwidth': common_config.get('ambiwidth', 1),
|
||||||
'tmux_escape': common_config.get('additional_escapes') == 'tmux',
|
'tmux_escape': common_config.get('additional_escapes') == 'tmux',
|
||||||
'screen_escape': common_config.get('additional_escapes') == 'screen',
|
'screen_escape': common_config.get('additional_escapes') == 'screen',
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from powerline.ipython import IpythonPowerline
|
from powerline.ipython import IpythonPowerline
|
||||||
|
|
||||||
from IPython.core.prompts import PromptManager
|
from IPython.core.prompts import PromptManager
|
||||||
|
from IPython.core.hooks import TryNext
|
||||||
|
|
||||||
|
|
||||||
class PowerlinePromptManager(PromptManager):
|
class PowerlinePromptManager(PromptManager):
|
||||||
@ -41,6 +42,12 @@ def load_ipython_extension(ip):
|
|||||||
ip.prompt_manager = PowerlinePromptManager(powerline=powerline,
|
ip.prompt_manager = PowerlinePromptManager(powerline=powerline,
|
||||||
shell=ip.prompt_manager.shell, config=ip.prompt_manager.config)
|
shell=ip.prompt_manager.shell, config=ip.prompt_manager.config)
|
||||||
|
|
||||||
|
def shutdown_hook():
|
||||||
|
powerline.renderer.shutdown()
|
||||||
|
raise TryNext()
|
||||||
|
|
||||||
|
ip.hooks.shutdown_hook.add(shutdown_hook)
|
||||||
|
|
||||||
|
|
||||||
def unload_ipython_extension(ip):
|
def unload_ipython_extension(ip):
|
||||||
ip.prompt_manager = old_prompt_manager
|
ip.prompt_manager = old_prompt_manager
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from powerline.ipython import IpythonPowerline
|
from powerline.ipython import IpythonPowerline
|
||||||
from IPython.Prompts import BasePrompt
|
from IPython.Prompts import BasePrompt
|
||||||
from IPython.ipapi import get as get_ipython
|
from IPython.ipapi import get as get_ipython
|
||||||
|
from IPython.ipapi import TryNext
|
||||||
|
|
||||||
|
|
||||||
class PowerlinePrompt(BasePrompt):
|
class PowerlinePrompt(BasePrompt):
|
||||||
@ -38,4 +39,11 @@ def setup(prompt='1', **kwargs):
|
|||||||
old_prompt = getattr(ip.IP.outputcache, attr)
|
old_prompt = getattr(ip.IP.outputcache, attr)
|
||||||
setattr(ip.IP.outputcache, attr, PowerlinePrompt(powerline,
|
setattr(ip.IP.outputcache, attr, PowerlinePrompt(powerline,
|
||||||
old_prompt.cache, old_prompt.sep, '', old_prompt.pad_left))
|
old_prompt.cache, old_prompt.sep, '', old_prompt.pad_left))
|
||||||
|
raise TryNext()
|
||||||
|
|
||||||
|
def shutdown_hook():
|
||||||
|
powerline.renderer.shutdown()
|
||||||
|
raise TryNext()
|
||||||
|
|
||||||
ip.IP.hooks.late_startup_hook.add(late_startup_hook)
|
ip.IP.hooks.late_startup_hook.add(late_startup_hook)
|
||||||
|
ip.IP.hooks.shutdown_hook.add(shutdown_hook)
|
||||||
|
@ -92,4 +92,5 @@ augroup Powerline
|
|||||||
autocmd!
|
autocmd!
|
||||||
autocmd ColorScheme * :exec s:powerline_pycmd 'powerline.renderer.reset_highlight()'
|
autocmd ColorScheme * :exec s:powerline_pycmd 'powerline.renderer.reset_highlight()'
|
||||||
autocmd VimEnter * :redrawstatus!
|
autocmd VimEnter * :redrawstatus!
|
||||||
|
autocmd VimLeave * :exec s:powerline_pycmd 'powerline.renderer.shutdown()'
|
||||||
augroup END
|
augroup END
|
||||||
|
@ -2,9 +2,13 @@
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from powerline.lib.memoize import memoize # NOQA
|
|
||||||
from powerline.lib.humanize_bytes import humanize_bytes # NOQA
|
def wraps_saveargs(wrapped):
|
||||||
from powerline.lib.url import urllib_read, urllib_urlencode # NOQA
|
def dec(wrapper):
|
||||||
|
r = wraps(wrapped)(wrapper)
|
||||||
|
r.powerline_origin = getattr(wrapped, 'powerline_origin', wrapped)
|
||||||
|
return r
|
||||||
|
return dec
|
||||||
|
|
||||||
|
|
||||||
def mergedicts(d1, d2):
|
def mergedicts(d1, d2):
|
||||||
@ -19,7 +23,7 @@ def mergedicts(d1, d2):
|
|||||||
|
|
||||||
def add_divider_highlight_group(highlight_group):
|
def add_divider_highlight_group(highlight_group):
|
||||||
def dec(func):
|
def dec(func):
|
||||||
@wraps(func)
|
@wraps_saveargs(func)
|
||||||
def f(**kwargs):
|
def f(**kwargs):
|
||||||
r = func(**kwargs)
|
r = func(**kwargs)
|
||||||
if r:
|
if r:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# vim:fileencoding=utf-8:noet
|
# vim:fileencoding=utf-8:noet
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
import time
|
from powerline.lib.time import monotonic
|
||||||
|
|
||||||
|
|
||||||
def default_cache_key(**kwargs):
|
def default_cache_key(**kwargs):
|
||||||
@ -28,10 +28,13 @@ class memoize(object):
|
|||||||
cached = self.cache.get(key, None)
|
cached = self.cache.get(key, None)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return func(**kwargs)
|
return func(**kwargs)
|
||||||
if cached is None or time.time() - cached['time'] > self.timeout:
|
# Handle case when time() appears to be less then cached['time'] due
|
||||||
|
# to clock updates. Not applicable for monotonic clock, but this
|
||||||
|
# case is currently rare.
|
||||||
|
if cached is None or not (cached['time'] < monotonic() < cached['time'] + self.timeout):
|
||||||
cached = self.cache[key] = {
|
cached = self.cache[key] = {
|
||||||
'result': func(**kwargs),
|
'result': func(**kwargs),
|
||||||
'time': time.time(),
|
'time': monotonic(),
|
||||||
}
|
}
|
||||||
return cached['result']
|
return cached['result']
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
131
powerline/lib/threaded.py
Normal file
131
powerline/lib/threaded.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# vim:fileencoding=utf-8:noet
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from powerline.lib.time import monotonic
|
||||||
|
|
||||||
|
from time import sleep
|
||||||
|
from threading import Thread, Lock
|
||||||
|
|
||||||
|
|
||||||
|
class ThreadedSegment(Thread):
|
||||||
|
daemon = True
|
||||||
|
min_sleep_time = 0.1
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(ThreadedSegment, self).__init__()
|
||||||
|
self.update_lock = Lock()
|
||||||
|
self.write_lock = Lock()
|
||||||
|
self.keep_going = True
|
||||||
|
self.run_once = True
|
||||||
|
self.did_set_interval = False
|
||||||
|
|
||||||
|
def __call__(self, **kwargs):
|
||||||
|
if self.run_once:
|
||||||
|
self.set_state(**kwargs)
|
||||||
|
self.update()
|
||||||
|
elif not self.is_alive():
|
||||||
|
self.startup(**kwargs)
|
||||||
|
|
||||||
|
with self.write_lock:
|
||||||
|
return self.render(**kwargs)
|
||||||
|
|
||||||
|
def sleep(self, adjust_time):
|
||||||
|
sleep(max(self.interval - adjust_time, self.min_sleep_time))
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while self.keep_going:
|
||||||
|
start_time = monotonic()
|
||||||
|
|
||||||
|
with self.update_lock:
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
self.sleep(monotonic() - start_time)
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
self.keep_going = False
|
||||||
|
self.update_lock.acquire()
|
||||||
|
|
||||||
|
def set_interval(self, interval=None, **kwargs):
|
||||||
|
# Allowing “interval” keyword in configuration.
|
||||||
|
# Note: Here **kwargs is needed to support foreign data, in subclasses
|
||||||
|
# it can be seen in a number of places in order to support
|
||||||
|
# .set_interval().
|
||||||
|
interval = interval or getattr(self, 'interval', 1)
|
||||||
|
self.interval = interval
|
||||||
|
self.has_set_interval = True
|
||||||
|
|
||||||
|
def set_state(self, **kwargs):
|
||||||
|
if not self.did_set_interval:
|
||||||
|
self.set_interval(**kwargs)
|
||||||
|
|
||||||
|
def startup(self, **kwargs):
|
||||||
|
# Normally .update() succeeds to run before value is requested, meaning
|
||||||
|
# that user is getting values he needs directly at vim startup. Without
|
||||||
|
# .startup() we will not have to wait long until receiving bug “I opened
|
||||||
|
# vim, but branch information is only shown after I move cursor”.
|
||||||
|
self.run_once = False
|
||||||
|
|
||||||
|
self.set_state(**kwargs)
|
||||||
|
|
||||||
|
if not self.is_alive():
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
|
||||||
|
def printed(func):
|
||||||
|
def f(*args, **kwargs):
|
||||||
|
print(func.__name__)
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
class KwThreadedSegment(ThreadedSegment):
|
||||||
|
drop_interval = 10 * 60
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(KwThreadedSegment, self).__init__()
|
||||||
|
self.queries = {}
|
||||||
|
self.update_missing = True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def key(**kwargs):
|
||||||
|
return frozenset(kwargs.items())
|
||||||
|
|
||||||
|
def render(self, **kwargs):
|
||||||
|
key = self.key(**kwargs)
|
||||||
|
try:
|
||||||
|
update_state = self.queries[key][1]
|
||||||
|
except KeyError:
|
||||||
|
update_state = self.compute_state(key) if self.update_missing else None
|
||||||
|
# No locks: render method is already running with write_lock acquired.
|
||||||
|
self.queries[key] = (monotonic(), update_state)
|
||||||
|
return self.render_one(update_state, **kwargs)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
updates = {}
|
||||||
|
removes = []
|
||||||
|
for key, (last_query_time, state) in list(self.queries.items()):
|
||||||
|
if last_query_time < monotonic() < last_query_time + self.drop_interval:
|
||||||
|
updates[key] = (last_query_time, self.compute_state(key))
|
||||||
|
else:
|
||||||
|
removes.append(key)
|
||||||
|
with self.write_lock:
|
||||||
|
self.queries.update(updates)
|
||||||
|
for key in removes:
|
||||||
|
self.queries.pop(key)
|
||||||
|
|
||||||
|
def set_state(self, **kwargs):
|
||||||
|
if not self.did_set_interval or ('interval' in kwargs and self.interval > kwargs['interval']):
|
||||||
|
self.set_interval(**kwargs)
|
||||||
|
|
||||||
|
key = self.key(**kwargs)
|
||||||
|
self.queries[key] = (monotonic(), None)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def render_one(update_state, **kwargs):
|
||||||
|
return update_state
|
||||||
|
|
||||||
|
|
||||||
|
def with_docstring(instance, doc):
|
||||||
|
instance.__doc__ = doc
|
||||||
|
return instance
|
103
powerline/lib/time.py
Normal file
103
powerline/lib/time.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
# vim:fileencoding=utf-8:noet
|
||||||
|
|
||||||
|
from __future__ import division, absolute_import
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
# >=python-3.3, Unix
|
||||||
|
from time import clock_gettime
|
||||||
|
try:
|
||||||
|
# >={kernel}-sources-2.6.28
|
||||||
|
from time import CLOCK_MONOTONIC_RAW as CLOCK_ID
|
||||||
|
except ImportError:
|
||||||
|
from time import CLOCK_MONOTONIC as CLOCK_ID # NOQA
|
||||||
|
|
||||||
|
monotonic = lambda: clock_gettime(CLOCK_ID)
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
# >=python-3.3
|
||||||
|
from time import monotonic # NOQA
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
import ctypes
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
# Windows only
|
||||||
|
GetTickCount64 = ctypes.windll.kernel32.GetTickCount64
|
||||||
|
GetTickCount64.restype = ctypes.c_ulonglong
|
||||||
|
|
||||||
|
def monotonic(): # NOQA
|
||||||
|
return GetTickCount64() / 1000
|
||||||
|
|
||||||
|
elif sys.platform == 'darwin':
|
||||||
|
# Mac OS X
|
||||||
|
from ctypes.util import find_library
|
||||||
|
|
||||||
|
libc_name = find_library('c')
|
||||||
|
if not libc_name:
|
||||||
|
raise OSError
|
||||||
|
|
||||||
|
libc = ctypes.CDLL(libc_name, use_errno=True)
|
||||||
|
|
||||||
|
mach_absolute_time = libc.mach_absolute_time
|
||||||
|
mach_absolute_time.argtypes = ()
|
||||||
|
mach_absolute_time.restype = ctypes.c_uint64
|
||||||
|
|
||||||
|
class mach_timebase_info_data_t(ctypes.Structure):
|
||||||
|
_fields_ = (
|
||||||
|
('numer', ctypes.c_uint32),
|
||||||
|
('denom', ctypes.c_uint32),
|
||||||
|
)
|
||||||
|
mach_timebase_info_data_p = ctypes.POINTER(mach_timebase_info_data_t)
|
||||||
|
|
||||||
|
_mach_timebase_info = libc.mach_timebase_info
|
||||||
|
_mach_timebase_info.argtypes = (mach_timebase_info_data_p,)
|
||||||
|
_mach_timebase_info.restype = ctypes.c_int
|
||||||
|
|
||||||
|
def mach_timebase_info():
|
||||||
|
timebase = mach_timebase_info_data_t()
|
||||||
|
_mach_timebase_info(ctypes.byref(timebase))
|
||||||
|
return (timebase.numer, timebase.denom)
|
||||||
|
|
||||||
|
timebase = mach_timebase_info()
|
||||||
|
factor = timebase[0] / timebase[1] * 1e-9
|
||||||
|
|
||||||
|
def monotonic(): # NOQA
|
||||||
|
return mach_absolute_time() * factor
|
||||||
|
else:
|
||||||
|
# linux only (no librt on OS X)
|
||||||
|
import os
|
||||||
|
|
||||||
|
# See <bits/time.h>
|
||||||
|
CLOCK_MONOTONIC = 1
|
||||||
|
CLOCK_MONOTONIC_RAW = 4
|
||||||
|
|
||||||
|
class timespec(ctypes.Structure):
|
||||||
|
_fields_ = (
|
||||||
|
('tv_sec', ctypes.c_long),
|
||||||
|
('tv_nsec', ctypes.c_long)
|
||||||
|
)
|
||||||
|
tspec = timespec()
|
||||||
|
|
||||||
|
librt = ctypes.CDLL('librt.so.1', use_errno=True)
|
||||||
|
clock_gettime = librt.clock_gettime
|
||||||
|
clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)]
|
||||||
|
|
||||||
|
if clock_gettime(CLOCK_MONOTONIC_RAW, ctypes.pointer(tspec)) == 0:
|
||||||
|
# >={kernel}-sources-2.6.28
|
||||||
|
clock_id = CLOCK_MONOTONIC_RAW
|
||||||
|
elif clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(tspec)) == 0:
|
||||||
|
clock_id = CLOCK_MONOTONIC
|
||||||
|
else:
|
||||||
|
raise OSError
|
||||||
|
|
||||||
|
def monotonic(): # NOQA
|
||||||
|
if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(tspec)) != 0:
|
||||||
|
errno_ = ctypes.get_errno()
|
||||||
|
raise OSError(errno_, os.strerror(errno_))
|
||||||
|
return tspec.tv_sec + tspec.tv_nsec / 1e9
|
||||||
|
|
||||||
|
except:
|
||||||
|
from time import time as monotonic # NOQA
|
@ -1,26 +1,16 @@
|
|||||||
# vim:fileencoding=utf-8:noet
|
# vim:fileencoding=utf-8:noet
|
||||||
|
|
||||||
|
try:
|
||||||
|
from urllib.error import HTTPError
|
||||||
|
from urllib.request import urlopen
|
||||||
|
from urllib.parse import urlencode as urllib_urlencode # NOQA
|
||||||
|
except ImportError:
|
||||||
|
from urllib2 import urlopen, HTTPError # NOQA
|
||||||
|
from urllib import urlencode as urllib_urlencode # NOQA
|
||||||
|
|
||||||
|
|
||||||
def urllib_read(url):
|
def urllib_read(url):
|
||||||
try:
|
try:
|
||||||
import urllib.error
|
return urlopen(url, timeout=10).read().decode('utf-8')
|
||||||
import urllib.request
|
except HTTPError:
|
||||||
try:
|
|
||||||
return urllib.request.urlopen(url, timeout=5).read().decode('utf-8')
|
|
||||||
except:
|
|
||||||
return
|
return
|
||||||
except ImportError:
|
|
||||||
import urllib2
|
|
||||||
try:
|
|
||||||
return urllib2.urlopen(url, timeout=5).read()
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def urllib_urlencode(string):
|
|
||||||
try:
|
|
||||||
import urllib.parse
|
|
||||||
return urllib.parse.urlencode(string)
|
|
||||||
except ImportError:
|
|
||||||
import urllib
|
|
||||||
return urllib.urlencode(string)
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
# vim:fileencoding=utf-8:noet
|
# vim:fileencoding=utf-8:noet
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
import os
|
import os
|
||||||
from powerline.lib.memoize import memoize
|
|
||||||
|
|
||||||
|
|
||||||
vcs_props = (
|
vcs_props = (
|
||||||
@ -16,12 +15,11 @@ def generate_directories(path):
|
|||||||
while True:
|
while True:
|
||||||
old_path = path
|
old_path = path
|
||||||
path = os.path.dirname(path)
|
path = os.path.dirname(path)
|
||||||
if path == old_path:
|
if path == old_path or not path:
|
||||||
break
|
break
|
||||||
yield path
|
yield path
|
||||||
|
|
||||||
|
|
||||||
@memoize(100)
|
|
||||||
def guess(path):
|
def guess(path):
|
||||||
for directory in generate_directories(path):
|
for directory in generate_directories(path):
|
||||||
for vcs, vcs_dir, check in vcs_props:
|
for vcs, vcs_dir, check in vcs_props:
|
||||||
|
@ -97,6 +97,7 @@ except ImportError:
|
|||||||
def readlines(cmd, cwd):
|
def readlines(cmd, cwd):
|
||||||
p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, cwd=cwd)
|
p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, cwd=cwd)
|
||||||
p.stderr.close()
|
p.stderr.close()
|
||||||
|
with p.stdout:
|
||||||
for line in p.stdout:
|
for line in p.stdout:
|
||||||
yield line[:-1].decode('utf-8')
|
yield line[:-1].decode('utf-8')
|
||||||
|
|
||||||
|
@ -40,6 +40,9 @@ class Renderer(object):
|
|||||||
def get_theme(self, matcher_info):
|
def get_theme(self, matcher_info):
|
||||||
return self.theme
|
return self.theme
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
self.theme.shutdown()
|
||||||
|
|
||||||
def get_highlighting(self, segment, mode):
|
def get_highlighting(self, segment, mode):
|
||||||
segment['highlight'] = self.colorscheme.get_highlighting(segment['highlight_group'], mode, segment.get('gradient_level'))
|
segment['highlight'] = self.colorscheme.get_highlighting(segment['highlight_group'], mode, segment.get('gradient_level'))
|
||||||
if segment['divider_highlight_group']:
|
if segment['divider_highlight_group']:
|
||||||
|
@ -31,6 +31,12 @@ class VimRenderer(Renderer):
|
|||||||
super(VimRenderer, self).__init__(*args, **kwargs)
|
super(VimRenderer, self).__init__(*args, **kwargs)
|
||||||
self.hl_groups = {}
|
self.hl_groups = {}
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
self.theme.shutdown()
|
||||||
|
for match in self.local_themes.values():
|
||||||
|
if 'theme' in match:
|
||||||
|
match['theme'].shutdown()
|
||||||
|
|
||||||
def add_local_theme(self, matcher, theme):
|
def add_local_theme(self, matcher, theme):
|
||||||
if matcher in self.local_themes:
|
if matcher in self.local_themes:
|
||||||
raise KeyError('There is already a local theme with given matcher')
|
raise KeyError('There is already a local theme with given matcher')
|
||||||
|
@ -79,6 +79,8 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None):
|
|||||||
'include_modes': segment.get('include_modes', []),
|
'include_modes': segment.get('include_modes', []),
|
||||||
'width': segment.get('width'),
|
'width': segment.get('width'),
|
||||||
'align': segment.get('align', 'l'),
|
'align': segment.get('align', 'l'),
|
||||||
|
'shutdown': getattr(contents_func, 'shutdown', None),
|
||||||
|
'startup': getattr(contents_func, 'startup', None),
|
||||||
'_rendered_raw': '',
|
'_rendered_raw': '',
|
||||||
'_rendered_hl': '',
|
'_rendered_hl': '',
|
||||||
'_len': 0,
|
'_len': 0,
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# vim:fileencoding=utf-8:noet
|
# vim:fileencoding=utf-8:noet
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@ -7,8 +9,13 @@ from datetime import datetime
|
|||||||
import socket
|
import socket
|
||||||
from multiprocessing import cpu_count
|
from multiprocessing import cpu_count
|
||||||
|
|
||||||
from powerline.lib import memoize, urllib_read, urllib_urlencode, add_divider_highlight_group
|
from powerline.lib import add_divider_highlight_group
|
||||||
|
from powerline.lib.url import urllib_read, urllib_urlencode
|
||||||
from powerline.lib.vcs import guess
|
from powerline.lib.vcs import guess
|
||||||
|
from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment, with_docstring
|
||||||
|
from powerline.lib.time import monotonic
|
||||||
|
from powerline.lib.humanize_bytes import humanize_bytes
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
|
||||||
def hostname(only_if_ssh=False):
|
def hostname(only_if_ssh=False):
|
||||||
@ -22,27 +29,8 @@ def hostname(only_if_ssh=False):
|
|||||||
return socket.gethostname()
|
return socket.gethostname()
|
||||||
|
|
||||||
|
|
||||||
def user():
|
|
||||||
'''Return the current user.
|
|
||||||
|
|
||||||
Highlights the user with the ``superuser`` if the effective user ID is 0.
|
|
||||||
|
|
||||||
Highlight groups used: ``superuser`` or ``user``. It is recommended to define all highlight groups.
|
|
||||||
'''
|
|
||||||
user = os.environ.get('USER')
|
|
||||||
try:
|
|
||||||
euid = os.geteuid()
|
|
||||||
except AttributeError:
|
|
||||||
# os.geteuid is not available on windows
|
|
||||||
euid = 1
|
|
||||||
return [{
|
|
||||||
'contents': user,
|
|
||||||
'highlight_group': 'user' if euid != 0 else ['superuser', 'user'],
|
|
||||||
}]
|
|
||||||
|
|
||||||
|
|
||||||
def branch(status_colors=True):
|
def branch(status_colors=True):
|
||||||
'''Return the current VCS branch.@
|
'''Return the current VCS branch.
|
||||||
|
|
||||||
:param bool status_colors:
|
:param bool status_colors:
|
||||||
determines whether repository status will be used to determine highlighting. Default: True.
|
determines whether repository status will be used to determine highlighting. Default: True.
|
||||||
@ -180,53 +168,38 @@ def fuzzy_time():
|
|||||||
return ' '.join([minute, hour])
|
return ' '.join([minute, hour])
|
||||||
|
|
||||||
|
|
||||||
@memoize(600)
|
|
||||||
def _external_ip(query_url='http://ipv4.icanhazip.com/'):
|
def _external_ip(query_url='http://ipv4.icanhazip.com/'):
|
||||||
return urllib_read(query_url).strip()
|
return urllib_read(query_url).strip()
|
||||||
|
|
||||||
|
|
||||||
def external_ip(query_url='http://ipv4.icanhazip.com/'):
|
class ExternalIpSegment(ThreadedSegment):
|
||||||
'''Return external IP address.
|
def set_state(self, query_url='http://ipv4.icanhazip.com/', **kwargs):
|
||||||
|
super(ExternalIpSegment, self).set_state(**kwargs)
|
||||||
|
self.query_url = query_url
|
||||||
|
|
||||||
Suggested URIs:
|
def update(self):
|
||||||
|
ip = _external_ip(query_url=self.query_url)
|
||||||
|
with self.write_lock:
|
||||||
|
self.ip = ip
|
||||||
|
|
||||||
* http://ipv4.icanhazip.com/
|
def render(self):
|
||||||
* http://ipv6.icanhazip.com/
|
return [{'contents': self.ip, 'divider_highlight_group': 'background:divider'}]
|
||||||
* http://icanhazip.com/ (returns IPv6 address if available, else IPv4)
|
|
||||||
|
|
||||||
:param str query_url:
|
|
||||||
|
external_ip = with_docstring(ExternalIpSegment(),
|
||||||
|
'''Return external IP address.
|
||||||
|
|
||||||
|
Suggested URIs:
|
||||||
|
|
||||||
|
* http://ipv4.icanhazip.com/
|
||||||
|
* http://ipv6.icanhazip.com/
|
||||||
|
* http://icanhazip.com/ (returns IPv6 address if available, else IPv4)
|
||||||
|
|
||||||
|
:param str query_url:
|
||||||
URI to query for IP address, should return only the IP address as a text string
|
URI to query for IP address, should return only the IP address as a text string
|
||||||
|
|
||||||
Divider highlight group used: ``background:divider``.
|
Divider highlight group used: ``background:divider``.
|
||||||
'''
|
''')
|
||||||
return [{'contents': _external_ip(query_url=query_url), 'divider_highlight_group': 'background:divider'}]
|
|
||||||
|
|
||||||
|
|
||||||
@add_divider_highlight_group('background:divider')
|
|
||||||
def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'):
|
|
||||||
'''Return system uptime.
|
|
||||||
|
|
||||||
Uses the ``psutil`` module if available for multi-platform compatibility,
|
|
||||||
falls back to reading :file:`/proc/uptime`.
|
|
||||||
|
|
||||||
:param str format:
|
|
||||||
format string, will be passed ``days``, ``hours`` and ``minutes`` as arguments
|
|
||||||
|
|
||||||
Divider highlight group used: ``background:divider``.
|
|
||||||
'''
|
|
||||||
try:
|
|
||||||
import psutil
|
|
||||||
seconds = int((datetime.now() - datetime.fromtimestamp(psutil.BOOT_TIME)).total_seconds())
|
|
||||||
except ImportError:
|
|
||||||
try:
|
|
||||||
with open('/proc/uptime', 'r') as f:
|
|
||||||
seconds = int(float(f.readline().split()[0]))
|
|
||||||
except IOError:
|
|
||||||
return None
|
|
||||||
minutes, seconds = divmod(seconds, 60)
|
|
||||||
hours, minutes = divmod(minutes, 60)
|
|
||||||
days, hours = divmod(hours, 24)
|
|
||||||
return format.format(days=int(days), hours=hours, minutes=minutes)
|
|
||||||
|
|
||||||
|
|
||||||
# Weather condition code descriptions available at
|
# Weather condition code descriptions available at
|
||||||
@ -304,66 +277,84 @@ weather_conditions_icons = {
|
|||||||
'unknown': '⚠',
|
'unknown': '⚠',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
temp_conversions = {
|
||||||
|
'C': lambda temp: temp,
|
||||||
|
'F': lambda temp: (temp * 9 / 5) + 32,
|
||||||
|
'K': lambda temp: temp + 273.15,
|
||||||
|
}
|
||||||
|
|
||||||
@memoize(1800)
|
# Note: there are also unicode characters for units: ℃, ℉ and K
|
||||||
def weather(unit='c', location_query=None, icons=None):
|
temp_units = {
|
||||||
'''Return weather from Yahoo! Weather.
|
'C': '°C',
|
||||||
|
'F': '°F',
|
||||||
|
'K': 'K',
|
||||||
|
}
|
||||||
|
|
||||||
Uses GeoIP lookup from http://freegeoip.net/ to automatically determine
|
|
||||||
your current location. This should be changed if you're in a VPN or if your
|
|
||||||
IP address is registered at another location.
|
|
||||||
|
|
||||||
Returns a list of colorized icon and temperature segments depending on
|
class WeatherSegment(ThreadedSegment):
|
||||||
weather conditions.
|
interval = 600
|
||||||
|
|
||||||
:param str unit:
|
def set_state(self, location_query=None, **kwargs):
|
||||||
temperature unit, can be one of ``F``, ``C`` or ``K``
|
super(WeatherSegment, self).set_state(**kwargs)
|
||||||
:param str location_query:
|
self.location = location_query
|
||||||
location query for your current location, e.g. ``oslo, norway``
|
self.url = None
|
||||||
:param dict icons:
|
self.condition = {}
|
||||||
dict for overriding default icons, e.g. ``{'heavy_snow' : u'❆'}``
|
|
||||||
|
|
||||||
Divider highlight group used: ``background:divider``.
|
def update(self):
|
||||||
|
|
||||||
Highlight groups used: ``weather_conditions`` or ``weather``, ``weather_temp_cold`` or ``weather_temp_hot`` or ``weather_temp`` or ``weather``.
|
|
||||||
Also uses ``weather_conditions_{condition}`` for all weather conditions supported by Yahoo.
|
|
||||||
'''
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
if not location_query:
|
if not self.url:
|
||||||
|
# Do not lock attribute assignments in this branch: they are used
|
||||||
|
# only in .update()
|
||||||
|
if not self.location:
|
||||||
try:
|
try:
|
||||||
location = json.loads(urllib_read('http://freegeoip.net/json/' + _external_ip()))
|
location_data = json.loads(urllib_read('http://freegeoip.net/json/' + _external_ip()))
|
||||||
location_query = ','.join([location['city'], location['region_name'], location['country_name']])
|
self.location = ','.join([location_data['city'],
|
||||||
|
location_data['region_name'],
|
||||||
|
location_data['country_name']])
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
return None
|
return
|
||||||
query_data = {
|
query_data = {
|
||||||
'q':
|
'q':
|
||||||
'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;'
|
'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;'
|
||||||
'select * from we where location="{0}" and unit="{1}"'.format(location_query, unit).encode('utf-8'),
|
'select * from we where location="{0}" and unit="c"'.format(self.location).encode('utf-8'),
|
||||||
'format': 'json'
|
'format': 'json',
|
||||||
}
|
}
|
||||||
|
self.url = 'http://query.yahooapis.com/v1/public/yql?' + urllib_urlencode(query_data)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
url = 'http://query.yahooapis.com/v1/public/yql?' + urllib_urlencode(query_data)
|
raw_response = urllib_read(self.url)
|
||||||
response = json.loads(urllib_read(url))
|
response = json.loads(raw_response)
|
||||||
condition = response['query']['results']['weather']['rss']['channel']['item']['condition']
|
condition = response['query']['results']['weather']['rss']['channel']['item']['condition']
|
||||||
condition_code = int(condition['code'])
|
condition_code = int(condition['code'])
|
||||||
|
temp = float(condition['temp'])
|
||||||
except (KeyError, TypeError, ValueError):
|
except (KeyError, TypeError, ValueError):
|
||||||
return None
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
icon_names = weather_conditions_codes[condition_code]
|
icon_names = weather_conditions_codes[condition_code]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
icon_names = (('not_available' if condition_code == 3200 else 'unknown'),)
|
icon_names = (('not_available' if condition_code == 3200 else 'unknown'),)
|
||||||
|
|
||||||
for icon_name in icon_names:
|
with self.write_lock:
|
||||||
|
self.temp = temp
|
||||||
|
self.icon_names = icon_names
|
||||||
|
|
||||||
|
def render(self, icons=None, unit='C', temperature_format=None, **kwargs):
|
||||||
|
if not hasattr(self, 'icon_names'):
|
||||||
|
return None
|
||||||
|
|
||||||
|
for icon_name in self.icon_names:
|
||||||
if icons:
|
if icons:
|
||||||
if icon_name in icons:
|
if icon_name in icons:
|
||||||
icon = icons[icon_name]
|
icon = icons[icon_name]
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
icon = weather_conditions_icons[icon_names[-1]]
|
icon = weather_conditions_icons[self.icon_names[-1]]
|
||||||
|
|
||||||
groups = ['weather_condition_' + icon_name for icon_name in icon_names] + ['weather_conditions', 'weather']
|
temperature_format = temperature_format or ('{temp:.0f}' + temp_units[unit])
|
||||||
|
temp = temp_conversions[unit](self.temp)
|
||||||
|
groups = ['weather_condition_' + icon_name for icon_name in self.icon_names] + ['weather_conditions', 'weather']
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'contents': icon + ' ',
|
'contents': icon + ' ',
|
||||||
@ -371,14 +362,40 @@ def weather(unit='c', location_query=None, icons=None):
|
|||||||
'divider_highlight_group': 'background:divider',
|
'divider_highlight_group': 'background:divider',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'contents': '{0}°{1}'.format(condition['temp'], unit.upper()),
|
'contents': temperature_format.format(temp=temp),
|
||||||
'highlight_group': ['weather_temp_cold' if int(condition['temp']) < 0 else 'weather_temp_hot', 'weather_temp', 'weather'],
|
'highlight_group': ['weather_temp_cold' if int(self.temp) < 0 else 'weather_temp_hot', 'weather_temp', 'weather'],
|
||||||
'draw_divider': False,
|
'draw_divider': False,
|
||||||
'divider_highlight_group': 'background:divider',
|
'divider_highlight_group': 'background:divider',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
weather = with_docstring(WeatherSegment(),
|
||||||
|
'''Return weather from Yahoo! Weather.
|
||||||
|
|
||||||
|
Uses GeoIP lookup from http://freegeoip.net/ to automatically determine
|
||||||
|
your current location. This should be changed if you're in a VPN or if your
|
||||||
|
IP address is registered at another location.
|
||||||
|
|
||||||
|
Returns a list of colorized icon and temperature segments depending on
|
||||||
|
weather conditions.
|
||||||
|
|
||||||
|
:param str unit:
|
||||||
|
temperature unit, can be one of ``F``, ``C`` or ``K``
|
||||||
|
:param str location_query:
|
||||||
|
location query for your current location, e.g. ``oslo, norway``
|
||||||
|
:param dict icons:
|
||||||
|
dict for overriding default icons, e.g. ``{'heavy_snow' : u'❆'}``
|
||||||
|
:param str temperature_format:
|
||||||
|
format string, receives ``temp`` as an argument. Should also hold unit.
|
||||||
|
|
||||||
|
Divider highlight group used: ``background:divider``.
|
||||||
|
|
||||||
|
Highlight groups used: ``weather_conditions`` or ``weather``, ``weather_temp_cold`` or ``weather_temp_hot`` or ``weather_temp`` or ``weather``.
|
||||||
|
Also uses ``weather_conditions_{condition}`` for all weather conditions supported by Yahoo.
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2):
|
def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2):
|
||||||
'''Return normalized system load average.
|
'''Return normalized system load average.
|
||||||
|
|
||||||
@ -397,7 +414,11 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2):
|
|||||||
|
|
||||||
Highlight groups used: ``system_load_good`` or ``system_load``, ``system_load_bad`` or ``system_load``, ``system_load_ugly`` or ``system_load``. It is recommended to define all highlight groups.
|
Highlight groups used: ``system_load_good`` or ``system_load``, ``system_load_bad`` or ``system_load``, ``system_load_ugly`` or ``system_load``. It is recommended to define all highlight groups.
|
||||||
'''
|
'''
|
||||||
|
global cpu_count
|
||||||
|
try:
|
||||||
cpu_num = cpu_count()
|
cpu_num = cpu_count()
|
||||||
|
except NotImplementedError:
|
||||||
|
return None
|
||||||
ret = []
|
ret = []
|
||||||
for avg in os.getloadavg():
|
for avg in os.getloadavg():
|
||||||
normalized = avg / cpu_num
|
normalized = avg / cpu_num
|
||||||
@ -419,7 +440,20 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def cpu_load_percent(measure_interval=.5):
|
try:
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
def _get_bytes(interface):
|
||||||
|
io_counters = psutil.network_io_counters(pernic=True)
|
||||||
|
if_io = io_counters.get(interface)
|
||||||
|
if not if_io:
|
||||||
|
return None
|
||||||
|
return if_io.bytes_recv, if_io.bytes_sent
|
||||||
|
|
||||||
|
def _get_user():
|
||||||
|
return psutil.Process(os.getpid()).username
|
||||||
|
|
||||||
|
def cpu_load_percent(measure_interval=.5):
|
||||||
'''Return the average CPU load as a percentage.
|
'''Return the average CPU load as a percentage.
|
||||||
|
|
||||||
Requires the ``psutil`` module.
|
Requires the ``psutil`` module.
|
||||||
@ -427,43 +461,10 @@ def cpu_load_percent(measure_interval=.5):
|
|||||||
:param float measure_interval:
|
:param float measure_interval:
|
||||||
interval used to measure CPU load (in seconds)
|
interval used to measure CPU load (in seconds)
|
||||||
'''
|
'''
|
||||||
try:
|
|
||||||
import psutil
|
|
||||||
except ImportError:
|
|
||||||
return None
|
|
||||||
cpu_percent = int(psutil.cpu_percent(interval=measure_interval))
|
cpu_percent = int(psutil.cpu_percent(interval=measure_interval))
|
||||||
return '{0}%'.format(cpu_percent)
|
return '{0}%'.format(cpu_percent)
|
||||||
|
except ImportError:
|
||||||
|
def _get_bytes(interface): # NOQA
|
||||||
@add_divider_highlight_group('background:divider')
|
|
||||||
def network_load(interface='eth0', measure_interval=1, suffix='B/s', si_prefix=False):
|
|
||||||
'''Return the network load.
|
|
||||||
|
|
||||||
Uses the ``psutil`` module if available for multi-platform compatibility,
|
|
||||||
falls back to reading
|
|
||||||
:file:`/sys/class/net/{interface}/statistics/{rx,tx}_bytes`.
|
|
||||||
|
|
||||||
:param str interface:
|
|
||||||
network interface to measure
|
|
||||||
:param float measure_interval:
|
|
||||||
interval used to measure the network load (in seconds)
|
|
||||||
:param str suffix:
|
|
||||||
string appended to each load string
|
|
||||||
:param bool si_prefix:
|
|
||||||
use SI prefix, e.g. MB instead of MiB
|
|
||||||
'''
|
|
||||||
import time
|
|
||||||
from powerline.lib import humanize_bytes
|
|
||||||
|
|
||||||
def get_bytes():
|
|
||||||
try:
|
|
||||||
import psutil
|
|
||||||
io_counters = psutil.network_io_counters(pernic=True)
|
|
||||||
if_io = io_counters.get(interface)
|
|
||||||
if not if_io:
|
|
||||||
return None
|
|
||||||
return (if_io.bytes_recv, if_io.bytes_sent)
|
|
||||||
except ImportError:
|
|
||||||
try:
|
try:
|
||||||
with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj:
|
with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj:
|
||||||
rx = int(file_obj.read())
|
rx = int(file_obj.read())
|
||||||
@ -473,15 +474,141 @@ def network_load(interface='eth0', measure_interval=1, suffix='B/s', si_prefix=F
|
|||||||
except IOError:
|
except IOError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
b1 = get_bytes()
|
def _get_user(): # NOQA
|
||||||
if b1 is None:
|
return os.environ.get('USER', None)
|
||||||
|
|
||||||
|
def cpu_load_percent(measure_interval=.5): # NOQA
|
||||||
|
'''Return the average CPU load as a percentage.
|
||||||
|
|
||||||
|
Requires the ``psutil`` module.
|
||||||
|
|
||||||
|
:param float measure_interval:
|
||||||
|
interval used to measure CPU load (in seconds)
|
||||||
|
'''
|
||||||
return None
|
return None
|
||||||
time.sleep(measure_interval)
|
|
||||||
b2 = get_bytes()
|
|
||||||
return '⬇ {rx_diff} ⬆ {tx_diff}'.format(
|
username = False
|
||||||
rx_diff=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, si_prefix).rjust(8),
|
|
||||||
tx_diff=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, si_prefix).rjust(8),
|
|
||||||
)
|
def user():
|
||||||
|
'''Return the current user.
|
||||||
|
|
||||||
|
Highlights the user with the ``superuser`` if the effective user ID is 0.
|
||||||
|
|
||||||
|
Highlight groups used: ``superuser`` or ``user``. It is recommended to define all highlight groups.
|
||||||
|
'''
|
||||||
|
global username
|
||||||
|
if username is False:
|
||||||
|
username = _get_user()
|
||||||
|
if username is None:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
euid = os.geteuid()
|
||||||
|
except AttributeError:
|
||||||
|
# os.geteuid is not available on windows
|
||||||
|
euid = 1
|
||||||
|
return [{
|
||||||
|
'contents': username,
|
||||||
|
'highlight_group': 'user' if euid != 0 else ['superuser', 'user'],
|
||||||
|
}]
|
||||||
|
|
||||||
|
|
||||||
|
if os.path.exists('/proc/uptime'):
|
||||||
|
def _get_uptime():
|
||||||
|
with open('/proc/uptime', 'r') as f:
|
||||||
|
return int(float(f.readline().split()[0]))
|
||||||
|
elif 'psutil' in globals():
|
||||||
|
from time import time
|
||||||
|
def _get_uptime(): # NOQA
|
||||||
|
# psutil.BOOT_TIME is not subject to clock adjustments, but time() is.
|
||||||
|
# Thus it is a fallback to /proc/uptime reading and not the reverse.
|
||||||
|
return int(time() - psutil.BOOT_TIME)
|
||||||
|
else:
|
||||||
|
def _get_uptime(): # NOQA
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
@add_divider_highlight_group('background:divider')
|
||||||
|
def uptime(format='{days}d {hours:02d}h {minutes:02d}m'):
|
||||||
|
'''Return system uptime.
|
||||||
|
|
||||||
|
:param str format:
|
||||||
|
format string, will be passed ``days``, ``hours``, ``minutes`` and
|
||||||
|
seconds as arguments
|
||||||
|
|
||||||
|
Divider highlight group used: ``background:divider``.
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
seconds = _get_uptime()
|
||||||
|
except (IOError, NotImplementedError):
|
||||||
|
return None
|
||||||
|
minutes, seconds = divmod(seconds, 60)
|
||||||
|
hours, minutes = divmod(minutes, 60)
|
||||||
|
days, hours = divmod(hours, 24)
|
||||||
|
return format.format(days=int(days), hours=hours, minutes=minutes, seconds=seconds)
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkLoadSegment(KwThreadedSegment):
|
||||||
|
interfaces = {}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def key(interface='eth0', **kwargs):
|
||||||
|
return interface
|
||||||
|
|
||||||
|
def compute_state(self, interface):
|
||||||
|
if interface in self.interfaces:
|
||||||
|
idata = self.interfaces[interface]
|
||||||
|
try:
|
||||||
|
idata['prev'] = idata['last']
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
idata = {}
|
||||||
|
if self.run_once:
|
||||||
|
idata['prev'] = (monotonic(), _get_bytes(interface))
|
||||||
|
self.sleep(0)
|
||||||
|
self.interfaces[interface] = idata
|
||||||
|
|
||||||
|
idata['last'] = (monotonic(), _get_bytes(interface))
|
||||||
|
return idata
|
||||||
|
|
||||||
|
def render_one(self, idata, format='⬇ {recv:>8} ⬆ {sent:>8}', suffix='B/s', si_prefix=False, **kwargs):
|
||||||
|
if not idata or 'prev' not in idata:
|
||||||
|
return None
|
||||||
|
|
||||||
|
t1, b1 = idata['prev']
|
||||||
|
t2, b2 = idata['last']
|
||||||
|
measure_interval = t2 - t1
|
||||||
|
|
||||||
|
if None in (b1, b2):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return [{
|
||||||
|
'contents': format.format(
|
||||||
|
recv=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, si_prefix),
|
||||||
|
sent=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, si_prefix),
|
||||||
|
),
|
||||||
|
'divider_highlight_group': 'background:divider',
|
||||||
|
}]
|
||||||
|
|
||||||
|
|
||||||
|
network_load = with_docstring(NetworkLoadSegment(),
|
||||||
|
'''Return the network load.
|
||||||
|
|
||||||
|
Uses the ``psutil`` module if available for multi-platform compatibility,
|
||||||
|
falls back to reading
|
||||||
|
:file:`/sys/class/net/{interface}/statistics/{rx,tx}_bytes`.
|
||||||
|
|
||||||
|
:param str interface:
|
||||||
|
network interface to measure
|
||||||
|
:param str suffix:
|
||||||
|
string appended to each load string
|
||||||
|
:param bool si_prefix:
|
||||||
|
use SI prefix, e.g. MB instead of MiB
|
||||||
|
:param str format:
|
||||||
|
format string, receives ``recv`` and ``sent`` as arguments
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
def virtualenv():
|
def virtualenv():
|
||||||
@ -489,32 +616,26 @@ def virtualenv():
|
|||||||
return os.path.basename(os.environ.get('VIRTUAL_ENV', '')) or None
|
return os.path.basename(os.environ.get('VIRTUAL_ENV', '')) or None
|
||||||
|
|
||||||
|
|
||||||
@memoize(60)
|
_IMAPKey = namedtuple('Key', 'username password server port folder')
|
||||||
def email_imap_alert(username, password, server='imap.gmail.com', port=993, folder='INBOX'):
|
|
||||||
'''Return unread e-mail count for IMAP servers.
|
|
||||||
|
|
||||||
:param str username:
|
|
||||||
login username
|
|
||||||
:param str password:
|
|
||||||
login password
|
|
||||||
:param str server:
|
|
||||||
e-mail server
|
|
||||||
:param int port:
|
|
||||||
e-mail server port
|
|
||||||
:param str folder:
|
|
||||||
folder to check for e-mails
|
|
||||||
|
|
||||||
Highlight groups used: ``email_alert``.
|
class EmailIMAPSegment(KwThreadedSegment):
|
||||||
'''
|
interval = 60
|
||||||
import imaplib
|
|
||||||
import re
|
|
||||||
|
|
||||||
if not username or not password:
|
@staticmethod
|
||||||
|
def key(username, password, server='imap.gmail.com', port=993, folder='INBOX'):
|
||||||
|
return _IMAPKey(username, password, server, port, folder)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def compute_state(key):
|
||||||
|
if not key.username or not key.password:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
mail = imaplib.IMAP4_SSL(server, port)
|
import imaplib
|
||||||
mail.login(username, password)
|
import re
|
||||||
rc, message = mail.status(folder, '(UNSEEN)')
|
mail = imaplib.IMAP4_SSL(key.server, key.port)
|
||||||
|
mail.login(key.username, key.password)
|
||||||
|
rc, message = mail.status(key.folder, '(UNSEEN)')
|
||||||
unread_str = message[0].decode('utf-8')
|
unread_str = message[0].decode('utf-8')
|
||||||
unread_count = int(re.search('UNSEEN (\d+)', unread_str).group(1))
|
unread_count = int(re.search('UNSEEN (\d+)', unread_str).group(1))
|
||||||
except socket.gaierror:
|
except socket.gaierror:
|
||||||
@ -529,6 +650,24 @@ def email_imap_alert(username, password, server='imap.gmail.com', port=993, fold
|
|||||||
}]
|
}]
|
||||||
|
|
||||||
|
|
||||||
|
email_imap_alert = with_docstring(EmailIMAPSegment(),
|
||||||
|
'''Return unread e-mail count for IMAP servers.
|
||||||
|
|
||||||
|
:param str username:
|
||||||
|
login username
|
||||||
|
:param str password:
|
||||||
|
login password
|
||||||
|
:param str server:
|
||||||
|
e-mail server
|
||||||
|
:param int port:
|
||||||
|
e-mail server port
|
||||||
|
:param str folder:
|
||||||
|
folder to check for e-mails
|
||||||
|
|
||||||
|
Highlight groups used: ``email_alert``.
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
class NowPlayingSegment(object):
|
class NowPlayingSegment(object):
|
||||||
STATE_SYMBOLS = {
|
STATE_SYMBOLS = {
|
||||||
'fallback': '♫',
|
'fallback': '♫',
|
||||||
|
@ -10,9 +10,11 @@ except ImportError:
|
|||||||
|
|
||||||
from powerline.bindings.vim import vim_get_func, getbufvar
|
from powerline.bindings.vim import vim_get_func, getbufvar
|
||||||
from powerline.theme import requires_segment_info
|
from powerline.theme import requires_segment_info
|
||||||
from powerline.lib import memoize, humanize_bytes, add_divider_highlight_group
|
from powerline.lib import add_divider_highlight_group
|
||||||
from powerline.lib.vcs import guess
|
from powerline.lib.vcs import guess
|
||||||
from functools import wraps
|
from powerline.lib.humanize_bytes import humanize_bytes
|
||||||
|
from powerline.lib.threaded import KwThreadedSegment, with_docstring
|
||||||
|
from powerline.lib import wraps_saveargs as wraps
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
vim_funcs = {
|
vim_funcs = {
|
||||||
@ -20,6 +22,7 @@ vim_funcs = {
|
|||||||
'fnamemodify': vim_get_func('fnamemodify'),
|
'fnamemodify': vim_get_func('fnamemodify'),
|
||||||
'expand': vim_get_func('expand'),
|
'expand': vim_get_func('expand'),
|
||||||
'bufnr': vim_get_func('bufnr', rettype=int),
|
'bufnr': vim_get_func('bufnr', rettype=int),
|
||||||
|
'line2byte': vim_get_func('line2byte', rettype=int),
|
||||||
}
|
}
|
||||||
|
|
||||||
vim_modes = {
|
vim_modes = {
|
||||||
@ -74,28 +77,18 @@ def launchevent(event):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def bufnr(segment_info, **kwargs):
|
|
||||||
'''Used for cache key, returns current buffer number'''
|
|
||||||
return segment_info['bufnr']
|
|
||||||
|
|
||||||
|
|
||||||
def bufname(segment_info, **kwargs):
|
|
||||||
'''Used for cache key, returns current buffer name'''
|
|
||||||
return segment_info['buffer'].name
|
|
||||||
|
|
||||||
|
|
||||||
# TODO Remove cache when needed
|
# TODO Remove cache when needed
|
||||||
def window_cached(func):
|
def window_cached(func):
|
||||||
cache = {}
|
cache = {}
|
||||||
|
|
||||||
@requires_segment_info
|
@requires_segment_info
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def ret(segment_info, *args, **kwargs):
|
def ret(segment_info, **kwargs):
|
||||||
window_id = segment_info['window_id']
|
window_id = segment_info['window_id']
|
||||||
if segment_info['mode'] == 'nc':
|
if segment_info['mode'] == 'nc':
|
||||||
return cache.get(window_id)
|
return cache.get(window_id)
|
||||||
else:
|
else:
|
||||||
r = func(*args, **kwargs)
|
r = func(**kwargs)
|
||||||
cache[window_id] = r
|
cache[window_id] = r
|
||||||
return r
|
return r
|
||||||
|
|
||||||
@ -167,7 +160,7 @@ def file_directory(segment_info, shorten_user=True, shorten_cwd=True, shorten_ho
|
|||||||
if not name:
|
if not name:
|
||||||
return None
|
return None
|
||||||
file_directory = vim_funcs['fnamemodify'](name, (':~' if shorten_user else '')
|
file_directory = vim_funcs['fnamemodify'](name, (':~' if shorten_user else '')
|
||||||
+ (':.' if shorten_home else '') + ':h')
|
+ (':.' if shorten_cwd else '') + ':h')
|
||||||
if shorten_home and file_directory.startswith('/home/'):
|
if shorten_home and file_directory.startswith('/home/'):
|
||||||
file_directory = '~' + file_directory[6:]
|
file_directory = '~' + file_directory[6:]
|
||||||
return file_directory + os.sep if file_directory else None
|
return file_directory + os.sep if file_directory else None
|
||||||
@ -195,10 +188,9 @@ def file_name(segment_info, display_no_file=False, no_file_text='[No file]'):
|
|||||||
return file_name
|
return file_name
|
||||||
|
|
||||||
|
|
||||||
@requires_segment_info
|
@window_cached
|
||||||
@memoize(2, cache_key=bufname, cache_reg_func=purgebuf_on_shell_and_write)
|
def file_size(suffix='B', si_prefix=False):
|
||||||
def file_size(segment_info, suffix='B', si_prefix=False):
|
'''Return file size in &encoding.
|
||||||
'''Return file size.
|
|
||||||
|
|
||||||
:param str suffix:
|
:param str suffix:
|
||||||
string appended to the file size
|
string appended to the file size
|
||||||
@ -206,13 +198,9 @@ def file_size(segment_info, suffix='B', si_prefix=False):
|
|||||||
use SI prefix, e.g. MB instead of MiB
|
use SI prefix, e.g. MB instead of MiB
|
||||||
:return: file size or None if the file isn't saved or if the size is too big to fit in a number
|
:return: file size or None if the file isn't saved or if the size is too big to fit in a number
|
||||||
'''
|
'''
|
||||||
file_name = segment_info['buffer'].name
|
# Note: returns file size in &encoding, not in &fileencoding. But returned
|
||||||
if not file_name:
|
# size is updated immediately; and it is valid for any buffer
|
||||||
return None
|
file_size = vim_funcs['line2byte'](len(vim.current.buffer) + 1) - 1
|
||||||
try:
|
|
||||||
file_size = os.stat(file_name).st_size
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
return humanize_bytes(file_size, suffix, si_prefix)
|
return humanize_bytes(file_size, suffix, si_prefix)
|
||||||
|
|
||||||
|
|
||||||
@ -311,38 +299,118 @@ def modified_buffers(text='+ ', join_str=','):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@requires_segment_info
|
class KwWindowThreadedSegment(KwThreadedSegment):
|
||||||
@memoize(2, cache_key=bufnr, cache_reg_func=purgeall_on_shell)
|
def set_state(self, **kwargs):
|
||||||
def branch(segment_info, status_colors=True):
|
for window in vim.windows:
|
||||||
'''Return the current working branch.
|
buffer = window.buffer
|
||||||
|
kwargs['segment_info'] = {'bufnr': buffer.number, 'buffer': buffer}
|
||||||
|
super(KwWindowThreadedSegment, self).set_state(**kwargs)
|
||||||
|
|
||||||
:param bool status_colors:
|
|
||||||
determines whether repository status will be used to determine highlighting. Default: True.
|
|
||||||
|
|
||||||
Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``.
|
class RepositorySegment(KwWindowThreadedSegment):
|
||||||
|
def __init__(self):
|
||||||
|
super(RepositorySegment, self).__init__()
|
||||||
|
self.directories = {}
|
||||||
|
|
||||||
Divider highlight group used: ``branch:divider``.
|
@staticmethod
|
||||||
'''
|
def key(segment_info, **kwargs):
|
||||||
repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd()))
|
# FIXME os.getcwd() is not a proper variant for non-current buffers
|
||||||
|
return segment_info['buffer'].name or os.getcwd()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
# .compute_state() is running only in this method, and only in one
|
||||||
|
# thread, thus operations with .directories do not need write locks
|
||||||
|
# (.render() method is not using .directories). If this is changed
|
||||||
|
# .directories needs redesigning
|
||||||
|
self.directories.clear()
|
||||||
|
super(RepositorySegment, self).update()
|
||||||
|
|
||||||
|
def compute_state(self, path):
|
||||||
|
repo = guess(path=path)
|
||||||
if repo:
|
if repo:
|
||||||
return [{
|
if repo.directory in self.directories:
|
||||||
'contents': repo.branch(),
|
return self.directories[repo.directory]
|
||||||
'highlight_group': (['branch_dirty' if repo.status() else 'branch_clean'] if status_colors else []) + ['branch'],
|
else:
|
||||||
'divider_highlight_group': 'branch:divider',
|
r = self.process_repo(repo)
|
||||||
}]
|
self.directories[repo.directory] = r
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
@requires_segment_info
|
||||||
|
class RepositoryStatusSegment(RepositorySegment):
|
||||||
|
interval = 2
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def process_repo(repo):
|
||||||
|
return repo.status()
|
||||||
|
|
||||||
|
|
||||||
|
repository_status = with_docstring(RepositoryStatusSegment(),
|
||||||
|
'''Return the status for the current repo.''')
|
||||||
|
|
||||||
|
|
||||||
|
@requires_segment_info
|
||||||
|
class BranchSegment(RepositorySegment):
|
||||||
|
interval = 0.2
|
||||||
|
started_repository_status = False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def process_repo(repo):
|
||||||
|
return repo.branch()
|
||||||
|
|
||||||
|
def render_one(self, update_state, segment_info, status_colors=False, **kwargs):
|
||||||
|
if not update_state:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if status_colors:
|
||||||
|
self.started_repository_status = True
|
||||||
|
|
||||||
|
return [{
|
||||||
|
'contents': update_state,
|
||||||
|
'highlight_group': (['branch_dirty' if repository_status(segment_info=segment_info) else 'branch_clean']
|
||||||
|
if status_colors else []) + ['branch'],
|
||||||
|
'divider_highlight_group': 'branch:divider',
|
||||||
|
}]
|
||||||
|
|
||||||
|
def startup(self, **kwargs):
|
||||||
|
super(BranchSegment, self).startup()
|
||||||
|
if kwargs.get('status_colors', False):
|
||||||
|
self.started_repository_status = True
|
||||||
|
repository_status.startup()
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
if self.started_repository_status:
|
||||||
|
repository_status.shutdown()
|
||||||
|
super(BranchSegment, self).shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
branch = with_docstring(BranchSegment(),
|
||||||
|
'''Return the current working branch.
|
||||||
|
|
||||||
|
:param bool status_colors:
|
||||||
|
determines whether repository status will be used to determine highlighting. Default: False.
|
||||||
|
|
||||||
|
Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``.
|
||||||
|
|
||||||
|
Divider highlight group used: ``branch:divider``.
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
@requires_segment_info
|
@requires_segment_info
|
||||||
@memoize(2, cache_key=bufnr, cache_reg_func=purgebuf_on_shell_and_write)
|
class FileVCSStatusSegment(KwWindowThreadedSegment):
|
||||||
def file_vcs_status(segment_info):
|
interval = 0.2
|
||||||
'''Return the VCS status for this buffer.
|
|
||||||
|
|
||||||
Highlight groups used: ``file_vcs_status``.
|
@staticmethod
|
||||||
'''
|
def key(segment_info, **kwargs):
|
||||||
name = segment_info['buffer'].name
|
name = segment_info['buffer'].name
|
||||||
if name and not getbufvar(segment_info['bufnr'], '&buftype'):
|
skip = not (name and (not getbufvar(segment_info['bufnr'], '&buftype')))
|
||||||
repo = guess(path=os.path.abspath(name))
|
return name, skip
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def compute_state(key):
|
||||||
|
name, skip = key
|
||||||
|
if not skip:
|
||||||
|
repo = guess(path=name)
|
||||||
if repo:
|
if repo:
|
||||||
status = repo.status(os.path.relpath(name, repo.directory))
|
status = repo.status(os.path.relpath(name, repo.directory))
|
||||||
if not status:
|
if not status:
|
||||||
@ -358,11 +426,8 @@ def file_vcs_status(segment_info):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@requires_segment_info
|
file_vcs_status = with_docstring(FileVCSStatusSegment(),
|
||||||
@memoize(2, cache_key=bufnr, cache_reg_func=purgeall_on_shell)
|
'''Return the VCS status for this buffer.
|
||||||
def repository_status(segment_info):
|
|
||||||
'''Return the status for the current repo.'''
|
Highlight groups used: ``file_vcs_status``.
|
||||||
repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd()))
|
''')
|
||||||
if repo:
|
|
||||||
return repo.status().strip() or None
|
|
||||||
return None
|
|
||||||
|
@ -14,10 +14,10 @@ def mergeargs(argvalue):
|
|||||||
|
|
||||||
|
|
||||||
class ShellPowerline(Powerline):
|
class ShellPowerline(Powerline):
|
||||||
def __init__(self, args):
|
def __init__(self, args, run_once=False):
|
||||||
self.args = args
|
self.args = args
|
||||||
self.theme_option = mergeargs(args.theme_option) or {}
|
self.theme_option = mergeargs(args.theme_option) or {}
|
||||||
super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module)
|
super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module, run_once=run_once)
|
||||||
|
|
||||||
def get_segment_info(self):
|
def get_segment_info(self):
|
||||||
return self.args
|
return self.args
|
||||||
|
@ -19,12 +19,12 @@ def u(s):
|
|||||||
|
|
||||||
|
|
||||||
def requires_segment_info(func):
|
def requires_segment_info(func):
|
||||||
func.requires_powerline_segment_info = True
|
func.powerline_requires_segment_info = True
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
|
||||||
class Theme(object):
|
class Theme(object):
|
||||||
def __init__(self, ext, theme_config, common_config, top_theme_config=None, segment_info=None):
|
def __init__(self, ext, theme_config, common_config, top_theme_config=None, segment_info=None, run_once=False):
|
||||||
self.dividers = theme_config.get('dividers', common_config['dividers'])
|
self.dividers = theme_config.get('dividers', common_config['dividers'])
|
||||||
self.spaces = theme_config.get('spaces', common_config['spaces'])
|
self.spaces = theme_config.get('spaces', common_config['spaces'])
|
||||||
self.segments = {
|
self.segments = {
|
||||||
@ -42,6 +42,18 @@ class Theme(object):
|
|||||||
get_segment = gen_segment_getter(ext, common_config['paths'], theme_configs, theme_config.get('default_module'))
|
get_segment = gen_segment_getter(ext, common_config['paths'], theme_configs, theme_config.get('default_module'))
|
||||||
for side in ['left', 'right']:
|
for side in ['left', 'right']:
|
||||||
self.segments[side].extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, [])))
|
self.segments[side].extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, [])))
|
||||||
|
if not run_once:
|
||||||
|
for segment in self.segments[side]:
|
||||||
|
if segment['startup']:
|
||||||
|
segment['startup'](**segment['args'])
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
for segments in self.segments.values():
|
||||||
|
for segment in segments:
|
||||||
|
try:
|
||||||
|
segment['shutdown']()
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
def get_divider(self, side='left', type='soft'):
|
def get_divider(self, side='left', type='soft'):
|
||||||
'''Return segment divider.'''
|
'''Return segment divider.'''
|
||||||
@ -60,8 +72,8 @@ class Theme(object):
|
|||||||
parsed_segments = []
|
parsed_segments = []
|
||||||
for segment in self.segments[side]:
|
for segment in self.segments[side]:
|
||||||
if segment['type'] == 'function':
|
if segment['type'] == 'function':
|
||||||
if (hasattr(segment['contents_func'], 'requires_powerline_segment_info')
|
if (hasattr(segment['contents_func'], 'powerline_requires_segment_info')
|
||||||
and segment['contents_func'].requires_powerline_segment_info):
|
and segment['contents_func'].powerline_requires_segment_info):
|
||||||
contents = segment['contents_func'](segment_info=self.segment_info, **segment['args'])
|
contents = segment['contents_func'](segment_info=self.segment_info, **segment['args'])
|
||||||
else:
|
else:
|
||||||
contents = segment['contents_func'](**segment['args'])
|
contents = segment['contents_func'](**segment['args'])
|
||||||
|
@ -12,7 +12,7 @@ except ImportError:
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
args = get_argparser(description=__doc__).parse_args()
|
args = get_argparser(description=__doc__).parse_args()
|
||||||
powerline = ShellPowerline(args)
|
powerline = ShellPowerline(args, run_once=True)
|
||||||
rendered = powerline.renderer.render(width=args.width, side=args.side)
|
rendered = powerline.renderer.render(width=args.width, side=args.side)
|
||||||
try:
|
try:
|
||||||
sys.stdout.write(rendered)
|
sys.stdout.write(rendered)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
pip install .
|
pip install .
|
||||||
|
pip install psutil
|
||||||
if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then
|
if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then
|
||||||
# Python 2
|
# Python 2
|
||||||
pip install mercurial bzr
|
pip install mercurial bzr
|
||||||
|
@ -24,7 +24,7 @@ def urllib_read(query_url):
|
|||||||
elif query_url.startswith('http://freegeoip.net/json/'):
|
elif query_url.startswith('http://freegeoip.net/json/'):
|
||||||
return '{"city": "Meppen", "region_code": "06", "region_name": "Niedersachsen", "areacode": "", "ip": "82.145.55.16", "zipcode": "49716", "longitude": 7.3167, "country_name": "Germany", "country_code": "DE", "metrocode": "", "latitude": 52.6833}'
|
return '{"city": "Meppen", "region_code": "06", "region_name": "Niedersachsen", "areacode": "", "ip": "82.145.55.16", "zipcode": "49716", "longitude": 7.3167, "country_name": "Germany", "country_code": "DE", "metrocode": "", "latitude": 52.6833}'
|
||||||
elif query_url.startswith('http://query.yahooapis.com/v1/public/'):
|
elif query_url.startswith('http://query.yahooapis.com/v1/public/'):
|
||||||
return '{"query":{"count":1,"created":"2013-03-02T13:20:22Z","lang":"en-US","results":{"weather":{"rss":{"version":"2.0","geo":"http://www.w3.org/2003/01/geo/wgs84_pos#","yweather":"http://xml.weather.yahoo.com/ns/rss/1.0","channel":{"title":"Yahoo! Weather - Russia, RU","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","description":"Yahoo! Weather for Russia, RU","language":"en-us","lastBuildDate":"Sat, 02 Mar 2013 4:58 pm MSK","ttl":"60","location":{"city":"Russia","country":"Russia","region":""},"units":{"distance":"km","pressure":"mb","speed":"km/h","temperature":"C"},"wind":{"chill":"-11","direction":"0","speed":""},"atmosphere":{"humidity":"94","pressure":"1006.1","rising":"0","visibility":""},"astronomy":{"sunrise":"10:04 am","sunset":"7:57 pm"},"image":{"title":"Yahoo! Weather","width":"142","height":"18","link":"http://weather.yahoo.com","url":"http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"},"item":{"title":"Conditions for Russia, RU at 4:58 pm MSK","lat":"59.45","long":"108.83","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","pubDate":"Sat, 02 Mar 2013 4:58 pm MSK","condition":{"code":"30","date":"Sat, 02 Mar 2013 4:58 pm MSK","temp":"-11","text":"Partly Cloudy"},"description":"<img src=\"http://l.yimg.com/a/i/us/we/52/30.gif\"/><br />\n<b>Current Conditions:</b><br />\nPartly Cloudy, -11 C<BR />\n<BR /><b>Forecast:</b><BR />\nSat - Partly Cloudy. High: -9 Low: -19<br />\nSun - Partly Cloudy. High: -12 Low: -18<br />\n<br />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html\">Full Forecast at Yahoo! Weather</a><BR/><BR/>\n(provided by <a href=\"http://www.weather.com\" >The Weather Channel</a>)<br/>","forecast":[{"code":"29","date":"2 Mar 2013","day":"Sat","high":"-9","low":"-19","text":"Partly Cloudy"},{"code":"30","date":"3 Mar 2013","day":"Sun","high":"-12","low":"-18","text":"Partly Cloudy"}],"guid":{"isPermaLink":"false","content":"RSXX1511_2013_03_03_7_00_MSK"}}}}}}}}'
|
return r'{"query":{"count":1,"created":"2013-03-02T13:20:22Z","lang":"en-US","results":{"weather":{"rss":{"version":"2.0","geo":"http://www.w3.org/2003/01/geo/wgs84_pos#","yweather":"http://xml.weather.yahoo.com/ns/rss/1.0","channel":{"title":"Yahoo! Weather - Russia, RU","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","description":"Yahoo! Weather for Russia, RU","language":"en-us","lastBuildDate":"Sat, 02 Mar 2013 4:58 pm MSK","ttl":"60","location":{"city":"Russia","country":"Russia","region":""},"units":{"distance":"km","pressure":"mb","speed":"km/h","temperature":"C"},"wind":{"chill":"-11","direction":"0","speed":""},"atmosphere":{"humidity":"94","pressure":"1006.1","rising":"0","visibility":""},"astronomy":{"sunrise":"10:04 am","sunset":"7:57 pm"},"image":{"title":"Yahoo! Weather","width":"142","height":"18","link":"http://weather.yahoo.com","url":"http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"},"item":{"title":"Conditions for Russia, RU at 4:58 pm MSK","lat":"59.45","long":"108.83","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","pubDate":"Sat, 02 Mar 2013 4:58 pm MSK","condition":{"code":"30","date":"Sat, 02 Mar 2013 4:58 pm MSK","temp":"-11","text":"Partly Cloudy"},"description":"<img src=\"http://l.yimg.com/a/i/us/we/52/30.gif\"/><br />\n<b>Current Conditions:</b><br />\nPartly Cloudy, -11 C<BR />\n<BR /><b>Forecast:</b><BR />\nSat - Partly Cloudy. High: -9 Low: -19<br />\nSun - Partly Cloudy. High: -12 Low: -18<br />\n<br />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html\">Full Forecast at Yahoo! Weather</a><BR/><BR/>\n(provided by <a href=\"http://www.weather.com\" >The Weather Channel</a>)<br/>","forecast":[{"code":"29","date":"2 Mar 2013","day":"Sat","high":"-9","low":"-19","text":"Partly Cloudy"},{"code":"30","date":"3 Mar 2013","day":"Sun","high":"-12","low":"-18","text":"Partly Cloudy"}],"guid":{"isPermaLink":"false","content":"RSXX1511_2013_03_03_7_00_MSK"}}}}}}}}'
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@ -1,19 +1,12 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
: ${PYTHON:=python}
|
: ${PYTHON:=python}
|
||||||
FAILED=0
|
FAILED=0
|
||||||
if ${PYTHON} -c 'import sys; sys.exit(1 * (sys.version_info >= (2, 7)))' ; then
|
export PYTHONPATH="${PYTHONPATH}:`realpath .`"
|
||||||
# Python 2.6
|
for file in tests/test_*.py ; do
|
||||||
export PYTHONPATH="${PYTHONPATH}:`realpath .`"
|
|
||||||
for file in tests/test_*.py ; do
|
|
||||||
if ! ${PYTHON} $file ; then
|
if ! ${PYTHON} $file ; then
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
if ! ${PYTHON} setup.py test ; then
|
|
||||||
FAILED=1
|
FAILED=1
|
||||||
fi
|
fi
|
||||||
fi
|
done
|
||||||
if ! ${PYTHON} scripts/powerline-lint ; then
|
if ! ${PYTHON} scripts/powerline-lint ; then
|
||||||
FAILED=1
|
FAILED=1
|
||||||
fi
|
fi
|
||||||
|
@ -49,6 +49,7 @@ class TestConfig(TestCase):
|
|||||||
check_output(1, 0)
|
check_output(1, 0)
|
||||||
finally:
|
finally:
|
||||||
vim_module._start_mode('n')
|
vim_module._start_mode('n')
|
||||||
|
powerline.renderer.shutdown()
|
||||||
|
|
||||||
def test_tmux(self):
|
def test_tmux(self):
|
||||||
from powerline.segments import common
|
from powerline.segments import common
|
||||||
@ -56,16 +57,16 @@ class TestConfig(TestCase):
|
|||||||
reload(common)
|
reload(common)
|
||||||
from powerline.shell import ShellPowerline
|
from powerline.shell import ShellPowerline
|
||||||
with replace_module_attr(common, 'urllib_read', urllib_read):
|
with replace_module_attr(common, 'urllib_read', urllib_read):
|
||||||
ShellPowerline(Args(ext=['tmux'])).renderer.render()
|
ShellPowerline(Args(ext=['tmux']), run_once=True).renderer.render()
|
||||||
reload(common)
|
reload(common)
|
||||||
|
|
||||||
def test_zsh(self):
|
def test_zsh(self):
|
||||||
from powerline.shell import ShellPowerline
|
from powerline.shell import ShellPowerline
|
||||||
ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt')).renderer.render()
|
ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt'), run_once=True).renderer.render()
|
||||||
|
|
||||||
def test_bash(self):
|
def test_bash(self):
|
||||||
from powerline.shell import ShellPowerline
|
from powerline.shell import ShellPowerline
|
||||||
ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})])).renderer.render()
|
ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]), run_once=True).renderer.render()
|
||||||
|
|
||||||
def test_ipython(self):
|
def test_ipython(self):
|
||||||
from powerline.ipython import IpythonPowerline
|
from powerline.ipython import IpythonPowerline
|
||||||
@ -75,7 +76,9 @@ class TestConfig(TestCase):
|
|||||||
config_overrides = None
|
config_overrides = None
|
||||||
theme_overrides = {}
|
theme_overrides = {}
|
||||||
|
|
||||||
IpyPowerline().renderer.render()
|
powerline = IpyPowerline()
|
||||||
|
powerline.renderer.render()
|
||||||
|
powerline.renderer.shutdown()
|
||||||
|
|
||||||
def test_wm(self):
|
def test_wm(self):
|
||||||
from powerline.segments import common
|
from powerline.segments import common
|
||||||
@ -83,7 +86,7 @@ class TestConfig(TestCase):
|
|||||||
reload(common)
|
reload(common)
|
||||||
from powerline import Powerline
|
from powerline import Powerline
|
||||||
with replace_module_attr(common, 'urllib_read', urllib_read):
|
with replace_module_attr(common, 'urllib_read', urllib_read):
|
||||||
Powerline(ext='wm', renderer_module='pango_markup').renderer.render()
|
Powerline(ext='wm', renderer_module='pango_markup', run_once=True).renderer.render()
|
||||||
reload(common)
|
reload(common)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# vim:fileencoding=utf-8:noet
|
# vim:fileencoding=utf-8:noet
|
||||||
from powerline.lib import mergedicts, add_divider_highlight_group, humanize_bytes
|
from powerline.lib import mergedicts, add_divider_highlight_group
|
||||||
|
from powerline.lib.humanize_bytes import humanize_bytes
|
||||||
from powerline.lib.vcs import guess
|
from powerline.lib.vcs import guess
|
||||||
from subprocess import call, PIPE
|
from subprocess import call, PIPE
|
||||||
import os
|
import os
|
||||||
|
@ -4,7 +4,7 @@ from powerline.segments import shell, common
|
|||||||
import tests.vim as vim_module
|
import tests.vim as vim_module
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from tests.lib import Args, urllib_read, replace_module, replace_module_attr, new_module, replace_module_module, replace_env
|
from tests.lib import Args, urllib_read, replace_module_attr, new_module, replace_module_module, replace_env
|
||||||
from tests import TestCase
|
from tests import TestCase
|
||||||
|
|
||||||
|
|
||||||
@ -37,8 +37,10 @@ class TestCommon(TestCase):
|
|||||||
self.assertEqual(common.hostname(only_if_ssh=True), None)
|
self.assertEqual(common.hostname(only_if_ssh=True), None)
|
||||||
|
|
||||||
def test_user(self):
|
def test_user(self):
|
||||||
new_os = new_module('os', environ={'USER': 'def'})
|
new_os = new_module('os', environ={'USER': 'def'}, getpid=lambda: 1)
|
||||||
|
new_psutil = new_module('psutil', Process=lambda pid: Args(username='def'))
|
||||||
with replace_module_attr(common, 'os', new_os):
|
with replace_module_attr(common, 'os', new_os):
|
||||||
|
with replace_module_attr(common, 'psutil', new_psutil):
|
||||||
self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}])
|
self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}])
|
||||||
new_os.geteuid = lambda: 1
|
new_os.geteuid = lambda: 1
|
||||||
self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}])
|
self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}])
|
||||||
@ -128,12 +130,41 @@ class TestCommon(TestCase):
|
|||||||
self.assertEqual(common.external_ip(), [{'contents': '127.0.0.1', 'divider_highlight_group': 'background:divider'}])
|
self.assertEqual(common.external_ip(), [{'contents': '127.0.0.1', 'divider_highlight_group': 'background:divider'}])
|
||||||
|
|
||||||
def test_uptime(self):
|
def test_uptime(self):
|
||||||
# TODO
|
with replace_module_attr(common, '_get_uptime', lambda: 65536):
|
||||||
pass
|
self.assertEqual(common.uptime(), [{'contents': '0d 18h 12m', 'divider_highlight_group': 'background:divider'}])
|
||||||
|
|
||||||
|
def _get_uptime():
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
with replace_module_attr(common, '_get_uptime', _get_uptime):
|
||||||
|
self.assertEqual(common.uptime(), None)
|
||||||
|
|
||||||
def test_weather(self):
|
def test_weather(self):
|
||||||
# TODO
|
with replace_module_attr(common, 'urllib_read', urllib_read):
|
||||||
pass
|
self.assertEqual(common.weather(), [
|
||||||
|
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '},
|
||||||
|
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '-11°C'}
|
||||||
|
])
|
||||||
|
self.assertEqual(common.weather(icons={'cloudy': 'o'}), [
|
||||||
|
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'o '},
|
||||||
|
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '-11°C'}
|
||||||
|
])
|
||||||
|
self.assertEqual(common.weather(icons={'partly_cloudy_day': 'x'}), [
|
||||||
|
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'x '},
|
||||||
|
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '-11°C'}
|
||||||
|
])
|
||||||
|
self.assertEqual(common.weather(unit='F'), [
|
||||||
|
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '},
|
||||||
|
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '12°F'}
|
||||||
|
])
|
||||||
|
self.assertEqual(common.weather(unit='K'), [
|
||||||
|
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '},
|
||||||
|
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '262K'}
|
||||||
|
])
|
||||||
|
self.assertEqual(common.weather(temperature_format='{temp:.1e}C'), [
|
||||||
|
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': '☁ '},
|
||||||
|
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_cold', 'weather_temp', 'weather'], 'contents': '-1.1e+01C'}
|
||||||
|
])
|
||||||
|
|
||||||
def test_system_load(self):
|
def test_system_load(self):
|
||||||
with replace_module_module(common, 'os', getloadavg=lambda: (7.5, 3.5, 1.5)):
|
with replace_module_module(common, 'os', getloadavg=lambda: (7.5, 3.5, 1.5)):
|
||||||
@ -148,12 +179,39 @@ class TestCommon(TestCase):
|
|||||||
{'contents': '2', 'highlight_group': ['system_load_bad', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}])
|
{'contents': '2', 'highlight_group': ['system_load_bad', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}])
|
||||||
|
|
||||||
def test_cpu_load_percent(self):
|
def test_cpu_load_percent(self):
|
||||||
with replace_module('psutil', cpu_percent=lambda **kwargs: 52.3):
|
with replace_module_module(common, 'psutil', cpu_percent=lambda **kwargs: 52.3):
|
||||||
self.assertEqual(common.cpu_load_percent(), '52%')
|
self.assertEqual(common.cpu_load_percent(), '52%')
|
||||||
|
|
||||||
def test_network_load(self):
|
def test_network_load(self):
|
||||||
# TODO
|
def _get_bytes(interface):
|
||||||
pass
|
return None
|
||||||
|
with replace_module_attr(common, '_get_bytes', _get_bytes):
|
||||||
|
self.assertEqual(common.network_load(), None)
|
||||||
|
l = [0, 0]
|
||||||
|
|
||||||
|
def _get_bytes2(interface):
|
||||||
|
l[0] += 1200
|
||||||
|
l[1] += 2400
|
||||||
|
return tuple(l)
|
||||||
|
|
||||||
|
from imp import reload
|
||||||
|
reload(common)
|
||||||
|
with replace_module_attr(common, '_get_bytes', _get_bytes2):
|
||||||
|
common.network_load.startup()
|
||||||
|
common.network_load.sleep(0)
|
||||||
|
common.network_load.sleep(0)
|
||||||
|
self.assertEqual(common.network_load(), [
|
||||||
|
{'divider_highlight_group': 'background:divider', 'contents': '⬇ 1 KiB/s ⬆ 2 KiB/s'}
|
||||||
|
])
|
||||||
|
self.assertEqual(common.network_load(format='r {recv} s {sent}'), [
|
||||||
|
{'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s s 2 KiB/s'}
|
||||||
|
])
|
||||||
|
self.assertEqual(common.network_load(format='r {recv} s {sent}', suffix='bps'), [
|
||||||
|
{'divider_highlight_group': 'background:divider', 'contents': 'r 1 Kibps s 2 Kibps'}
|
||||||
|
])
|
||||||
|
self.assertEqual(common.network_load(format='r {recv} s {sent}', si_prefix=True), [
|
||||||
|
{'divider_highlight_group': 'background:divider', 'contents': 'r 1 kB/s s 2 kB/s'}
|
||||||
|
])
|
||||||
|
|
||||||
def test_virtualenv(self):
|
def test_virtualenv(self):
|
||||||
with replace_env('VIRTUAL_ENV', '/abc/def/ghi'):
|
with replace_env('VIRTUAL_ENV', '/abc/def/ghi'):
|
||||||
@ -229,7 +287,7 @@ class TestVim(TestCase):
|
|||||||
|
|
||||||
def test_file_size(self):
|
def test_file_size(self):
|
||||||
segment_info = vim_module._get_segment_info()
|
segment_info = vim_module._get_segment_info()
|
||||||
self.assertEqual(vim.file_size(segment_info=segment_info), None)
|
self.assertEqual(vim.file_size(segment_info=segment_info), '0 B')
|
||||||
with vim_module._with('buffer', os.path.join(os.path.dirname(__file__), 'empty')) as segment_info:
|
with vim_module._with('buffer', os.path.join(os.path.dirname(__file__), 'empty')) as segment_info:
|
||||||
self.assertEqual(vim.file_size(segment_info=segment_info), '0 B')
|
self.assertEqual(vim.file_size(segment_info=segment_info), '0 B')
|
||||||
|
|
||||||
@ -267,16 +325,36 @@ class TestVim(TestCase):
|
|||||||
self.assertEqual(vim.modified_buffers(), None)
|
self.assertEqual(vim.modified_buffers(), None)
|
||||||
|
|
||||||
def test_branch(self):
|
def test_branch(self):
|
||||||
# TODO
|
with vim_module._with('buffer', '/foo') as segment_info:
|
||||||
pass
|
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)):
|
||||||
|
self.assertEqual(vim.branch(segment_info=segment_info),
|
||||||
|
[{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}])
|
||||||
|
self.assertEqual(vim.branch(segment_info=segment_info, status_colors=True),
|
||||||
|
[{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_clean', 'branch'], 'contents': 'foo'}])
|
||||||
|
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)):
|
||||||
|
self.assertEqual(vim.branch(segment_info=segment_info),
|
||||||
|
[{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}])
|
||||||
|
self.assertEqual(vim.branch(segment_info=segment_info, status_colors=True),
|
||||||
|
[{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_dirty', 'branch'], 'contents': 'foo'}])
|
||||||
|
|
||||||
def test_file_vcs_status(self):
|
def test_file_vcs_status(self):
|
||||||
# TODO
|
with vim_module._with('buffer', '/foo') as segment_info:
|
||||||
pass
|
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)):
|
||||||
|
self.assertEqual(vim.file_vcs_status(segment_info=segment_info),
|
||||||
|
[{'highlight_group': ['file_vcs_status_M', 'file_vcs_status'], 'contents': 'M'}])
|
||||||
|
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: None, directory=path)):
|
||||||
|
self.assertEqual(vim.file_vcs_status(segment_info=segment_info), None)
|
||||||
|
with vim_module._with('buffer', '/bar') as segment_info:
|
||||||
|
with vim_module._with('bufoptions', buftype='nofile'):
|
||||||
|
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)):
|
||||||
|
self.assertEqual(vim.file_vcs_status(segment_info=segment_info), None)
|
||||||
|
|
||||||
def test_repository_status(self):
|
def test_repository_status(self):
|
||||||
# TODO
|
segment_info = vim_module._get_segment_info()
|
||||||
pass
|
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)):
|
||||||
|
self.assertEqual(vim.repository_status(segment_info=segment_info), None)
|
||||||
|
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)):
|
||||||
|
self.assertEqual(vim.repository_status(segment_info=segment_info), 'DU')
|
||||||
|
|
||||||
|
|
||||||
old_cwd = None
|
old_cwd = None
|
||||||
|
16
tests/vim.py
16
tests/vim.py
@ -150,6 +150,13 @@ def _emul_exists(varname):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
@_logged
|
||||||
|
def _emul_line2byte(line):
|
||||||
|
buflines = _buf_lines[_buffer()]
|
||||||
|
if line == len(buflines) + 1:
|
||||||
|
return sum((len(s) for s in buflines)) + 1
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
_window_ids = [None]
|
_window_ids = [None]
|
||||||
_window_id = 0
|
_window_id = 0
|
||||||
_win_scopes = [None]
|
_win_scopes = [None]
|
||||||
@ -242,6 +249,15 @@ class _Buffer(object):
|
|||||||
_buf_scopes.pop(bufnr)
|
_buf_scopes.pop(bufnr)
|
||||||
|
|
||||||
|
|
||||||
|
class _Current(object):
|
||||||
|
@property
|
||||||
|
def buffer(self):
|
||||||
|
return buffers[_buffer()]
|
||||||
|
|
||||||
|
|
||||||
|
current = _Current()
|
||||||
|
|
||||||
|
|
||||||
_dict = None
|
_dict = None
|
||||||
|
|
||||||
|
|
||||||
|
646
tools/colors.map
Normal file
646
tools/colors.map
Normal file
@ -0,0 +1,646 @@
|
|||||||
|
Grey 545454
|
||||||
|
Grey, Silver C0C0C0
|
||||||
|
grey BEBEBE
|
||||||
|
LightGray D3D3D3
|
||||||
|
LightSlateGrey 778899
|
||||||
|
SlateGray 708090
|
||||||
|
SlateGray1 C6E2FF
|
||||||
|
SlateGray2 B9D3EE
|
||||||
|
SlateGray3 9FB6CD
|
||||||
|
SlateGray4 6C7B8B
|
||||||
|
black 000000
|
||||||
|
grey0 000000
|
||||||
|
grey1 030303
|
||||||
|
grey2 050505
|
||||||
|
grey3 080808
|
||||||
|
grey4 0A0A0A
|
||||||
|
grey5 0D0D0D
|
||||||
|
grey6 0F0F0F
|
||||||
|
grey7 121212
|
||||||
|
grey8 141414
|
||||||
|
grey9 171717
|
||||||
|
grey10 1A1A1A
|
||||||
|
grey11 1C1C1C
|
||||||
|
grey12 1F1F1F
|
||||||
|
grey13 212121
|
||||||
|
grey14 242424
|
||||||
|
grey15 262626
|
||||||
|
grey16 292929
|
||||||
|
grey17 2B2B2B
|
||||||
|
grey18 2E2E2E
|
||||||
|
grey19 303030
|
||||||
|
grey20 333333
|
||||||
|
grey21 363636
|
||||||
|
grey22 383838
|
||||||
|
grey23 3B3B3B
|
||||||
|
grey24 3D3D3D
|
||||||
|
grey25 404040
|
||||||
|
grey26 424242
|
||||||
|
grey27 454545
|
||||||
|
grey28 474747
|
||||||
|
grey29 4A4A4A
|
||||||
|
grey30 4D4D4D
|
||||||
|
grey31 4F4F4F
|
||||||
|
grey32 525252
|
||||||
|
grey33 545454
|
||||||
|
grey34 575757
|
||||||
|
grey35 595959
|
||||||
|
grey36 5C5C5C
|
||||||
|
grey37 5E5E5E
|
||||||
|
grey38 616161
|
||||||
|
grey39 636363
|
||||||
|
grey40 666666
|
||||||
|
grey41, DimGrey 696969
|
||||||
|
grey42 6B6B6B
|
||||||
|
grey43 6E6E6E
|
||||||
|
grey44 707070
|
||||||
|
grey45 737373
|
||||||
|
grey46 757575
|
||||||
|
grey47 787878
|
||||||
|
grey48 7A7A7A
|
||||||
|
grey49 7D7D7D
|
||||||
|
grey50 7F7F7F
|
||||||
|
grey51 828282
|
||||||
|
grey52 858585
|
||||||
|
grey53 878787
|
||||||
|
grey54 8A8A8A
|
||||||
|
grey55 8C8C8C
|
||||||
|
grey56 8F8F8F
|
||||||
|
grey57 919191
|
||||||
|
grey58 949494
|
||||||
|
grey59 969696
|
||||||
|
grey60 999999
|
||||||
|
grey61 9C9C9C
|
||||||
|
grey62 9E9E9E
|
||||||
|
grey63 A1A1A1
|
||||||
|
grey64 A3A3A3
|
||||||
|
grey65 A6A6A6
|
||||||
|
grey66 A8A8A8
|
||||||
|
grey67 ABABAB
|
||||||
|
grey68 ADADAD
|
||||||
|
grey69 B0B0B0
|
||||||
|
grey70 B3B3B3
|
||||||
|
grey71 B5B5B5
|
||||||
|
grey72 B8B8B8
|
||||||
|
grey73 BABABA
|
||||||
|
grey74 BDBDBD
|
||||||
|
grey75 BFBFBF
|
||||||
|
grey76 C2C2C2
|
||||||
|
grey77 C4C4C4
|
||||||
|
grey78 C7C7C7
|
||||||
|
grey79 C9C9C9
|
||||||
|
grey80 CCCCCC
|
||||||
|
grey81 CFCFCF
|
||||||
|
grey82 D1D1D1
|
||||||
|
grey83 D4D4D4
|
||||||
|
grey84 D6D6D6
|
||||||
|
grey85 D9D9D9
|
||||||
|
grey86 DBDBDB
|
||||||
|
grey87 DEDEDE
|
||||||
|
grey88 E0E0E0
|
||||||
|
grey89 E3E3E3
|
||||||
|
grey90 E5E5E5
|
||||||
|
grey91 E8E8E8
|
||||||
|
grey92 EBEBEB
|
||||||
|
grey93 EDEDED
|
||||||
|
grey94 F0F0F0
|
||||||
|
grey95 F2F2F2
|
||||||
|
grey96 F5F5F5
|
||||||
|
grey97 F7F7F7
|
||||||
|
grey98 FAFAFA
|
||||||
|
grey99 FCFCFC
|
||||||
|
grey100, White FFFFFF
|
||||||
|
Dark Slate Grey 2F4F4F
|
||||||
|
Dim Grey 545454
|
||||||
|
Very Light Grey CDCDCD
|
||||||
|
Free Speech Grey 635688
|
||||||
|
AliceBlue F0F8FF
|
||||||
|
BlueViolet 8A2BE2
|
||||||
|
Cadet Blue 5F9F9F
|
||||||
|
CadetBlue 5F9EA0
|
||||||
|
CadetBlue 5F9EA0
|
||||||
|
CadetBlue1 98F5FF
|
||||||
|
CadetBlue2 8EE5EE
|
||||||
|
CadetBlue3 7AC5CD
|
||||||
|
CadetBlue4 53868B
|
||||||
|
Corn Flower Blue 42426F
|
||||||
|
CornflowerBlue 6495ED
|
||||||
|
DarkSlateBlue 483D8B
|
||||||
|
DarkTurquoise 00CED1
|
||||||
|
DeepSkyBlue 00BFFF
|
||||||
|
DeepSkyBlue1 00BFFF
|
||||||
|
DeepSkyBlue2 00B2EE
|
||||||
|
DeepSkyBlue3 009ACD
|
||||||
|
DeepSkyBlue4 00688B
|
||||||
|
DodgerBlue 1E90FF
|
||||||
|
DodgerBlue1 1E90FF
|
||||||
|
DodgerBlue2 1C86EE
|
||||||
|
DodgerBlue3 1874CD
|
||||||
|
DodgerBlue4 104E8B
|
||||||
|
LightBlue ADD8E6
|
||||||
|
LightBlue1 BFEFFF
|
||||||
|
LightBlue2 B2DFEE
|
||||||
|
LightBlue3 9AC0CD
|
||||||
|
LightBlue4 68838B
|
||||||
|
LightCyan E0FFFF
|
||||||
|
LightCyan1 E0FFFF
|
||||||
|
LightCyan2 D1EEEE
|
||||||
|
LightCyan3 B4CDCD
|
||||||
|
LightCyan4 7A8B8B
|
||||||
|
LightSkyBlue 87CEFA
|
||||||
|
LightSkyBlue1 B0E2FF
|
||||||
|
LightSkyBlue2 A4D3EE
|
||||||
|
LightSkyBlue3 8DB6CD
|
||||||
|
LightSkyBlue4 607B8B
|
||||||
|
LightSlateBlue 8470FF
|
||||||
|
LightSteelBlue B0C4DE
|
||||||
|
LightSteelBlue1 CAE1FF
|
||||||
|
LightSteelBlue2 BCD2EE
|
||||||
|
LightSteelBlue3 A2B5CD
|
||||||
|
LightSteelBlue4 6E7B8B
|
||||||
|
Aquamarine 70DB93
|
||||||
|
MediumBlue 0000CD
|
||||||
|
MediumSlateBlue 7B68EE
|
||||||
|
MediumTurquoise 48D1CC
|
||||||
|
MidnightBlue 191970
|
||||||
|
NavyBlue 000080
|
||||||
|
PaleTurquoise AFEEEE
|
||||||
|
PaleTurquoise1 BBFFFF
|
||||||
|
PaleTurquoise2 AEEEEE
|
||||||
|
PaleTurquoise3 96CDCD
|
||||||
|
PaleTurquoise4 668B8B
|
||||||
|
PowderBlue B0E0E6
|
||||||
|
RoyalBlue 4169E1
|
||||||
|
RoyalBlue1 4876FF
|
||||||
|
RoyalBlue2 436EEE
|
||||||
|
RoyalBlue3 3A5FCD
|
||||||
|
RoyalBlue4 27408B
|
||||||
|
RoyalBlue5 002266
|
||||||
|
SkyBlue 87CEEB
|
||||||
|
SkyBlue1 87CEFF
|
||||||
|
SkyBlue2 7EC0EE
|
||||||
|
SkyBlue3 6CA6CD
|
||||||
|
SkyBlue4 4A708B
|
||||||
|
SlateBlue 6A5ACD
|
||||||
|
SlateBlue1 836FFF
|
||||||
|
SlateBlue2 7A67EE
|
||||||
|
SlateBlue3 6959CD
|
||||||
|
SlateBlue4 473C8B
|
||||||
|
SteelBlue 4682B4
|
||||||
|
SteelBlue1 63B8FF
|
||||||
|
SteelBlue2 5CACEE
|
||||||
|
SteelBlue3 4F94CD
|
||||||
|
SteelBlue4 36648B
|
||||||
|
aquamarine 7FFFD4
|
||||||
|
aquamarine1 7FFFD4
|
||||||
|
aquamarine2 76EEC6
|
||||||
|
aquamarine3, MediumAquamarine 66CDAA
|
||||||
|
aquamarine4 458B74
|
||||||
|
azure F0FFFF
|
||||||
|
azure1 F0FFFF
|
||||||
|
azure2 E0EEEE
|
||||||
|
azure3 C1CDCD
|
||||||
|
azure4 838B8B
|
||||||
|
blue 0000FF
|
||||||
|
blue1 0000FF
|
||||||
|
blue2 0000EE
|
||||||
|
blue3 0000CD
|
||||||
|
blue4 00008B
|
||||||
|
aqua 00FFFF
|
||||||
|
True Iris Blue 03B4CC
|
||||||
|
cyan 00FFFF
|
||||||
|
cyan1 00FFFF
|
||||||
|
cyan2 00EEEE
|
||||||
|
cyan3 00CDCD
|
||||||
|
cyan4 008B8B
|
||||||
|
navy 000080
|
||||||
|
teal 008080
|
||||||
|
turquoise 40E0D0
|
||||||
|
turquoise1 00F5FF
|
||||||
|
turquoise2 00E5EE
|
||||||
|
turquoise3 00C5CD
|
||||||
|
turquoise4 00868B
|
||||||
|
DarkSlateGray 2F4F4F
|
||||||
|
DarkSlateGray1 97FFFF
|
||||||
|
DarkSlateGray2 8DEEEE
|
||||||
|
DarkSlateGray3 79CDCD
|
||||||
|
DarkSlateGray4 528B8B
|
||||||
|
Dark Slate Blue 241882
|
||||||
|
Dark Turquoise 7093DB
|
||||||
|
Medium Slate Blue 7F00FF
|
||||||
|
Medium Turquoise 70DBDB
|
||||||
|
Midnight Blue 2F2F4F
|
||||||
|
Navy Blue 23238E
|
||||||
|
Neon Blue 4D4DFF
|
||||||
|
New Midnight Blue 00009C
|
||||||
|
Rich Blue 5959AB
|
||||||
|
Sky Blue 3299CC
|
||||||
|
Slate Blue 007FFF
|
||||||
|
Summer Sky 38B0DE
|
||||||
|
Iris Blue 03B4C8
|
||||||
|
Free Speech Blue 4156C5
|
||||||
|
RosyBrown BC8F8F
|
||||||
|
RosyBrown1 FFC1C1
|
||||||
|
RosyBrown2 EEB4B4
|
||||||
|
RosyBrown3 CD9B9B
|
||||||
|
RosyBrown4 8B6969
|
||||||
|
SaddleBrown 8B4513
|
||||||
|
SandyBrown F4A460
|
||||||
|
beige F5F5DC
|
||||||
|
brown A52A2A
|
||||||
|
brown A62A2A
|
||||||
|
brown1 FF4040
|
||||||
|
brown2 EE3B3B
|
||||||
|
brown3 CD3333
|
||||||
|
brown4 8B2323
|
||||||
|
dark brown 5C4033
|
||||||
|
burlywood DEB887
|
||||||
|
burlywood1 FFD39B
|
||||||
|
burlywood2 EEC591
|
||||||
|
burlywood3 CDAA7D
|
||||||
|
burlywood4 8B7355
|
||||||
|
baker's chocolate 5C3317
|
||||||
|
chocolate D2691E
|
||||||
|
chocolate1 FF7F24
|
||||||
|
chocolate2 EE7621
|
||||||
|
chocolate3 CD661D
|
||||||
|
chocolate4 8B4513
|
||||||
|
peru CD853F
|
||||||
|
tan D2B48C
|
||||||
|
tan1 FFA54F
|
||||||
|
tan2 EE9A49
|
||||||
|
tan3 CD853F
|
||||||
|
tan4 8B5A2B
|
||||||
|
Dark Tan 97694F
|
||||||
|
Dark Wood 855E42
|
||||||
|
Light Wood 856363
|
||||||
|
Medium Wood A68064
|
||||||
|
New Tan EBC79E
|
||||||
|
Semi-Sweet Chocolate 6B4226
|
||||||
|
Sienna 8E6B23
|
||||||
|
Tan DB9370
|
||||||
|
Very Dark Brown 5C4033
|
||||||
|
Dark Green 2F4F2F
|
||||||
|
DarkGreen 006400
|
||||||
|
dark green copper 4A766E
|
||||||
|
DarkKhaki BDB76B
|
||||||
|
DarkOliveGreen 556B2F
|
||||||
|
DarkOliveGreen1 CAFF70
|
||||||
|
DarkOliveGreen2 BCEE68
|
||||||
|
DarkOliveGreen3 A2CD5A
|
||||||
|
DarkOliveGreen4 6E8B3D
|
||||||
|
olive 808000
|
||||||
|
DarkSeaGreen 8FBC8F
|
||||||
|
DarkSeaGreen1 C1FFC1
|
||||||
|
DarkSeaGreen2 B4EEB4
|
||||||
|
DarkSeaGreen3 9BCD9B
|
||||||
|
DarkSeaGreen4 698B69
|
||||||
|
ForestGreen 228B22
|
||||||
|
GreenYellow ADFF2F
|
||||||
|
LawnGreen 7CFC00
|
||||||
|
LightSeaGreen 20B2AA
|
||||||
|
LimeGreen 32CD32
|
||||||
|
MediumSeaGreen 3CB371
|
||||||
|
MediumSpringGreen 00FA9A
|
||||||
|
MintCream F5FFFA
|
||||||
|
OliveDrab 6B8E23
|
||||||
|
OliveDrab1 C0FF3E
|
||||||
|
OliveDrab2 B3EE3A
|
||||||
|
OliveDrab3 9ACD32
|
||||||
|
OliveDrab4 698B22
|
||||||
|
PaleGreen 98FB98
|
||||||
|
PaleGreen1 9AFF9A
|
||||||
|
PaleGreen2 90EE90
|
||||||
|
PaleGreen3 7CCD7C
|
||||||
|
PaleGreen4 548B54
|
||||||
|
SeaGreen, SeaGreen4 2E8B57
|
||||||
|
SeaGreen1 54FF9F
|
||||||
|
SeaGreen2 4EEE94
|
||||||
|
SeaGreen3 43CD80
|
||||||
|
SpringGreen 00FF7F
|
||||||
|
SpringGreen1 00FF7F
|
||||||
|
SpringGreen2 00EE76
|
||||||
|
SpringGreen3 00CD66
|
||||||
|
SpringGreen4 008B45
|
||||||
|
YellowGreen 9ACD32
|
||||||
|
chartreuse 7FFF00
|
||||||
|
chartreuse1 7FFF00
|
||||||
|
chartreuse2 76EE00
|
||||||
|
chartreuse3 66CD00
|
||||||
|
chartreuse4 458B00
|
||||||
|
green 00FF00
|
||||||
|
green 008000
|
||||||
|
lime 00FF00
|
||||||
|
green1 00FF00
|
||||||
|
green2 00EE00
|
||||||
|
green3 00CD00
|
||||||
|
green4 008B00
|
||||||
|
khaki F0E68C
|
||||||
|
khaki1 FFF68F
|
||||||
|
khaki2 EEE685
|
||||||
|
khaki3 CDC673
|
||||||
|
khaki4 8B864E
|
||||||
|
Dark Olive Green 4F4F2F
|
||||||
|
Green Yellow <a href=#sic>[sic]</a> D19275
|
||||||
|
Hunter Green <a href=#sic>[sic]</a> 8E2323
|
||||||
|
Forest Green, Khaki, Medium Aquamarine 238E23
|
||||||
|
Medium Forest Green DBDB70
|
||||||
|
Medium Sea Green 426F42
|
||||||
|
Medium Spring Green 7FFF00
|
||||||
|
Pale Green 8FBC8F
|
||||||
|
Sea Green 238E68
|
||||||
|
Spring Green 00FF7F
|
||||||
|
Free Speech Green 09F911
|
||||||
|
Free Speech Aquamarine 029D74
|
||||||
|
DarkOrange FF8C00
|
||||||
|
DarkOrange1 FF7F00
|
||||||
|
DarkOrange2 EE7600
|
||||||
|
DarkOrange3 CD6600
|
||||||
|
DarkOrange4 8B4500
|
||||||
|
DarkSalmon E9967A
|
||||||
|
LightCoral F08080
|
||||||
|
LightSalmon FFA07A
|
||||||
|
LightSalmon1 FFA07A
|
||||||
|
LightSalmon2 EE9572
|
||||||
|
LightSalmon3 CD8162
|
||||||
|
LightSalmon4 8B5742
|
||||||
|
PeachPuff FFDAB9
|
||||||
|
PeachPuff1 FFDAB9
|
||||||
|
PeachPuff2 EECBAD
|
||||||
|
PeachPuff3 CDAF95
|
||||||
|
PeachPuff4 8B7765
|
||||||
|
bisque FFE4C4
|
||||||
|
bisque1 FFE4C4
|
||||||
|
bisque2 EED5B7
|
||||||
|
bisque3 CDB79E
|
||||||
|
bisque4 8B7D6B
|
||||||
|
coral FF7F00
|
||||||
|
coral FF7F50
|
||||||
|
coral1 FF7256
|
||||||
|
coral2 EE6A50
|
||||||
|
coral3 CD5B45
|
||||||
|
coral4 8B3E2F
|
||||||
|
honeydew F0FFF0
|
||||||
|
honeydew1 F0FFF0
|
||||||
|
honeydew2 E0EEE0
|
||||||
|
honeydew3 C1CDC1
|
||||||
|
honeydew4 838B83
|
||||||
|
orange FFA500
|
||||||
|
orange1 FFA500
|
||||||
|
orange2 EE9A00
|
||||||
|
orange3 CD8500
|
||||||
|
orange4 8B5A00
|
||||||
|
salmon FA8072
|
||||||
|
salmon1 FF8C69
|
||||||
|
salmon2 EE8262
|
||||||
|
salmon3 CD7054
|
||||||
|
salmon4 8B4C39
|
||||||
|
sienna A0522D
|
||||||
|
sienna1 FF8247
|
||||||
|
sienna2 EE7942
|
||||||
|
sienna3 CD6839
|
||||||
|
sienna4 8B4726
|
||||||
|
Mandarian Orange 8E2323
|
||||||
|
Orange FF7F00
|
||||||
|
Orange Red FF2400
|
||||||
|
DeepPink FF1493
|
||||||
|
DeepPink1 FF1493
|
||||||
|
DeepPink2 EE1289
|
||||||
|
DeepPink3 CD1076
|
||||||
|
DeepPink4 8B0A50
|
||||||
|
HotPink FF69B4
|
||||||
|
HotPink1 FF6EB4
|
||||||
|
HotPink2 EE6AA7
|
||||||
|
HotPink3 CD6090
|
||||||
|
HotPink4 8B3A62
|
||||||
|
IndianRed CD5C5C
|
||||||
|
IndianRed1 FF6A6A
|
||||||
|
IndianRed2 EE6363
|
||||||
|
IndianRed3 CD5555
|
||||||
|
IndianRed4 8B3A3A
|
||||||
|
LightPink FFB6C1
|
||||||
|
LightPink1 FFAEB9
|
||||||
|
LightPink2 EEA2AD
|
||||||
|
LightPink3 CD8C95
|
||||||
|
LightPink4 8B5F65
|
||||||
|
MediumVioletRed C71585
|
||||||
|
MistyRose FFE4E1
|
||||||
|
MistyRose1 FFE4E1
|
||||||
|
MistyRose2 EED5D2
|
||||||
|
MistyRose3 CDB7B5
|
||||||
|
MistyRose4 8B7D7B
|
||||||
|
OrangeRed FF4500
|
||||||
|
OrangeRed1 FF4500
|
||||||
|
OrangeRed2 EE4000
|
||||||
|
OrangeRed3 CD3700
|
||||||
|
OrangeRed4 8B2500
|
||||||
|
PaleVioletRed DB7093
|
||||||
|
PaleVioletRed1 FF82AB
|
||||||
|
PaleVioletRed2 EE799F
|
||||||
|
PaleVioletRed3 CD6889
|
||||||
|
PaleVioletRed4 8B475D
|
||||||
|
VioletRed D02090
|
||||||
|
VioletRed1 FF3E96
|
||||||
|
VioletRed2 EE3A8C
|
||||||
|
VioletRed3 CD3278
|
||||||
|
VioletRed4 8B2252
|
||||||
|
firebrick B22222
|
||||||
|
firebrick1 FF3030
|
||||||
|
firebrick2 EE2C2C
|
||||||
|
firebrick3 CD2626
|
||||||
|
firebrick4 8B1A1A
|
||||||
|
pink FFC0CB
|
||||||
|
pink1 FFB5C5
|
||||||
|
pink2 EEA9B8
|
||||||
|
pink3 CD919E
|
||||||
|
pink4 8B636C
|
||||||
|
Flesh F5CCB0
|
||||||
|
Feldspar D19275
|
||||||
|
red FF0000
|
||||||
|
red1 FF0000
|
||||||
|
red2 EE0000
|
||||||
|
red3 CD0000
|
||||||
|
red4 8B0000
|
||||||
|
tomato FF6347
|
||||||
|
tomato1 FF6347
|
||||||
|
tomato2 EE5C42
|
||||||
|
tomato3 CD4F39
|
||||||
|
tomato4 8B3626
|
||||||
|
Dusty Rose 856363
|
||||||
|
Firebrick 8E2323
|
||||||
|
Indian Red F5CCB0
|
||||||
|
Pink BC8F8F
|
||||||
|
Salmon 6F4242
|
||||||
|
Scarlet 8C1717
|
||||||
|
Spicy Pink FF1CAE
|
||||||
|
Free Speech Magenta E35BD8
|
||||||
|
Free Speech Red C00000
|
||||||
|
DarkOrchid 9932CC
|
||||||
|
DarkOrchid1 BF3EFF
|
||||||
|
DarkOrchid2 B23AEE
|
||||||
|
DarkOrchid3 9A32CD
|
||||||
|
DarkOrchid4 68228B
|
||||||
|
DarkViolet 9400D3
|
||||||
|
LavenderBlush FFF0F5
|
||||||
|
LavenderBlush1 FFF0F5
|
||||||
|
LavenderBlush2 EEE0E5
|
||||||
|
LavenderBlush3 CDC1C5
|
||||||
|
LavenderBlush4 8B8386
|
||||||
|
MediumOrchid BA55D3
|
||||||
|
MediumOrchid1 E066FF
|
||||||
|
MediumOrchid2 D15FEE
|
||||||
|
MediumOrchid3 B452CD
|
||||||
|
MediumOrchid4 7A378B
|
||||||
|
MediumPurple 9370DB
|
||||||
|
Medium Orchid 9370DB
|
||||||
|
MediumPurple1 AB82FF
|
||||||
|
Dark Orchid 9932CD
|
||||||
|
MediumPurple2 9F79EE
|
||||||
|
MediumPurple3 8968CD
|
||||||
|
MediumPurple4 5D478B
|
||||||
|
lavender E6E6FA
|
||||||
|
magenta FF00FF
|
||||||
|
fuchsia FF00FF
|
||||||
|
magenta1 FF00FF
|
||||||
|
magenta2 EE00EE
|
||||||
|
magenta3 CD00CD
|
||||||
|
magenta4 8B008B
|
||||||
|
maroon B03060
|
||||||
|
maroon1 FF34B3
|
||||||
|
maroon2 EE30A7
|
||||||
|
maroon3 CD2990
|
||||||
|
maroon4 8B1C62
|
||||||
|
orchid DA70D6
|
||||||
|
Orchid DB70DB
|
||||||
|
orchid1 FF83FA
|
||||||
|
orchid2 EE7AE9
|
||||||
|
orchid3 CD69C9
|
||||||
|
orchid4 8B4789
|
||||||
|
plum DDA0DD
|
||||||
|
plum1 FFBBFF
|
||||||
|
plum2 EEAEEE
|
||||||
|
plum3 CD96CD
|
||||||
|
plum4 8B668B
|
||||||
|
purple A020F0
|
||||||
|
purple 800080
|
||||||
|
purple1 9B30FF
|
||||||
|
purple2 912CEE
|
||||||
|
purple3 7D26CD
|
||||||
|
purple4 551A8B
|
||||||
|
thistle D8BFD8
|
||||||
|
thistle1 FFE1FF
|
||||||
|
thistle2 EED2EE
|
||||||
|
thistle3 CDB5CD
|
||||||
|
thistle4 8B7B8B
|
||||||
|
violet EE82EE
|
||||||
|
violet blue 9F5F9F
|
||||||
|
Dark Purple 871F78
|
||||||
|
Maroon 800000
|
||||||
|
Medium Violet Red DB7093
|
||||||
|
Neon Pink FF6EC7
|
||||||
|
Plum EAADEA
|
||||||
|
Thistle D8BFD8
|
||||||
|
Turquoise ADEAEA
|
||||||
|
Violet 4F2F4F
|
||||||
|
Violet Red CC3299
|
||||||
|
AntiqueWhite FAEBD7
|
||||||
|
AntiqueWhite1 FFEFDB
|
||||||
|
AntiqueWhite2 EEDFCC
|
||||||
|
AntiqueWhite3 CDC0B0
|
||||||
|
AntiqueWhite4 8B8378
|
||||||
|
FloralWhite FFFAF0
|
||||||
|
GhostWhite F8F8FF
|
||||||
|
NavajoWhite FFDEAD
|
||||||
|
NavajoWhite1 FFDEAD
|
||||||
|
NavajoWhite2 EECFA1
|
||||||
|
NavajoWhite3 CDB38B
|
||||||
|
NavajoWhite4 8B795E
|
||||||
|
OldLace FDF5E6
|
||||||
|
WhiteSmoke F5F5F5
|
||||||
|
gainsboro DCDCDC
|
||||||
|
ivory FFFFF0
|
||||||
|
ivory1 FFFFF0
|
||||||
|
ivory2 EEEEE0
|
||||||
|
ivory3 CDCDC1
|
||||||
|
ivory4 8B8B83
|
||||||
|
linen FAF0E6
|
||||||
|
seashell FFF5EE
|
||||||
|
seashell1 FFF5EE
|
||||||
|
seashell2 EEE5DE
|
||||||
|
seashell3 CDC5BF
|
||||||
|
seashell4 8B8682
|
||||||
|
snow FFFAFA
|
||||||
|
snow1 FFFAFA
|
||||||
|
snow2 EEE9E9
|
||||||
|
snow3 CDC9C9
|
||||||
|
snow4 8B8989
|
||||||
|
wheat F5DEB3
|
||||||
|
wheat1 FFE7BA
|
||||||
|
wheat2 EED8AE
|
||||||
|
wheat3 CDBA96
|
||||||
|
wheat4 8B7E66
|
||||||
|
white FFFFFF
|
||||||
|
Quartz D9D9F3
|
||||||
|
Wheat D8D8BF
|
||||||
|
BlanchedAlmond FFEBCD
|
||||||
|
DarkGoldenrod B8860B
|
||||||
|
DarkGoldenrod1 FFB90F
|
||||||
|
DarkGoldenrod2 EEAD0E
|
||||||
|
DarkGoldenrod3 CD950C
|
||||||
|
DarkGoldenrod4 8B6508
|
||||||
|
LemonChiffon FFFACD
|
||||||
|
LemonChiffon1 FFFACD
|
||||||
|
LemonChiffon2 EEE9BF
|
||||||
|
LemonChiffon3 CDC9A5
|
||||||
|
LemonChiffon4 8B8970
|
||||||
|
LightGoldenrod EEDD82
|
||||||
|
LightGoldenrod1 FFEC8B
|
||||||
|
LightGoldenrod2 EEDC82
|
||||||
|
LightGoldenrod3 CDBE70
|
||||||
|
LightGoldenrod4 8B814C
|
||||||
|
LightGoldenrodYellow FAFAD2
|
||||||
|
LightYellow FFFFE0
|
||||||
|
LightYellow1 FFFFE0
|
||||||
|
LightYellow2 EEEED1
|
||||||
|
LightYellow3 CDCDB4
|
||||||
|
LightYellow4 8B8B7A
|
||||||
|
PaleGoldenrod EEE8AA
|
||||||
|
PapayaWhip FFEFD5
|
||||||
|
cornsilk FFF8DC
|
||||||
|
cornsilk1 FFF8DC
|
||||||
|
cornsilk2 EEE8CD
|
||||||
|
cornsilk3 CDC8B1
|
||||||
|
cornsilk4 8B8878
|
||||||
|
goldenrod DAA520
|
||||||
|
goldenrod1 FFC125
|
||||||
|
goldenrod2 EEB422
|
||||||
|
goldenrod3 CD9B1D
|
||||||
|
goldenrod4 8B6914
|
||||||
|
moccasin FFE4B5
|
||||||
|
yellow FFFF00
|
||||||
|
yellow1 FFFF00
|
||||||
|
yellow2 EEEE00
|
||||||
|
yellow3 CDCD00
|
||||||
|
yellow4 8B8B00
|
||||||
|
gold FFD700
|
||||||
|
gold1 FFD700
|
||||||
|
gold2 EEC900
|
||||||
|
gold3 CDAD00
|
||||||
|
gold4 8B7500
|
||||||
|
Goldenrod DBDB70
|
||||||
|
Medium Goldenrod EAEAAE
|
||||||
|
Yellow Green 99CC32
|
||||||
|
copper B87333
|
||||||
|
cool copper D98719
|
||||||
|
Green Copper 856363
|
||||||
|
brass B5A642
|
||||||
|
bronze 8C7853
|
||||||
|
bronze II A67D3D
|
||||||
|
bright gold D9D919
|
||||||
|
Old Gold CFB53B
|
||||||
|
CSS Gold CC9900
|
||||||
|
gold CD7F32
|
||||||
|
silver E6E8FA
|
||||||
|
Silver, Grey C0C0C0
|
||||||
|
Light Steel Blue 545454
|
||||||
|
Steel Blue 236B8E
|
38
tools/colors_find.py
Normal file
38
tools/colors_find.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def get_color(name, rgb):
|
||||||
|
return name, (int(rgb[:2], 16), int(rgb[2:4], 16), int(rgb[4:6], 16))
|
||||||
|
|
||||||
|
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), 'colors.map'), 'r') as f:
|
||||||
|
colors = [get_color(*line.split('\t')) for line in f]
|
||||||
|
|
||||||
|
|
||||||
|
urgb = get_color(None, sys.argv[1])[1]
|
||||||
|
|
||||||
|
|
||||||
|
def col_distance(rgb1, rgb2):
|
||||||
|
return sum(((rgb1[i] - rgb2[i]) ** 2 for i in range(3)))
|
||||||
|
|
||||||
|
|
||||||
|
def find_color(urgb, colors):
|
||||||
|
cur_distance = 3 * (255 ** 2 + 1)
|
||||||
|
cur_color = None
|
||||||
|
for color, crgb in colors:
|
||||||
|
dist = col_distance(urgb, crgb)
|
||||||
|
if dist < cur_distance:
|
||||||
|
cur_distance = dist
|
||||||
|
cur_color = (color, crgb)
|
||||||
|
return cur_color
|
||||||
|
|
||||||
|
|
||||||
|
cur_color = find_color(urgb, colors)
|
||||||
|
|
||||||
|
print urgb, ':', cur_color
|
||||||
|
|
||||||
|
col_1 = ';2;' + ';'.join((str(i) for i in urgb)) + 'm'
|
||||||
|
col_2 = ';2;' + ';'.join((str(i) for i in cur_color[1])) + 'm'
|
||||||
|
sys.stdout.write('\033[48' + col_1 + '\033[38' + col_2 + 'abc\033[0m <-- bg:urgb, fg:crgb\n')
|
||||||
|
sys.stdout.write('\033[48' + col_2 + '\033[38' + col_1 + 'abc\033[0m <-- bg:crgb, fg:urgb\n')
|
105
tools/generate_gradients.py
Normal file
105
tools/generate_gradients.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import sys
|
||||||
|
import json
|
||||||
|
from powerline.colorscheme import cterm_to_hex
|
||||||
|
from itertools import groupby
|
||||||
|
|
||||||
|
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
sys.stderr.write('''
|
||||||
|
Usage: generate_gradients.py colors itemnum[ "show"]
|
||||||
|
|
||||||
|
colors: JSON list with either cterm ([200, 42, 6]) or RGB (["abcdef",
|
||||||
|
"feffef"]) colors.
|
||||||
|
|
||||||
|
itemnum: number of items in generated gradient.
|
||||||
|
|
||||||
|
"show": static string, determines whether gradient sample should be
|
||||||
|
printed to stdout as well.
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
|
def linear_gradient(start_value, stop_value, start_offset, stop_offset, offset):
|
||||||
|
return start_value + ((offset - start_offset) * (stop_value - start_value) / (stop_offset - start_offset))
|
||||||
|
|
||||||
|
|
||||||
|
def gradient(DATA):
|
||||||
|
def gradient_function(y):
|
||||||
|
initial_offset = 0
|
||||||
|
for offset, start, end in DATA:
|
||||||
|
if y <= offset:
|
||||||
|
return [linear_gradient(start[i], end[i], initial_offset, offset, y) for i in range(3)]
|
||||||
|
initial_offset = offset
|
||||||
|
return gradient_function
|
||||||
|
|
||||||
|
|
||||||
|
def get_color(rgb):
|
||||||
|
if type(rgb) is unicode:
|
||||||
|
return int(rgb[:2], 16), int(rgb[2:4], 16), int(rgb[4:6], 16)
|
||||||
|
else:
|
||||||
|
return rgbint_to_rgb(cterm_to_hex[rgb])
|
||||||
|
|
||||||
|
|
||||||
|
def get_rgb(*args):
|
||||||
|
return "%02x%02x%02x" % args
|
||||||
|
|
||||||
|
|
||||||
|
def col_distance(rgb1, rgb2):
|
||||||
|
return sum(((rgb1[i] - rgb2[i]) ** 2 for i in range(3)))
|
||||||
|
|
||||||
|
|
||||||
|
def rgbint_to_rgb(rgbint):
|
||||||
|
return ((rgbint >> 16) & 0xFF, (rgbint >> 8) & 0xFF, rgbint & 0xFF)
|
||||||
|
|
||||||
|
|
||||||
|
def find_color(urgb, colors):
|
||||||
|
cur_distance = 3 * (255 ** 2 + 1)
|
||||||
|
cur_color = None
|
||||||
|
i = 0
|
||||||
|
for crgbint in colors:
|
||||||
|
crgb = rgbint_to_rgb(crgbint)
|
||||||
|
dist = col_distance(urgb, crgb)
|
||||||
|
if dist < cur_distance:
|
||||||
|
cur_distance = dist
|
||||||
|
cur_color = (i, crgb)
|
||||||
|
i += 1
|
||||||
|
return cur_color
|
||||||
|
|
||||||
|
|
||||||
|
def print_color(color):
|
||||||
|
if type(color) is int:
|
||||||
|
colstr = '5;' + str(color)
|
||||||
|
else:
|
||||||
|
colstr = '2;' + ';'.join((str(i) for i in color))
|
||||||
|
sys.stdout.write('\033[48;' + colstr + 'm ')
|
||||||
|
|
||||||
|
|
||||||
|
def print_colors(colors):
|
||||||
|
for i in range(101):
|
||||||
|
color = colors[int(round(i * (len(colors) - 1) / 100))]
|
||||||
|
print_color(color)
|
||||||
|
sys.stdout.write('\033[0m\n')
|
||||||
|
|
||||||
|
|
||||||
|
c = [get_color(color) for color in json.loads(sys.argv[1])]
|
||||||
|
m = int(sys.argv[2]) if len(sys.argv) > 2 else 100
|
||||||
|
m += m % (len(c) - 1)
|
||||||
|
step = m / (len(c) - 1)
|
||||||
|
data = [(i * step, c[i - 1], c[i]) for i in range(1, len(c))]
|
||||||
|
gr_func = gradient(data)
|
||||||
|
gradient = [gr_func(y) for y in range(0, m - 1)]
|
||||||
|
r = [get_rgb(*color) for color in gradient]
|
||||||
|
r2 = [find_color(color, cterm_to_hex)[0] for color in gradient]
|
||||||
|
r3 = [i[0] for i in groupby(r2)]
|
||||||
|
print json.dumps(r)
|
||||||
|
print json.dumps(r2)
|
||||||
|
print json.dumps(r3)
|
||||||
|
if len(sys.argv) > 3 and sys.argv[3] == 'show':
|
||||||
|
print_colors(gradient)
|
||||||
|
print_colors(r2)
|
||||||
|
print_colors(r3)
|
||||||
|
sys.stdout.write('0')
|
||||||
|
sys.stdout.write(''.join(('%10u' % (i * 10) for i in range(1, 11))))
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
nums = (''.join((str(i) for i in range(10))))
|
||||||
|
sys.stdout.write(''.join(((('\033[1m' if j % 2 else '\033[0m') + nums) for j in range(10))))
|
||||||
|
sys.stdout.write('\033[0m0\n')
|
Loading…
x
Reference in New Issue
Block a user