Add output_width used for various shells

This commit is contained in:
ZyX 2014-08-10 19:05:53 +04:00
parent 72b7744b98
commit c97ab8055a
5 changed files with 74 additions and 38 deletions

View File

@ -27,15 +27,16 @@ class PowerlinePromptManager(PromptManager):
powerline = self.non_prompt_powerline
else:
powerline = self.prompt_powerline
res, res_nocolor = powerline.render(
output_raw=True,
res = powerline.render(
output_width=True,
output_raw=not color,
width=width,
matcher_info=name,
segment_info=self.powerline_segment_info,
)
self.txtwidth = len(res_nocolor)
self.width = self.txtwidth
ret = res if color else res_nocolor
self.txtwidth = res[-1]
self.width = res[-1]
ret = res[0] if color else res[1]
if name == 'rewrite':
return RewriteResult(ret)
else:

View File

@ -33,11 +33,14 @@ class PowerlinePrompt(BasePrompt):
return string(self.p_str)
def set_p_str(self, width=None):
self.p_str, self.p_str_nocolor = (
self.powerline.render(output_raw=True,
segment_info=self.powerline_segment_info,
matcher_info=self.powerline_prompt_type,
width=width)
self.p_str, self.p_str_nocolor, self.powerline_prompt_width = (
self.powerline.render(
output_raw=True,
output_width=True,
segment_info=self.powerline_segment_info,
matcher_info=self.powerline_prompt_type,
width=width
)
)
@staticmethod
@ -58,7 +61,7 @@ class PowerlinePrompt1(PowerlinePrompt):
def set_p_str(self):
super(PowerlinePrompt1, self).set_p_str()
self.nrspaces = len(self.rspace.search(self.p_str_nocolor).group())
self.prompt_text_len = len(self.p_str_nocolor) - self.nrspaces
self.prompt_text_len = self.powerline_prompt_width - self.nrspaces
self.powerline_last_in['nrspaces'] = self.nrspaces
self.powerline_last_in['prompt_text_len'] = self.prompt_text_len

View File

@ -15,11 +15,15 @@ except ImportError:
pass
def construct_returned_value(rendered_highlighted, segments, output_raw):
if output_raw:
return rendered_highlighted, ''.join((segment['_rendered_raw'] for segment in segments))
else:
def construct_returned_value(rendered_highlighted, segments, width, output_raw, output_width):
if not (output_raw or output_width):
return rendered_highlighted
else:
return (
(rendered_highlighted,)
+ ((''.join((segment['_rendered_raw'] for segment in segments)),) if output_raw else ())
+ ((width,) if output_width else ())
)
class Renderer(object):
@ -188,7 +192,7 @@ class Renderer(object):
for line in range(theme.get_line_number() - 1, 0, -1):
yield self.render(side=None, line=line, **kwargs)
def render(self, mode=None, width=None, side=None, line=0, output_raw=False, segment_info=None, matcher_info=None):
def render(self, mode=None, width=None, side=None, line=0, output_raw=False, output_width=False, segment_info=None, matcher_info=None):
'''Render all segments.
When a width is provided, low-priority segments are dropped one at
@ -213,6 +217,11 @@ class Renderer(object):
Changes the output: if this parameter is ``True`` then in place of
one string this method outputs a pair ``(colored_string,
colorless_string)``.
:param bool output_width:
Changes the output: if this parameter is ``True`` then in place of
one string this method outputs a pair ``(colored_string,
string_width)``. Returns a three-tuple if ``output_raw`` is also
``True``: ``(colored_string, colorless_string, string_width)``.
:param dict segment_info:
Segment information. See also ``.get_segment_info()`` method.
:param matcher_info:
@ -225,11 +234,24 @@ class Renderer(object):
side=side,
line=line,
output_raw=output_raw,
output_width=output_width,
segment_info=segment_info,
theme=theme,
)
def do_render(self, mode, width, side, line, output_raw, segment_info, theme):
def compute_divider_widths(self, theme):
return {
'left': {
'hard': self.strwidth(theme.get_divider('left', 'hard')),
'soft': self.strwidth(theme.get_divider('left', 'soft')),
},
'right': {
'hard': self.strwidth(theme.get_divider('right', 'hard')),
'soft': self.strwidth(theme.get_divider('right', 'soft')),
},
}
def do_render(self, mode, width, side, line, output_raw, output_width, segment_info, theme):
'''Like Renderer.render(), but accept theme in place of matcher_info
'''
segments = theme.get_segments(side, line, self.get_segment_info(segment_info, mode))
@ -246,35 +268,36 @@ class Renderer(object):
)
]
current_width = 0
if not width:
# No width specified, so we don't need to crop or pad anything
if output_width:
current_width = self._render_length(theme, segments, self.compute_divider_widths(theme))
return construct_returned_value(''.join([
segment['_rendered_hl']
for segment in self._render_segments(theme, segments)
]) + self.hlstyle(), segments, output_raw)
]) + self.hlstyle(), segments, current_width, output_raw, output_width)
divider_lengths = {
'left': {
'hard': self.strwidth(theme.get_divider('left', 'hard')),
'soft': self.strwidth(theme.get_divider('left', 'soft')),
},
'right': {
'hard': self.strwidth(theme.get_divider('right', 'hard')),
'soft': self.strwidth(theme.get_divider('right', 'soft')),
},
}
divider_widths = self.compute_divider_widths(theme)
# 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:
if self._render_length(theme, segments, divider_lengths) <= width:
current_width = self._render_length(theme, segments, divider_widths)
if current_width <= width:
break
segments.remove(segment)
# Distribute the remaining space on spacer segments
segments_spacers = [segment for segment in segments if segment['width'] == 'auto']
if segments_spacers:
distribute_len, distribute_len_remainder = divmod(width - sum([segment['_len'] for segment in segments]), len(segments_spacers))
if not segments_priority:
# Update segment['_len'] and current_width if not already done
# (is not done in shells where there is nothing to remove
# because “priority” key is not specified)
current_width = self._render_length(theme, segments, divider_widths)
distribute_len, distribute_len_remainder = divmod(width - current_width, len(segments_spacers))
for segment in segments_spacers:
if segment['align'] == 'l':
segment['_space_right'] += distribute_len
@ -285,12 +308,17 @@ class Renderer(object):
segment['_space_left'] += space_side + space_side_remainder
segment['_space_right'] += space_side
segments_spacers[0]['_space_right'] += distribute_len_remainder
# `_len` key is not needed anymore, but current_width should have an
# actual value for various bindings.
current_width = width
elif output_width:
current_width = self._render_length(theme, segments, divider_widths)
rendered_highlighted = ''.join([segment['_rendered_hl'] for segment in self._render_segments(theme, segments)]) + self.hlstyle()
return construct_returned_value(rendered_highlighted, segments, output_raw)
return construct_returned_value(rendered_highlighted, segments, current_width, output_raw, output_width)
def _render_length(self, theme, segments, divider_lengths):
def _render_length(self, theme, segments, divider_widths):
'''Update segments lengths and return them
'''
segments_len = len(segments)
@ -316,7 +344,7 @@ class Renderer(object):
draw_divider = segment['draw_' + divider_type + '_divider']
segment_len += segment['_space_left'] + segment['_space_right'] + outer_padding
if draw_divider:
segment_len += divider_lengths[side][divider_type] + divider_spaces
segment_len += divider_widths[side][divider_type] + divider_spaces
segment['_len'] = segment_len
ret += segment_len

View File

@ -32,5 +32,9 @@ class IpythonRenderer(ShellRenderer):
if 'theme' in match:
match['theme'].shutdown()
def render(self, *args, **kwargs):
# XXX super(ShellRenderer), *not* super(IpythonRenderer)
return super(ShellRenderer, self).render(*args, **kwargs)
renderer = IpythonRenderer

View File

@ -34,22 +34,22 @@ class ShellRenderer(Renderer):
width = kwargs.pop('width', None)
local_theme = segment_info.get('local_theme')
if client_id and local_theme:
output_raw = False
output_width = False
try:
width = self.old_widths[key]
except KeyError:
pass
else:
output_raw = True
output_width = True
ret = super(ShellRenderer, self).render(
output_raw=output_raw,
output_width=output_width,
width=width,
matcher_info=local_theme,
segment_info=segment_info,
*args, **kwargs
)
if output_raw:
self.old_widths[key] = len(ret[1])
if output_width:
self.old_widths[key] = ret[1]
ret = ret[0]
return ret