diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 326de4ba..c3096b9c 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -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. diff --git a/docs/source/develop/listers.rst b/docs/source/develop/listers.rst index 8c7b439e..85513718 100644 --- a/docs/source/develop/listers.rst +++ b/docs/source/develop/listers.rst @@ -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 +` 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. diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index fe802818..22f88206 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -51,6 +51,8 @@ powerline: theme-specific values go directly to :ref:`top-level 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 `. + * Any arguments found in user configuration for the given segment (i.e. + :ref:`args key `). + + It must return new value of :ref:`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 `. @@ -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-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 ` or + function name (last component of the :ref:`function key + `). May be ``None``. + + ``type`` + :ref:`Segment 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 ` or :ref:`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 + `. May be ``None``. + + ``priority`` + :ref:`Segment 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 `. + + ``side`` + Segment side: ``right`` or ``left``. + + ``exclude_modes``, ``include_modes`` + :ref:`Mode display control lists `. May be + empty, but may not be ``None``. + + ``width``, ``align`` + :ref:`Width and align options `. May be ``None``. + + ``expand`` + Partially applied :ref:`expand function `. Accepts + ``pl``, ``amount`` and ``segment`` positional parameters, keyword parameters + from :ref:`args ` key were applied. + + ``startup`` + Partially applied :ref:`startup function `. Accepts + ``pl`` and ``shutdown_event`` positional parameters, keyword parameters from + :ref:`args ` key were applied. + + ``shutdown`` + :ref:`Shutdown function `. Accepts no argument. + + ``mode`` + :ref:`Segment-specific mode `. May be ``None``. + Segments layout =============== diff --git a/powerline/segment.py b/powerline/segment.py index 00a48a30..5cbe31dc 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -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, diff --git a/powerline/theme.py b/powerline/theme.py index 2fbdd79e..2558ca0e 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -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) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index de15ab88..e4fc7f5f 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -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