diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index 92f0887c..f10006b9 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -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: diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index a007cdb0..e10e4d5c 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -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 diff --git a/powerline/renderer.py b/powerline/renderer.py index c7e55883..63bc1454 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -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 diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index cf31e119..7d7e4c55 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -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 diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index dd9906a8..931c1178 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -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