Merge remote-tracking branch 'zyx-i/doc-lint' into develop

This commit is contained in:
Kim Silkebækken 2013-04-20 16:56:43 +02:00
commit badc2cf891
15 changed files with 477 additions and 160 deletions

View File

@ -377,14 +377,13 @@ Themes
can be aligned with the ``align`` property.
``priority``
Optional segment priority. Segments with priority ``-1`` (the
default priority) will always be included, regardless of the width
of the prompt/statusline.
Optional segment priority. Segments with priority ``None`` (the default
priority, represented by ``null`` in json) will always be included,
regardless of the width of the prompt/statusline.
If the priority is ``0`` or more, the segment may be removed if the
If the priority is any number, the segment may be removed if the
prompt/statusline width is too small for all the segments to be
rendered. A lower number means that the segment has a higher
priority.
rendered. A lower number means that the segment has a higher priority.
Segments are removed according to their priority, with low priority
segments being removed first.

View File

@ -91,12 +91,12 @@ Usage
Vim statusline
--------------
Add the following line to your :file:`vimrc`, where ``{path}`` is the
Add the following line to your :file:`vimrc`, where ``{repository_root}`` is the
absolute path to your Powerline installation directory:
.. code-block:: vim
set rtp+={path}/powerline/bindings/vim
set rtp+={repository_root}/powerline/bindings/vim
If you're using Vundle or Pathogen and don't want Powerline functionality in
any other applications, simply add Powerline as a bundle and point the path
@ -115,22 +115,22 @@ Shell prompts
Bash prompt
^^^^^^^^^^^
Add the following line to your :file:`bashrc`, where ``{path}`` is the
absolute path to your Powerline installation directory:
Add the following line to your :file:`bashrc`, where ``{repository_root}`` is
the absolute path to your Powerline installation directory:
.. code-block:: bash
. {path}/powerline/bindings/bash/powerline.sh
. {repository_root}/powerline/bindings/bash/powerline.sh
Zsh prompt
^^^^^^^^^^
Add the following line to your :file:`zshrc`, where ``{path}`` is the
Add the following line to your :file:`zshrc`, where ``{repository_root}`` is the
absolute path to your Powerline installation directory:
.. code-block:: bash
. {path}/powerline/bindings/zsh/powerline.zsh
. {repository_root}/powerline/bindings/zsh/powerline.zsh
If you are not satisfied with powerline speed in this case, compile zpython
branch from https://bitbucket.org/ZyX_I/zsh.
@ -138,10 +138,10 @@ branch from https://bitbucket.org/ZyX_I/zsh.
Tmux statusline
---------------
Add the following line to your :file:`tmux.conf`, where ``{path}`` is the
absolute path to your Powerline installation directory::
Add the following line to your :file:`tmux.conf`, where ``{repository_root}`` is
the absolute path to your Powerline installation directory::
source '{path}/powerline/bindings/tmux/powerline.conf'
source '{repository_root}/powerline/bindings/tmux/powerline.conf'
IPython prompt
--------------
@ -174,12 +174,12 @@ Awesome widget
.. note:: The Powerline widget will spawn a shell script that runs in the
background and updates the statusline with ``awesome-client``.
Add the following to your :file:`rc.lua`, where ``{path}`` is the absolute
path to your Powerline installation directory:
Add the following to your :file:`rc.lua`, where ``{repository_root}`` is the
absolute path to your Powerline installation directory:
.. code-block:: lua
package.path = package.path .. ';{path}/powerline/bindings/awesome/?.lua'
package.path = package.path .. ';{repository_root}/powerline/bindings/awesome/?.lua'
require('powerline')
Then add the ``powerline_widget`` to your ``wibox``:

View File

@ -1,8 +1,8 @@
# vim:fileencoding=utf-8:noet
from sphinx.ext import autodoc
from sphinx.util.inspect import getargspec
from inspect import ArgSpec, formatargspec
from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment
from itertools import count
from inspect import formatargspec
from powerline.lint.inspect import getconfigargspec
from powerline.lib.threaded import ThreadedSegment
try:
from __builtin__ import unicode
@ -25,55 +25,7 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter):
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)]
if self.object.update_first:
args.append('update_first')
defaults.append(True)
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)) or
arg in 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))
argspec = getconfigargspec(self.object)
return formatargspec(*argspec, formatvalue=formatvalue).replace('\\', '\\\\')

