diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index 22f88206..e1dd1b64 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -102,6 +102,16 @@ powerline: It must return new value of :ref:`contents ` key. +.. _dev-segments-truncate: + +``truncate`` + Like :ref:`expand function `, but for truncating + segments. Here ``amount`` means the number of display cells which must be + freed. + + This function is called for all segments before powerline starts purging + them to free space. + 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 @@ -251,10 +261,11 @@ Segment dictionary contains the following keys: ``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. + ``expand``, ``truncate`` + Partially applied :ref:`expand ` or :ref:`truncate + ` function. Accepts ``pl``, ``amount`` and + ``segment`` positional parameters, keyword parameters from :ref:`args + ` key were applied. ``startup`` Partially applied :ref:`startup function `. Accepts diff --git a/powerline/renderer.py b/powerline/renderer.py index ca62e387..a1408b5e 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,9 +1,12 @@ # vim:fileencoding=utf-8:noet -from powerline.theme import Theme -from unicodedata import east_asian_width, combining import os +from unicodedata import east_asian_width, combining +from itertools import chain + +from powerline.theme import Theme + try: NBSP = unicode(' ', 'utf-8') except NameError: @@ -262,11 +265,17 @@ class Renderer(object): # Create an ordered list of segments that can be dropped segments_priority = sorted((segment for segment in segments if segment['priority'] is not None), key=lambda segment: segment['priority'], reverse=True) - for segment in segments_priority: - current_width = self._render_length(theme, segments, divider_widths) - if current_width <= width: - break - segments.remove(segment) + no_priority_segments = filter(lambda segment: segment['priority'] is None, segments) + current_width = self._render_length(theme, segments, divider_widths) + if current_width > width: + for segment in chain(segments_priority, no_priority_segments): + if segment['truncate'] is not None: + segment['contents'] = segment['truncate'](self.pl, current_width - width, segment) + for segment in segments_priority: + if current_width <= width: + break + segments.remove(segment) + current_width = self._render_length(theme, segments, divider_widths) # Distribute the remaining space on spacer segments segments_spacers = [segment for segment in segments if segment['expand'] is not None] diff --git a/powerline/segment.py b/powerline/segment.py index 5cbe31dc..4dd283e4 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -93,7 +93,7 @@ def get_attr_func(contents_func, key, args, is_space_func=False): try: return func(pl=pl, amount=amount, segment=segment, **args) except Exception as e: - pl.exception('Exception while computing segment expansion: {0}', str(e)) + pl.exception('Exception while computing {0} function: {1}', key, str(e)) return segment['contents'] + (' ' * amount) return expand_func else: @@ -292,6 +292,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'width': None, 'align': None, 'expand': None, + 'truncate': None, 'startup': None, 'shutdown': None, 'mode': None, @@ -305,6 +306,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge startup_func = get_attr_func(_contents_func, 'startup', args) shutdown_func = getattr(_contents_func, 'shutdown', None) expand_func = get_attr_func(_contents_func, 'expand', args, True) + truncate_func = get_attr_func(_contents_func, 'truncate', args, True) if hasattr(_contents_func, 'powerline_requires_filesystem_watcher'): create_watcher = lambda: create_file_watcher(pl, common_config['watcher']) @@ -319,6 +321,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge shutdown_func = None contents_func = None expand_func = None + truncate_func = None return { 'name': name or function_name, @@ -339,6 +342,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'width': segment.get('width'), 'align': segment.get('align', 'l'), 'expand': expand_func, + 'truncate': truncate_func, 'startup': startup_func, 'shutdown': shutdown_func, 'mode': None, diff --git a/tests/test_configuration.py b/tests/test_configuration.py index e4fc7f5f..f6cbc975 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -494,6 +494,25 @@ class TestSegmentAttributes(TestRender): } self.assertRenderEqual(p, '{56} ----pl,{6-}>>{--}', width=10) + @add_args + def test_truncate(self, p, config): + def m1(divider=',', **kwargs): + return divider.join(kwargs.keys()) + divider + + def truncate(pl, amount, segment, **kwargs): + return segment['contents'][:-amount] + + m1.truncate = truncate + sys.modules['bar'] = Args(m1=m1) + config['themes/test/default']['segments'] = { + 'left': [ + { + 'function': 'bar.m1' + } + ] + } + self.assertRenderEqual(p, '{56} p{6-}>>{--}', width=4) + class TestSegmentData(TestRender): @add_args