Add support for clickable tab names

Fixes #1353
This commit is contained in:
Foo 2015-05-17 14:16:48 +03:00
parent e2168e2167
commit f5d85b7294
5 changed files with 169 additions and 59 deletions

View File

@ -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 <dev-segments-contents>`, :ref:`after
<config-themes-seg-after>`, :ref:`before <config-themes-seg-before>`) will
be ignored.
.. _dev-segments-draw_inner_divider:
``draw_hard_divider``, ``draw_soft_divider``, ``draw_inner_divider``

View File

@ -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",

View File

@ -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):

View File

@ -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),

View File

@ -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