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: Each segment dictionary has the following options:
.. _config-themes-seg-type:
``type`` ``type``
The segment type. Can be one of ``function`` (default), ``string`` or The segment type. Can be one of ``function`` (default), ``string`` or
``segments_list``: ``segments_list``:
@ -439,9 +441,13 @@ ascii Theme without any unicode characters at all
``args`` ``args``
A dict of arguments to be passed to a ``function`` segment. A dict of arguments to be passed to a ``function`` segment.
.. _config-themes-seg-align:
``align`` ``align``
Aligns the segments contents to the left (``l``), center (``c``) or 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: .. _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 either returned by a function or a static string, and the contents
can be aligned with the ``align`` property. can be aligned with the ``align`` property.
.. _config-themes-seg-priority:
``priority`` ``priority``
Optional segment priority. Segments with priority ``None`` (the default Optional segment priority. Segments with priority ``None`` (the default
priority, represented by ``null`` in json) will always be included, 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. segments. Only applicable for functions returning multiple segments.
Defaults to ``False``. Defaults to ``False``.
.. _config-themes-seg-exclude_modes:
``exclude_modes`` ``exclude_modes``
A list of modes where this segment will be excluded: The segment is A list of modes where this segment will be excluded: The segment is
included in all modes, *except* for the modes in this list. 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. a ``segment_info`` dictionary specific to one of the listed entities.
Second item must contain another dictionary: it will be used to modify the 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 resulting segment. In addition to :ref:`usual keys that describe segment
keys may be present (it is advised that *only* the following keys will be used): <dev-segments-segment>` the following keys may be present (it is advised that
*only* the following keys will be used):
.. _dev-listers-mode:
``mode`` ``mode``
Segment-specific mode. Used to alter segment highlighting. 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 theme-specific values go directly to :ref:`top-level themes
<config-themes>`. <config-themes>`.
.. _dev-segments-startup:
``startup`` ``startup``
This attribute must be a callable which accepts the following keyword This attribute must be a callable which accepts the following keyword
arguments: arguments:
@ -68,6 +70,8 @@ powerline:
used (more specific: when :py:class:`powerline.Powerline` constructor used (more specific: when :py:class:`powerline.Powerline` constructor
received true ``run_once`` argument). received true ``run_once`` argument).
.. _dev-segments-shutdown:
``shutdown`` ``shutdown``
This attribute must be a callable that accepts no arguments and shuts down 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 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. 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 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 ``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 or a list of dictionaries. String object is a short form of the following return
@ -87,6 +114,8 @@ value:
'highlight_group': [segment_name], 'highlight_group': [segment_name],
}] }]
.. _dev-segments-return:
Returned list is a list of segments treated independently, except for Returned list is a list of segments treated independently, except for
:ref:`draw_inner_divider key <dev-segments-draw_inner_divider>`. :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 No error occurs if segment has this key, but no used highlight groups use
gradient color. 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 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: try:
func = getattr(contents_func, key) func = getattr(contents_func, key)
except AttributeError: except AttributeError:
return None return None
else: else:
if args is None: if is_space_func:
return lambda: 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: else:
return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **args) 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': if segment_type == 'function':
startup_func = get_attr_func(_contents_func, 'startup', args) 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'): if hasattr(_contents_func, 'powerline_requires_filesystem_watcher'):
create_watcher = lambda: create_file_watcher(pl, common_config['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 startup_func = None
shutdown_func = None shutdown_func = None
contents_func = None contents_func = None
expand_func = None
return { return {
'name': name or function_name, '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', []), 'include_modes': segment.get('include_modes', []),
'width': segment.get('width'), 'width': segment.get('width'),
'align': segment.get('align', 'l'), 'align': segment.get('align', 'l'),
'expand': None, 'expand': expand_func,
'startup': startup_func, 'startup': startup_func,
'shutdown': shutdown_func, 'shutdown': shutdown_func,
'mode': None, 'mode': None,

View File

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