Add support for segment.expand

Closes #154
This commit is contained in:
ZyX 2014-08-30 17:54:46 +04:00
parent 5b038dba82
commit d6c603daf0
6 changed files with 159 additions and 9 deletions

View File

@ -369,6 +369,8 @@ ascii Theme without any unicode characters at all
Each segment dictionary has the following options:
.. _config-themes-seg-type:
``type``
The segment type. Can be one of ``function`` (default), ``string`` or
``segments_list``:
@ -439,9 +441,13 @@ ascii Theme without any unicode characters at all
``args``
A dict of arguments to be passed to a ``function`` segment.
.. _config-themes-seg-align:
``align``
Aligns the segments contents to the left (``l``), center (``c``) or
right (``r``). Has no sense if ``width`` key was not specified.
right (``r``). Has no sense if ``width`` key was not specified or if
segment provides its own function for ``auto`` ``width`` handling and
does not care about this option.
.. _config-themes-seg-width:
@ -454,6 +460,8 @@ ascii Theme without any unicode characters at all
either returned by a function or a static string, and the contents
can be aligned with the ``align`` property.
.. _config-themes-seg-priority:
``priority``
Optional segment priority. Segments with priority ``None`` (the default
priority, represented by ``null`` in json) will always be included,
@ -482,6 +490,8 @@ ascii Theme without any unicode characters at all
segments. Only applicable for functions returning multiple segments.
Defaults to ``False``.
.. _config-themes-seg-exclude_modes:
``exclude_modes``
A list of modes where this segment will be excluded: The segment is
included in all modes, *except* for the modes in this list.

View File

@ -36,8 +36,11 @@ Listers must return a sequence of pairs. First item in the pair must contain
a ``segment_info`` dictionary specific to one of the listed entities.
Second item must contain another dictionary: it will be used to modify the
resulting segment. In addition to usual keys that describe segment the following
keys may be present (it is advised that *only* the following keys will be used):
resulting segment. In addition to :ref:`usual keys that describe segment
<dev-segments-segment>` the following keys may be present (it is advised that
*only* the following keys will be used):
.. _dev-listers-mode:
``mode``
Segment-specific mode. Used to alter segment highlighting.

View File

@ -51,6 +51,8 @@ powerline:
theme-specific values go directly to :ref:`top-level themes
<config-themes>`.
.. _dev-segments-startup:
``startup``
This attribute must be a callable which accepts the following keyword
arguments:
@ -68,6 +70,8 @@ powerline:
used (more specific: when :py:class:`powerline.Powerline` constructor
received true ``run_once`` argument).
.. _dev-segments-shutdown:
``shutdown``
This attribute must be a callable that accepts no arguments and shuts down
threads and frees any other resources allocated in ``startup`` method of the
@ -75,6 +79,29 @@ powerline:
This function is not called when ``startup`` method is not called.
.. _dev-segments-expand:
``expand``
This attribute must be a callable that accepts the following keyword
arguments:
* ``pl``: :py:class:`powerline.PowerlineLogger` instance which is to be used
for logging.
* ``amount``: integer number representing amount of display cells result
must occupy.
.. warning::
“Amount of display cells” is *not* number of Unicode codepoints, string
length, or byte count. It is suggested that your function should look
something like ``return (' ' * amount) + segment['contents']`` where
``' '`` may be replaced with anything that is known to occupy exactly
one display cell.
* ``segment``: :ref:`segment dictionary <dev-segments-segment>`.
* Any arguments found in user configuration for the given segment (i.e.
:ref:`args key <config-themes-seg-args>`).
It must return new value of :ref:`contents <dev-segments-seg-contents>` key.
This callable object should may return either a string (``unicode`` in Python2
or ``str`` in Python3, *not* ``str`` in Python2 or ``bytes`` in Python3) object
or a list of dictionaries. String object is a short form of the following return
@ -87,6 +114,8 @@ value:
'highlight_group': [segment_name],
}]
.. _dev-segments-return:
Returned list is a list of segments treated independently, except for
:ref:`draw_inner_divider key <dev-segments-draw_inner_divider>`.
@ -158,6 +187,86 @@ Detailed description of used dictionary keys:
No error occurs if segment has this key, but no used highlight groups use
gradient color.
``_*``
Keys starting with underscore are reserved for powerline and must not be
returned.
``__*``
Keys starting with two underscores are reserved for the segment functions,
specifically for :ref:`expand function <dev-segments-expand>`.
.. _dev-segments-segment:
Segment dictionary
==================
Segment dictionary contains the following keys:
* All keys returned by segment function (if it was used).
* All of the following keys:
``name``
Segment name: value of the :ref:`name key <config-themes-seg-name>` or
function name (last component of the :ref:`function key
<config-themes-seg-function>`). May be ``None``.
``type``
:ref:`Segment type <config-themes-seg-type>`. Always represents actual type
and is never ``None``.
``highlight_group``, ``divider_highlight_group``
Used highlight groups. May be ``None``.
.. _dev-segments-seg-around:
``before``, ``after``
Value of :ref:`before <config-themes-seg-before>` or :ref:`after
<config-themes-seg-after>` configuration options. May be ``None`` as well as
an empty string.
``contents_func``
Function used to get segment contents. May be ``None``.
.. _dev-segments-seg-contents:
``contents``
Actual segment contents, excluding dividers and :ref:`before/after
<dev-segments-seg-around>`. May be ``None``.
``priority``
:ref:`Segment priority <config-themes-seg-priority>`. May be ``None`` for no
priority (such segments are always shown).
``draw_soft_divider``, ``draw_hard_divider``, ``draw_inner_divider``
:ref:`Divider control flags <dev-segments-draw_inner_divider>`.
``side``
Segment side: ``right`` or ``left``.
``exclude_modes``, ``include_modes``
:ref:`Mode display control lists <config-themes-seg-exclude_modes>`. May be
empty, but may not be ``None``.
``width``, ``align``
:ref:`Width and align options <config-themes-seg-align>`. May be ``None``.
``expand``
Partially applied :ref:`expand function <dev-segments-expand>`. Accepts
``pl``, ``amount`` and ``segment`` positional parameters, keyword parameters
from :ref:`args <config-themes-seg-args>` key were applied.
``startup``
Partially applied :ref:`startup function <dev-segments-startup>`. Accepts
``pl`` and ``shutdown_event`` positional parameters, keyword parameters from
:ref:`args <config-themes-seg-args>` key were applied.
``shutdown``
:ref:`Shutdown function <dev-segments-shutdown>`. Accepts no argument.
``mode``
:ref:`Segment-specific mode <dev-listers-mode>`. May be ``None``.
Segments layout
===============