View File

@ -23,7 +23,7 @@ def find_config_file(search_paths, config_file):
raise IOError('Config file not found in search path: {0}'.format(config_file))
class PowerlineState(object):
class PowerlineLogger(object):
def __init__(self, use_daemon_threads, logger, ext):
self.logger = logger
self.ext = ext
@ -175,7 +175,7 @@ class Powerline(object):
self.logger.addHandler(handler)
if not self.pl:
self.pl = PowerlineState(self.use_daemon_threads, self.logger, self.ext)
self.pl = PowerlineLogger(self.use_daemon_threads, self.logger, self.ext)
if not self.config_loader.pl:
self.config_loader.pl = self.pl

View File

@ -3,6 +3,9 @@ from powerline import find_config_file, Powerline
from powerline.lib.config import load_json_config
from powerline.lint.markedjson.error import echoerr, MarkedError
from powerline.segments.vim import vim_modes
from powerline.lint.inspect import getconfigargspec
from powerline.lint.markedjson.markedvalue import gen_marked_value
from powerline.lib.threaded import ThreadedSegment
import itertools
import sys
import os
@ -25,8 +28,33 @@ def open_file(path):
EMPTYTUPLE = tuple()
class JStr(unicode):
def join(self, iterable):
return super(JStr, self).join((unicode(item) for item in iterable))
key_sep = JStr('/')
list_sep = JStr(', ')
def context_key(context):
return '/'.join((unicode(c[0]) for c in context))
return key_sep.join((c[0] for c in context))
class DelayedEchoErr(object):
def __init__(self, echoerr):
self.echoerr = echoerr
self.errs = []
def __call__(self, *args, **kwargs):
self.errs.append((args, kwargs))
def echo_all(self):
for args, kwargs in self.errs:
self.echoerr(*args, **kwargs)
def __nonzero__(self):
return not not self.errs
class Spec(object):
@ -80,7 +108,7 @@ class Spec(object):
context_mark=context_mark,
problem='{0!r} must be a {1} instance, not {2}'.format(
value,
', '.join((t.__name__ for t in types)),
list_sep.join((t.__name__ for t in types)),
type(value.value).__name__
),
problem_mark=value.mark)
@ -118,10 +146,7 @@ class Spec(object):
return True, hadproblem
def check_either(self, value, context_mark, data, context, echoerr, start, end):
errs = []
def new_echoerr(*args, **kwargs):
errs.append((args, kwargs))
new_echoerr = DelayedEchoErr(echoerr)
hadproblem = False
for spec in self.specs[start:end]:
@ -131,8 +156,7 @@ class Spec(object):
if not hadproblem:
return True, False
for args, kwargs in errs:
echoerr(*args, **kwargs)
new_echoerr.echo_all()
return False, hadproblem
@ -392,6 +416,7 @@ def check_config(d, theme, data, context, echoerr):
return True, False, True
return True, False, False
divider_spec = Spec().type(unicode).len('le', 3,
lambda value: 'Divider {0!r} is too large!'.format(value)).copy
divside_spec = Spec(
@ -427,7 +452,7 @@ main_spec = (Spec(
theme=theme_spec(),
local_themes=Spec()
.unknown_spec(lambda *args: check_matcher_func('vim', *args), theme_spec())
),
).optional(),
ipython=Spec(
colorscheme=colorscheme_spec(),
theme=theme_spec(),
@ -436,7 +461,7 @@ main_spec = (Spec(
out=theme_spec(),
rewrite=theme_spec(),
),
),
).optional(),
).unknown_spec(check_ext,
Spec(
colorscheme=colorscheme_spec(),
@ -527,10 +552,11 @@ type_keys = {
}
required_keys = {
'function': set(),
'string': set(('contents', 'highlight_group')),
'filler': set(('highlight_group',)),
'string': set(('contents',)),
'filler': set(),
}
function_keys = set(('args', 'module'))
highlight_keys = set(('highlight_group', 'name'))
def check_key_compatibility(segment, data, context, echoerr):
@ -542,25 +568,33 @@ def check_key_compatibility(segment, data, context, echoerr):
problem_mark=segment_type.mark)
return False, False, True
hadproblem = False
keys = set(segment)
if not ((keys - generic_keys) < type_keys[segment_type]):
unknown_keys = keys - generic_keys - type_keys[segment_type]
echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)),
context_mark=context[-1][1].mark,
problem='found keys not used with the current segment type: {0}'.format(
', '.join((unicode(key) for key in unknown_keys))),
list_sep.join(unknown_keys)),
problem_mark=list(unknown_keys)[0].mark)
return True, False, True
hadproblem = True
if not (keys > required_keys[segment_type]):
missing_keys = required_keys[segment_type] - keys
echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)),
context_mark=context[-1][1].mark,
problem='found missing required keys: {0}'.format(
', '.join((unicode(key) for key in missing_keys))))
return True, False, True
list_sep.join(missing_keys)))
hadproblem = True
return True, False, False
if not (segment_type == 'function' or (keys & highlight_keys)):
echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)),
context_mark=context[-1][1].mark,
problem='found missing keys required to determine highlight group. Either highlight_group or name key must be present')
hadproblem = True
return True, False, hadproblem
def check_segment_module(module, data, context, echoerr):
@ -610,28 +644,39 @@ def check_full_segment_data(segment, data, context, echoerr):
return check_key_compatibility(segment_copy, data, context, echoerr)
def import_segment(name, data, context, echoerr, module=None):
if not module:
module = context[-2][1].get('module', context[0][1].get('default_module', 'powerline.segments.' + data['ext']))
with WithPath(data['import_paths']):
try:
func = getattr(__import__(unicode(module), fromlist=[unicode(name)]), unicode(name))
except ImportError:
echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)),
problem='failed to import module {0}'.format(module),
problem_mark=module.mark)
return None
except AttributeError:
echoerr(context='Error while loading segment function (key {key})'.format(key=context_key(context)),
problem='failed to load function {0} from module {1}'.format(name, module),
problem_mark=name.mark)
return None
if not callable(func):
echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)),
problem='imported "function" {0} from module {1} is not callable'.format(name, module),
problem_mark=module.mark)
return None
return func
def check_segment_name(name, data, context, echoerr):
ext = data['ext']
if context[-2][1].get('type', 'function') == 'function':
module = context[-2][1].get('module', context[0][1].get('default_module', 'powerline.segments.' + ext))
with WithPath(data['import_paths']):
try:
func = getattr(__import__(unicode(module), fromlist=[unicode(name)]), unicode(name))
except ImportError:
echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)),
problem='failed to import module {0}'.format(module),
problem_mark=module.mark)
return True, False, True
except AttributeError:
echoerr(context='Error while loading segment function (key {key})'.format(key=context_key(context)),
problem='failed to load function {0} from module {1}'.format(name, module),
problem_mark=name.mark)
return True, False, True
func = import_segment(name, data, context, echoerr)
if not callable(func):
echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)),
problem='imported "function" {0} from module {1} is not callable'.format(name, module),
problem_mark=module.mark)
if not func:
return True, False, True
hl_groups = []
@ -653,32 +698,32 @@ def check_segment_name(name, data, context, echoerr):
if r:
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight group {0} not defined in the following colorschemes: {1}\n(Group name was obtained from function documentation.)'.format(
divider_hl_group, ', '.join(r)),
divider_hl_group, list_sep.join(r)),
problem_mark=name.mark)
hadproblem = True
if hl_groups:
greg = re.compile(r'``([^`]+)``( \(gradient\))?')
hl_groups = [[greg.match(subs).groups() for subs in s.split(' or ')] for s in (', '.join(hl_groups)).split(', ')]
hl_groups = [[greg.match(subs).groups() for subs in s.split(' or ')] for s in (list_sep.join(hl_groups)).split(', ')]
for required_pack in hl_groups:
rs = [hl_exists(hl_group, data, context, echoerr, allow_gradients=('force' if gradient else False))
for hl_group, gradient in required_pack]
if all(rs):
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight groups list ({0}) with all groups not defined in some colorschemes\n(Group names were taken from function documentation.)'.format(
', '.join((unicode(h[0]) for h in required_pack))),
list_sep.join((h[0] for h in required_pack))),
problem_mark=name.mark)
for r, h in zip(rs, required_pack):
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight group {0} not defined in the following colorschemes: {1}'.format(
h[0], ', '.join(r)))
h[0], list_sep.join(r)))
hadproblem = True
else:
r = hl_exists(name, data, context, echoerr, allow_gradients=True)
if r:
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight group {0} not defined in the following colorschemes: {1}\n(If not specified otherwise in documentation, highlight group for function segments\nis the same as the function name.)'.format(
name, ', '.join(r)),
name, list_sep.join(r)),
problem_mark=name.mark)
hadproblem = True
@ -744,7 +789,7 @@ def check_highlight_group(hl_group, data, context, echoerr):
if r:
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight group {0} not defined in the following colorschemes: {1}'.format(
hl_group, ', '.join(r)),
hl_group, list_sep.join(r)),
problem_mark=hl_group.mark)
return True, False, True
return True, False, False
@ -755,12 +800,12 @@ def check_highlight_groups(hl_groups, data, context, echoerr):
if all(rs):
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight groups list ({0}) with all groups not defined in some colorschemes'.format(
', '.join((unicode(h) for h in hl_groups))),
list_sep.join((unicode(h) for h in hl_groups))),
problem_mark=hl_groups.mark)
for r, hl_group in zip(rs, hl_groups):
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight group {0} not defined in the following colorschemes: {1}'.format(
hl_group, ', '.join(r)),
hl_group, list_sep.join(r)),
problem_mark=hl_group.mark)
return True, False, True
return True, False, False
@ -798,11 +843,94 @@ def check_segment_data_key(key, data, context, echoerr):
return True, False, False
# FIXME More checks, limit existing to ThreadedSegment instances only
threaded_args_specs = {
'interval': Spec().cmp('gt', 0.0),
'update_first': Spec().type(bool),
'shutdown_event': Spec().error('Shutdown event must be set by powerline'),
}
def check_args_variant(segment, args, data, context, echoerr):
argspec = getconfigargspec(segment)
present_args = set(args)
all_args = set(argspec.args)
required_args = set(argspec.args[:-len(argspec.defaults)])
hadproblem = False
if required_args - present_args:
echoerr(context='Error while checking segment arguments (key {key})'.format(key=context_key(context)),
context_mark=args.mark,
problem='some of the required keys are missing: {0}'.format(list_sep.join(required_args - present_args)))
hadproblem = True
if not all_args >= present_args:
echoerr(context='Error while checking segment arguments (key {key})'.format(key=context_key(context)),
context_mark=args.mark,
problem='found unknown keys: {0}'.format(list_sep.join(present_args - all_args)),
problem_mark=next(iter(present_args - all_args)).mark)
hadproblem = True
if isinstance(segment, ThreadedSegment):
for key in set(threaded_args_specs) & present_args:
proceed, khadproblem = threaded_args_specs[key].match(args[key], args.mark, data, context + ((key, args[key]),), echoerr)
if khadproblem:
hadproblem = True
if not proceed:
return hadproblem
return hadproblem
def check_args(get_segment_variants, args, data, context, echoerr):
new_echoerr = DelayedEchoErr(echoerr)
count = 0
hadproblem = False
for segment in get_segment_variants(data, context, new_echoerr):
count += 1
shadproblem = check_args_variant(segment, args, data, context, echoerr)
if shadproblem:
hadproblem = True
if not count:
hadproblem = True
if new_echoerr:
new_echoerr.echo_all()
else:
echoerr(context='Error while checking segment arguments (key {key})'.format(key=context_key(context)),
context_mark=context[-2][1].mark,
problem='no suitable segments found')
return True, False, hadproblem
def get_one_segment_variant(data, context, echoerr):
name = context[-2][1].get('name')
if name:
func = import_segment(name, data, context, echoerr)
if func:
yield func
def get_all_possible_segments(data, context, echoerr):
name = context[-2][0]
module, name = name.rpartition('.')[::2]
if module:
func = import_segment(name, data, context, echoerr, module=module)
if func:
yield func
else:
for theme_config in data['ext_theme_configs'].values():
for segments in theme_config.get('segments', {}).values():
for segment in segments:
if segment.get('type', 'function') == 'function':
module = segment.get('module', context[0][1].get('default_module', 'powerline.segments.' + data['ext']))
func = import_segment(name, data, context, echoerr, module=module)
if func:
yield func
args_spec = Spec(
interval=Spec().cmp('gt', 0.0).optional(),
update_first=Spec().type(bool).optional(),
shutdown_event=Spec().error('Shutdown event must be set by powerline').optional(),
pl=Spec().error('pl object must be set by powerline').optional(),
segment_info=Spec().error('Segment info dictionary must be set by powerline').optional(),
).unknown_spec(Spec(), Spec()).optional().copy
@ -818,12 +946,12 @@ segments_spec = Spec().optional().list(
draw_soft_divider=Spec().type(bool).optional(),
draw_inner_divider=Spec().type(bool).optional(),
module=segment_module_spec(),
priority=Spec().either(Spec().cmp('eq', -1), Spec().cmp('ge', 0.0)).optional(),
priority=Spec().type(int, float, type(None)).optional(),
after=Spec().type(unicode).optional(),
before=Spec().type(unicode).optional(),
width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(),
align=Spec().oneof(set('lr')).optional(),
args=args_spec(),
args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_variant, *args, **kwargs)),
contents=Spec().type(unicode).optional(),
highlight_group=Spec().list(
highlight_group_spec().re('^(?:(?!:divider$).)+$',
@ -840,7 +968,7 @@ theme_spec = (Spec(
Spec(
after=Spec().type(unicode).optional(),
before=Spec().type(unicode).optional(),
args=args_spec(),
args=args_spec().func(lambda *args, **kwargs: check_args(get_all_possible_segments, *args, **kwargs)),
contents=Spec().type(unicode).optional(),
),
).optional().context_message('Error while loading segment data (key {key})'),

59
powerline/lint/inspect.py Normal file
View File

@ -0,0 +1,59 @@
# vim:fileencoding=utf-8:noet
from __future__ import absolute_import
from inspect import ArgSpec, getargspec
from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment
from itertools import count
def getconfigargspec(obj):
if isinstance(obj, ThreadedSegment):
args = ['interval']
defaults = [getattr(obj, 'interval', 1)]
if obj.update_first:
args.append('update_first')
defaults.append(True)
methods = ['render', 'set_state']
if isinstance(obj, KwThreadedSegment):
methods += ['key', 'render_one']
for method in methods:
if hasattr(obj, method):
# Note: on <python-2.6 it may return simple tuple, not
# ArgSpec instance.
argspec = getargspec(getattr(obj, method))
for i, arg in zip(count(1), reversed(argspec.args)):
if (arg == 'self' or
(arg == 'segment_info' and
getattr(obj, 'powerline_requires_segment_info', None)) or
(arg == 'pl') or
(method.startswith('render') and (1 if argspec.args[0] == 'self' else 0) + i == len(argspec.args)) or
arg in 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(obj, 'powerline_origin'):
obj = obj.powerline_origin
else:
obj = obj
argspec = getargspec(obj)
args = []
defaults = []
for i, arg in zip(count(1), reversed(argspec.args)):
if ((arg == 'segment_info' and getattr(obj, 'powerline_requires_segment_info', None)) or
arg == 'pl'):
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 argspec

View File

@ -29,6 +29,9 @@ class Mark:
self.buffer = buffer
self.pointer = pointer
def copy(self):
return Mark(self.name, self.line, self.column, self.buffer, self.pointer)
def get_snippet(self, indent=4, max_length=75):
if self.buffer is None:
return None

View File

@ -1,17 +1,71 @@
__all__ = ['gen_marked_value', 'MarkedValue']
try:
from __builtin__ import unicode
except ImportError:
unicode = str
def gen_new(cls):
def __new__(arg_cls, value, mark):
r = super(arg_cls, arg_cls).__new__(arg_cls, value)
r.mark = mark
r.value = value
return r
return __new__
class MarkedUnicode(unicode):
__new__ = gen_new(unicode)
def _proc_partition(self, part_result):
pointdiff = 1
r = []
for s in part_result:
mark = self.mark.copy()
# XXX Does not work properly with escaped strings, but this requires
# saving much more information in mark.
mark.column += pointdiff
mark.pointer += pointdiff
r.append(MarkedUnicode(s, mark))
pointdiff += len(s)
return tuple(r)
def rpartition(self, sep):
return self._proc_partition(super(MarkedUnicode, self).rpartition(sep))
def partition(self, sep):
return self._proc_partition(super(MarkedUnicode, self).partition(sep))
class MarkedInt(int):
__new__ = gen_new(int)
class MarkedFloat(float):
__new__ = gen_new(float)
class MarkedValue:
def __init__(self, value, mark):
self.mark = mark
self.value = value
specialclasses = {
unicode: MarkedUnicode,
int: MarkedInt,
float: MarkedFloat,
}
classcache = {}
def gen_marked_value(value, mark):
if value.__class__ in classcache:
def gen_marked_value(value, mark, use_special_classes=True):
if use_special_classes and value.__class__ in specialclasses:
Marked = specialclasses[value.__class__]
elif value.__class__ in classcache:
Marked = classcache[value.__class__]
else:
class Marked(MarkedValue):

View File

@ -19,11 +19,51 @@ def construct_returned_value(rendered_highlighted, segments, output_raw):
class Renderer(object):
'''Object that is responsible for generating the highlighted string.
:param dict theme_config:
Main theme configuration.
:param local_themes:
Local themes. Is to be used by subclasses from ``.get_theme()`` method,
base class only records this parameter to a ``.local_themes`` attribute.
:param dict theme_kwargs:
Keyword arguments for ``Theme`` class constructor.
:param Colorscheme colorscheme:
Colorscheme object that holds colors configuration.
:param PowerlineLogger pl:
Object used for logging.
:param int ambiwidth:
Width of the characters with east asian width unicode attribute equal to
``A`` (Ambigious).
:param dict options:
Various options. Are normally not used by base renderer, but all options
are recorded as attributes.
'''
segment_info = {
'environ': os.environ,
'getcwd': getattr(os, 'getcwdu', os.getcwd),
'home': os.environ.get('HOME'),
}
'''Basic segment info. Is merged with local segment information by
``.get_segment_info()`` method. Keys:
``environ``
Object containing environment variables. Must define at least the
following methods: ``.__getitem__(var)`` that raises ``KeyError`` in
case requested environment variable is not present, ``.get(var,
default=None)`` that works like ``dict.get`` and be able to be passed to
``Popen``.
``getcwd``
Function that returns current working directory. Will be called without
any arguments, should return ``unicode`` or (in python-2) regular
string.
``home``
String containing path to home directory. Should be ``unicode`` or (in
python-2) regular string or ``None``.
'''
def __init__(self,
theme_config,
@ -31,6 +71,7 @@ class Renderer(object):
theme_kwargs,
colorscheme,
pl,
ambiwidth=1,
**options):
self.__dict__.update(options)
self.theme_config = theme_config
@ -41,24 +82,48 @@ class Renderer(object):
self.theme_kwargs = theme_kwargs
self.colorscheme = colorscheme
self.width_data = {
'N': 1, # Neutral
'Na': 1, # Narrow
'A': getattr(self, 'ambiwidth', 1), # Ambigious
'H': 1, # Half-width
'W': 2, # Wide
'F': 2, # Fullwidth
}
'N': 1, # Neutral
'Na': 1, # Narrow
'A': ambiwidth, # Ambigious
'H': 1, # Half-width
'W': 2, # Wide
'F': 2, # Fullwidth
}
def strwidth(self, string):
'''Function that returns string width.
Is used to calculate the place given string occupies when handling
``width`` argument to ``.render()`` method. Must take east asian width
into account.
:param unicode string:
String whose width will be calculated.
:return: unsigned integer.
'''
return sum((0 if combining(symbol) else self.width_data[east_asian_width(symbol)] for symbol in string))
def get_theme(self, matcher_info):
'''Get Theme object.
Is to be overridden by subclasses to support local themes, this variant
only returns ``.theme`` attribute.
:param matcher_info:
Parameter ``matcher_info`` that ``.render()`` method received.
Unused.
'''
return self.theme
def shutdown(self):
'''Prepare for interpreter shutdown. The only job it is supposed to do
is calling ``.shutdown()`` method for all theme objects. Should be
overridden by subclasses in case they support local themes.
'''
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'))
if segment['divider_highlight_group']:
segment['divider_highlight'] = self.colorscheme.get_highlighting(segment['divider_highlight_group'], mode)
@ -67,6 +132,21 @@ class Renderer(object):
return segment
def get_segment_info(self, segment_info):
'''Get segment information.
Must return a dictionary containing at least ``home``, ``environ`` and
``getcwd`` keys (see documentation for ``segment_info`` attribute). This
implementation merges ``segment_info`` dictionary passed to
``.render()`` method with ``.segment_info`` attribute, preferring keys
from the former. It also replaces ``getcwd`` key with function returning
``segment_info['environ']['PWD']`` in case ``PWD`` variable is
available.
:param dict segment_info:
Segment information that was passed to ``.render()`` method.
:return: dict with segment information.
'''
r = self.segment_info.copy()
if segment_info:
r.update(segment_info)
@ -82,12 +162,30 @@ class Renderer(object):
with a negative priority are left. If one or more filler segments are
provided they will fill the remaining space until the desired width is
reached.
:param str mode:
Mode string. Affects contents (colors and the set of segments) of
rendered string.
:param int width:
Maximum width text can occupy. May be exceeded if there are too much
non-removable segments.
:param str side:
One of ``left``, ``right``. Determines which side will be rendered.
If not present all sides are rendered.
:param bool output_raw:
Changes the output: if this parameter is ``True`` then in place of
one string this method outputs a pair ``(colored_string,
colorless_string)``.
:param dict segment_info:
Segment information. See also ``.get_segment_info()`` method.
:param matcher_info:
Matcher information. Is processed in ``.get_theme()`` method.
'''
theme = self.get_theme(matcher_info)
segments = theme.get_segments(side, self.get_segment_info(segment_info))
# Handle excluded/included segments for the current mode
segments = [self.get_highlighting(segment, mode) for segment in segments
segments = [self._get_highlighting(segment, mode) for segment in segments
if mode not in segment['exclude_modes'] or (segment['include_modes'] and segment in segment['include_modes'])]
segments = [segment for segment in self._render_segments(theme, segments)]
@ -97,7 +195,7 @@ class Renderer(object):
return construct_returned_value(''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw)
# Create an ordered list of segments that can be dropped
segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0]
segments_priority = sorted((segment for segment in segments if segment['priority'] is not None), key=lambda segment: segment['priority'], reverse=True)
while sum([segment['_len'] for segment in segments]) > width and len(segments_priority):
segments.remove(segments_priority[0])
segments_priority.pop(0)
@ -199,10 +297,23 @@ class Renderer(object):
@staticmethod
def escape(string):
'''Method that escapes segment contents.
'''
return string
def hlstyle(fg=None, bg=None, attr=None):
'''Output highlight style string.
Assuming highlighted string looks like ``{style}{contents}`` this method
should output ``{style}``. If it is called without arguments this method
is supposed to reset style to its default.
'''
raise NotImplementedError
def hl(self, contents, fg=None, bg=None, attr=None):
'''Output highlighted chunk.
This implementation just outputs ``.hlstyle()`` joined with
``contents``.
'''
return self.hlstyle(fg, bg, attr) + (contents or '')

View File

@ -39,17 +39,17 @@ def get_filler(data, segment):
segment_getters = {
"function": get_function,
"string": get_string,
"filler": get_filler,
}
"function": get_function,
"string": get_string,
"filler": get_filler,
}
def gen_segment_getter(ext, path, theme_configs, default_module=None):
data = {
'default_module': default_module or 'powerline.segments.' + ext,
'path': path,
}
'default_module': default_module or 'powerline.segments.' + ext,
'path': path,
}
def get_key(segment, module, key, default=None):
return get_segment_key(segment, theme_configs, key, module, default)
@ -61,8 +61,18 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None):
get_segment_info = segment_getters[segment_type]
except KeyError:
raise TypeError('Unknown segment type: {0}'.format(segment_type))
contents, contents_func, module = get_segment_info(data, segment)
highlight_group = segment_type != 'function' and segment.get('highlight_group') or segment.get('name')
try:
contents, contents_func, module = get_segment_info(data, segment)
except Exception as e:
pl.exception('Failed to generate segment from {0!r}: {1}', segment, str(e), prefix='segment_generator')
return None
if segment_type == 'function':
highlight_group = [module + '.' + segment['name'], segment['name']]
else:
highlight_group = segment.get('highlight_group') or segment.get('name')
return {
'name': segment.get('name'),
'type': segment_type,
@ -73,7 +83,7 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None):
'contents_func': contents_func,
'contents': contents,
'args': get_key(segment, module, 'args', {}) if segment_type == 'function' else {},
'priority': segment.get('priority', -1),
'priority': segment.get('priority', None),
'draw_hard_divider': segment.get('draw_hard_divider', True),
'draw_soft_divider': segment.get('draw_soft_divider', True),
'draw_inner_divider': segment.get('draw_inner_divider', False),
@ -89,6 +99,6 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None):
'_len': 0,
'_space_left': 0,
'_space_right': 0,
}
}
return get

View File

@ -274,6 +274,7 @@ def col_current(pl, segment_info):
return str(segment_info['window'].cursor[1] + 1)
# TODO Add &textwidth-based gradient
@window_cached
def virtcol_current(pl):
'''Return current visual column with concealed characters ingored

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fileencoding=utf-8:noet
'''Powerline prompt and statusline script.'''
import sys
import os

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fileencoding=utf-8:noet
'''Powerline configuration checker.'''
import argparse
from powerline.lint import check

View File

@ -3,7 +3,7 @@
FAILED=0
export PYTHONPATH="${PYTHONPATH}:`realpath .`"
for file in tests/test_*.py ; do
if ! ${PYTHON} $file ; then
if ! ${PYTHON} $file --verbose --catch ; then
FAILED=1
fi
done

View File

@ -316,13 +316,13 @@ class _Buffer(object):
self.name = os.path.abspath(name) if name else None
_buf_scopes[bufnr] = {}
_buf_options[bufnr] = {
'modified': 0,
'readonly': 0,
'fileformat': 'unix',
'filetype': '',
'buftype': '',
'fileencoding': 'utf-8',
}
'modified': 0,
'readonly': 0,
'fileformat': 'unix',
'filetype': '',
'buftype': '',
'fileencoding': 'utf-8',
}
_buf_lines[bufnr] = ['']
from copy import copy
_undostate[bufnr] = [copy(_buf_lines[bufnr])]
@ -393,9 +393,9 @@ def _init():
@_vim
def _get_segment_info():
mode_translations = {
chr(ord('V') - 0x40): '^V',
chr(ord('S') - 0x40): '^S',
}
chr(ord('V') - 0x40): '^V',
chr(ord('S') - 0x40): '^S',
}
mode = _mode
mode = mode_translations.get(mode, mode)
return {