mirror of
https://github.com/powerline/powerline.git
synced 2025-04-08 19:25:04 +02:00
Merge remote-tracking branch 'zyx-i/doc-lint' into develop
This commit is contained in:
commit
badc2cf891
@ -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.
|
||||
|
@ -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``:
|
||||
|
@ -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('\\', '\\\\')
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
59
powerline/lint/inspect.py
Normal 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
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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 '')
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
20
tests/vim.py
20
tests/vim.py
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user