View File

@ -82,14 +82,20 @@ segment_getters = {
}
def get_attr_func(contents_func, key, args):
def get_attr_func(contents_func, key, args, is_space_func=False):
try:
func = getattr(contents_func, key)
except AttributeError:
return None
else:
if args is None:
return lambda: func()
if is_space_func:
def expand_func(pl, amount, segment):
try:
return func(pl=pl, amount=amount, segment=segment, **args)
except Exception as e:
pl.exception('Exception while computing segment expansion: {0}', str(e))
return segment['contents'] + (' ' * amount)
return expand_func
else:
return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **args)
@ -297,7 +303,8 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge
if segment_type == 'function':
startup_func = get_attr_func(_contents_func, 'startup', args)
shutdown_func = get_attr_func(_contents_func, 'shutdown', None)
shutdown_func = getattr(_contents_func, 'shutdown', None)
expand_func = get_attr_func(_contents_func, 'expand', args, True)
if hasattr(_contents_func, 'powerline_requires_filesystem_watcher'):
create_watcher = lambda: create_file_watcher(pl, common_config['watcher'])
@ -311,6 +318,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge
startup_func = None
shutdown_func = None
contents_func = None
expand_func = None
return {
'name': name or function_name,
@ -330,7 +338,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge
'include_modes': segment.get('include_modes', []),
'width': segment.get('width'),
'align': segment.get('align', 'l'),
'expand': None,
'expand': expand_func,
'startup': startup_func,
'shutdown': shutdown_func,
'mode': None,

View File

@ -150,7 +150,7 @@ class Theme(object):
for segment in parsed_segments:
width = segment['width']
align = segment['align']
if width == 'auto':
if width == 'auto' and segment['expand'] is None:
segment['expand'] = expand_functions.get(align)
if segment['expand'] is None:
self.pl.error('Align argument must be “r”, “l” or “c”, not “{0}', align)

View File

@ -474,6 +474,26 @@ class TestSegmentAttributes(TestRender):
}
self.assertRenderEqual(p, '{56} pl;{6-}>>{--}')
@add_args
def test_expand(self, p, config):
def m1(divider=',', **kwargs):
return divider.join(kwargs.keys()) + divider
def expand(pl, amount, segment, **kwargs):
return ('-' * amount) + segment['contents']
m1.expand = expand
sys.modules['bar'] = Args(m1=m1)
config['themes/test/default']['segments'] = {
'left': [
{
'function': 'bar.m1',
'width': 'auto'
}
]
}
self.assertRenderEqual(p, '{56} ----pl,{6-}>>{--}', width=10)
class TestSegmentData(TestRender):
@add_args