diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index 3fea00a2..03a5108c 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -153,10 +153,25 @@ All keys in segments returned by the function override those obtained from Detailed description of used dictionary keys: +.. _dev-segments-contents: + ``contents`` Text displayed by segment. Should be a ``unicode`` (Python2) or ``str`` (Python3) instance. +``literal_contents`` + Text that needs to be output literally (i.e. without passing through + :py:meth:`powerline.renderer.strwidth` to determine length, through + :py:meth:`powerline.renderer.escape` to escape special characters and + through :py:meth:`powerline.renderer.hl` to highlight it). Should be a tuple + ``(contents_length, contents)`` where ``contents_length`` is an integer and + ``contents`` is a ``unicode`` (Python2) or ``str`` (Python3) instance. + + If this key is present and its second value is true then other contents keys + (:ref:`contents `, :ref:`after + `, :ref:`before `) will + be ignored. + .. _dev-segments-draw_inner_divider: ``draw_hard_divider``, ``draw_soft_divider``, ``draw_inner_divider`` diff --git a/powerline/config_files/themes/vim/tabline.json b/powerline/config_files/themes/vim/tabline.json index 31112cb1..48bae6b8 100644 --- a/powerline/config_files/themes/vim/tabline.json +++ b/powerline/config_files/themes/vim/tabline.json @@ -7,6 +7,9 @@ "function": "powerline.listers.vim.tablister", "exclude_function": "single_tab", "segments": [ + { + "function": "tab" + }, { "function": "tabnr", "after": " ", @@ -29,6 +32,12 @@ } ] }, + { + "function": "tab", + "args": { + "end": true + } + }, { "type": "segment_list", "function": "powerline.listers.vim.bufferlister", diff --git a/powerline/renderer.py b/powerline/renderer.py index 13c7a04f..76a7e796 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -391,7 +391,10 @@ class Renderer(object): segment['contents'] = translate_np(segment['contents']) if calculate_contents_len: for segment in segments: - segment['_contents_len'] = self.strwidth(segment['contents']) + if segment['literal_contents'][1]: + segment['_contents_len'] = segment['literal_contents'][0] + else: + segment['_contents_len'] = self.strwidth(segment['contents']) def _render_length(self, theme, segments, divider_widths): '''Update segments lengths and return them @@ -399,24 +402,52 @@ class Renderer(object): segments_len = len(segments) ret = 0 divider_spaces = theme.get_spaces() + prev_segment = theme.EMPTY_SEGMENT + try: + first_segment = next(iter(( + segment + for segment in segments + if not segment['literal_contents'][1] + ))) + except StopIteration: + first_segment = None + try: + last_segment = next(iter(( + segment + for segment in reversed(segments) + if not segment['literal_contents'][1] + ))) + except StopIteration: + last_segment = None for index, segment in enumerate(segments): side = segment['side'] segment_len = segment['_contents_len'] + if not segment['literal_contents'][1]: + if side == 'left': + if segment is not last_segment: + compare_segment = next(iter(( + segment + for segment in segments[index + 1:] + if not segment['literal_contents'][1] + ))) + else: + compare_segment = theme.EMPTY_SEGMENT + else: + compare_segment = prev_segment - prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT - next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT - compare_segment = next_segment if side == 'left' else prev_segment - divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard' + divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard' - outer_padding = int(bool( - (index == 0 and side == 'left') or - (index == segments_len - 1 and side == 'right') - )) + outer_padding = int(bool( + segment is first_segment + if side == 'left' else + segment is last_segment + )) - draw_divider = segment['draw_' + divider_type + '_divider'] - segment_len += outer_padding - if draw_divider: - segment_len += divider_widths[side][divider_type] + divider_spaces + draw_divider = segment['draw_' + divider_type + '_divider'] + segment_len += outer_padding + if draw_divider: + segment_len += divider_widths[side][divider_type] + divider_spaces + prev_segment = segment segment['_len'] = segment_len ret += segment_len @@ -435,61 +466,92 @@ class Renderer(object): ''' segments_len = len(segments) divider_spaces = theme.get_spaces() + prev_segment = theme.EMPTY_SEGMENT + try: + first_segment = next(iter(( + segment + for segment in segments + if not segment['literal_contents'][1] + ))) + except StopIteration: + first_segment = None + try: + last_segment = next(iter(( + segment + for segment in reversed(segments) + if not segment['literal_contents'][1] + ))) + except StopIteration: + last_segment = None for index, segment in enumerate(segments): side = segment['side'] - prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT - next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT - compare_segment = next_segment if side == 'left' else prev_segment - outer_padding = int(bool( - (index == 0 and side == 'left') or - (index == segments_len - 1 and side == 'right') - )) * ' ' - divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard' - - divider_highlighted = '' - contents_raw = segment['contents'] - contents_highlighted = '' - draw_divider = segment['draw_' + divider_type + '_divider'] - - # XXX Make sure self.hl() calls are called in the same order - # segments are displayed. This is needed for Vim renderer to work. - if draw_divider: - divider_raw = self.escape(theme.get_divider(side, divider_type)) + if not segment['literal_contents'][1]: if side == 'left': - contents_raw = outer_padding + contents_raw + (divider_spaces * ' ') + if segment is not last_segment: + compare_segment = next(iter(( + segment + for segment in segments[index + 1:] + if not segment['literal_contents'][1] + ))) + else: + compare_segment = theme.EMPTY_SEGMENT else: - contents_raw = (divider_spaces * ' ') + contents_raw + outer_padding + compare_segment = prev_segment + outer_padding = int(bool( + segment is first_segment + if side == 'left' else + segment is last_segment + )) * ' ' + divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard' - if divider_type == 'soft': - divider_highlight_group_key = 'highlight' if segment['divider_highlight_group'] is None else 'divider_highlight' - divider_fg = segment[divider_highlight_group_key]['fg'] - divider_bg = segment[divider_highlight_group_key]['bg'] - else: - divider_fg = segment['highlight']['bg'] - divider_bg = compare_segment['highlight']['bg'] + divider_highlighted = '' + contents_raw = segment['contents'] + contents_highlighted = '' + draw_divider = segment['draw_' + divider_type + '_divider'] - if side == 'left': - if render_highlighted: - contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) - divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) - segment['_rendered_raw'] = contents_raw + divider_raw - segment['_rendered_hl'] = contents_highlighted + divider_highlighted + # XXX Make sure self.hl() calls are called in the same order + # segments are displayed. This is needed for Vim renderer to work. + if draw_divider: + divider_raw = self.escape(theme.get_divider(side, divider_type)) + if side == 'left': + contents_raw = outer_padding + contents_raw + (divider_spaces * ' ') + else: + contents_raw = (divider_spaces * ' ') + contents_raw + outer_padding + + if divider_type == 'soft': + divider_highlight_group_key = 'highlight' if segment['divider_highlight_group'] is None else 'divider_highlight' + divider_fg = segment[divider_highlight_group_key]['fg'] + divider_bg = segment[divider_highlight_group_key]['bg'] + else: + divider_fg = segment['highlight']['bg'] + divider_bg = compare_segment['highlight']['bg'] + + if side == 'left': + if render_highlighted: + contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) + divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) + segment['_rendered_raw'] = contents_raw + divider_raw + segment['_rendered_hl'] = contents_highlighted + divider_highlighted + else: + if render_highlighted: + divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) + contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) + segment['_rendered_raw'] = divider_raw + contents_raw + segment['_rendered_hl'] = divider_highlighted + contents_highlighted else: - if render_highlighted: - divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) - contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) - segment['_rendered_raw'] = divider_raw + contents_raw - segment['_rendered_hl'] = divider_highlighted + contents_highlighted + if side == 'left': + contents_raw = outer_padding + contents_raw + else: + contents_raw = contents_raw + outer_padding + + contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) + segment['_rendered_raw'] = contents_raw + segment['_rendered_hl'] = contents_highlighted + prev_segment = segment else: - if side == 'left': - contents_raw = outer_padding + contents_raw - else: - contents_raw = contents_raw + outer_padding - - contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) - segment['_rendered_raw'] = contents_raw - segment['_rendered_hl'] = contents_highlighted + segment['_rendered_raw'] = ' ' * segment['literal_contents'][0] + segment['_rendered_hl'] = segment['literal_contents'][1] yield segment def escape(self, string): diff --git a/powerline/segment.py b/powerline/segment.py index 7864cf31..c83bf6f2 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -127,6 +127,8 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, mode, colors colorscheme, ) new_pslen = len(parsed_segments) + while parsed_segments[new_pslen - 1]['literal_contents'][1]: + new_pslen -= 1 if new_pslen > old_pslen + 1 and draw_inner_divider is not None: for i in range(old_pslen, new_pslen - 1) if side == 'left' else range(old_pslen + 1, new_pslen): parsed_segments[i]['draw_soft_divider'] = draw_inner_divider @@ -134,6 +136,8 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, mode, colors def set_segment_highlighting(pl, colorscheme, segment, mode): + if segment['literal_contents'][1]: + return True try: highlight_group_prefix = segment['highlight_group_prefix'] except KeyError: @@ -228,6 +232,7 @@ get_fallback_segment = { 'before': None, 'after': None, 'contents': '', + 'literal_contents': (0, ''), 'priority': None, 'draw_soft_divider': True, 'draw_hard_divider': True, @@ -245,6 +250,7 @@ get_fallback_segment = { '_contents_len': None, }.copy + def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, get_module_attr, top_theme): data = { 'default_module': default_module or 'powerline.segments.' + ext, @@ -373,6 +379,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge ) ), 'contents': None, + 'literal_contents': None, 'priority': None, 'draw_soft_divider': None, 'draw_hard_divider': None, @@ -421,6 +428,7 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'after': get_key(False, segment, module, function_name, name, 'after', ''), 'contents_func': contents_func, 'contents': contents, + 'literal_contents': (0, ''), 'priority': segment.get('priority', None), 'draw_hard_divider': segment.get('draw_hard_divider', True), 'draw_soft_divider': segment.get('draw_soft_divider', True), diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index 77702191..5191e6fb 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -740,3 +740,19 @@ def csv_col_current(pl, segment_info, display_name='auto', name_format=' ({colum 'contents': name_format.format(column_name=column_name), 'highlight_groups': ['csv:column_name', 'csv'], }] if column_name else []) + + +@requires_segment_info +def tab(pl, segment_info, end=False): + '''Mark start of the clickable region for tabpage + + :param bool end: + In place of starting region for the current tab end it. + ''' + try: + return [{ + 'contents': None, + 'literal_contents': (0, '%{tabnr}T'.format(tabnr=('' if end else segment_info['tabnr']))), + }] + except KeyError: + return None