diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index 8f043e43..63181e44 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -283,6 +283,16 @@ ascii Theme without any unicode characters at all background colors, while the ``soft`` dividers are used to divide segments with the same background color. +.. _config-themes-cursor_space: + +``cursor_space`` + Space reserved for user input in shell bindings. It is measured in per + cents. + +``cursor_columns`` + Space reserved for user input in shell bindings. Unlike :ref:`cursor_space + ` it is measured in absolute amout of columns. + .. _config-themes-segment_data: ``segment_data`` diff --git a/powerline/bindings/bash/powerline.sh b/powerline/bindings/bash/powerline.sh index eb705a7c..93670555 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -41,7 +41,12 @@ _powerline_init_tmux_support() { _powerline_prompt() { # Arguments: side, last_exit_code, jobnum - $POWERLINE_COMMAND shell $1 -w "${COLUMNS:-$(_powerline_columns_fallback)}" -r bash_prompt --last_exit_code=$2 --jobnum=$3 + $POWERLINE_COMMAND shell $1 \ + --width="${COLUMNS:-$(_powerline_columns_fallback)}" \ + -r bash_prompt \ + --last_exit_code=$2 \ + --jobnum=$3 \ + --renderer_arg="client_id=$$" } _powerline_set_prompt() { diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index 56f6d52e..6dc100c0 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -34,6 +34,9 @@ function powerline-setup set -l addargs "--last_exit_code=\$status" set -l addargs "$addargs --last_pipe_status=\$status" set -l addargs "$addargs --jobnum=(jobs -p | wc -l)" + # One random value has an 1/32767 = 0.0031% probability of having + # the same value in two shells + set -l addargs "$addargs --renderer_arg=client_id="(random) set -l addargs "$addargs --width=\$_POWERLINE_COLUMNS" set -l promptside set -l rpromptpast diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index 92f0887c..ec3a4769 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -22,20 +22,20 @@ class PowerlinePromptManager(PromptManager): self.shell = shell def render(self, name, color=True, *args, **kwargs): - width = None if name == 'in' else self.width if name == 'out' or name == 'rewrite': powerline = self.non_prompt_powerline else: powerline = self.prompt_powerline - res, res_nocolor = powerline.render( - output_raw=True, - width=width, + res = powerline.render( + side='left', + output_width=True, + output_raw=not color, 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: @@ -43,12 +43,12 @@ class PowerlinePromptManager(PromptManager): class ConfigurableIpythonPowerline(IpythonPowerline): - def __init__(self, ip, is_prompt): + def __init__(self, ip, is_prompt, old_widths): config = ip.config.Powerline self.config_overrides = config.get('config_overrides') self.theme_overrides = config.get('theme_overrides', {}) self.paths = config.get('paths') - super(ConfigurableIpythonPowerline, self).__init__(is_prompt) + super(ConfigurableIpythonPowerline, self).__init__(is_prompt, old_widths) old_prompt_manager = None @@ -58,8 +58,9 @@ def load_ipython_extension(ip): global old_prompt_manager old_prompt_manager = ip.prompt_manager - prompt_powerline = ConfigurableIpythonPowerline(ip, True) - non_prompt_powerline = ConfigurableIpythonPowerline(ip, False) + old_widths = {} + prompt_powerline = ConfigurableIpythonPowerline(ip, True, old_widths) + non_prompt_powerline = ConfigurableIpythonPowerline(ip, False, old_widths) ip.prompt_manager = PowerlinePromptManager( prompt_powerline=prompt_powerline, diff --git a/powerline/bindings/ipython/pre_0_11.py b/powerline/bindings/ipython/pre_0_11.py index a007cdb0..ed6ad75e 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -32,12 +32,15 @@ class PowerlinePrompt(BasePrompt): self.set_p_str() 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) + def set_p_str(self): + self.p_str, self.p_str_nocolor, self.powerline_prompt_width = ( + self.powerline.render( + side='left', + output_raw=True, + output_width=True, + segment_info=self.powerline_segment_info, + matcher_info=self.powerline_prompt_type, + ) ) @staticmethod @@ -58,20 +61,21 @@ 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.powerline_last_in['nrspaces'] = self.nrspaces - self.powerline_last_in['prompt_text_len'] = self.prompt_text_len def auto_rewrite(self): - return RewriteResult(self.other_powerline.render(matcher_info='rewrite', width=self.prompt_text_len, segment_info=self.powerline_segment_info) - + (' ' * self.nrspaces)) + return RewriteResult(self.other_powerline.render( + side='left', + matcher_info='rewrite', + segment_info=self.powerline_segment_info) + (' ' * self.nrspaces) + ) class PowerlinePromptOut(PowerlinePrompt): powerline_prompt_type = 'out' def set_p_str(self): - super(PowerlinePromptOut, self).set_p_str(width=self.powerline_last_in['prompt_text_len']) + super(PowerlinePromptOut, self).set_p_str() spaces = ' ' * self.powerline_last_in['nrspaces'] self.p_str += spaces self.p_str_nocolor += spaces @@ -82,21 +86,22 @@ class PowerlinePrompt2(PowerlinePromptOut): class ConfigurableIpythonPowerline(IpythonPowerline): - def __init__(self, is_prompt, config_overrides=None, theme_overrides={}, paths=None): + def __init__(self, is_prompt, old_widths, config_overrides=None, theme_overrides={}, paths=None): self.config_overrides = config_overrides self.theme_overrides = theme_overrides self.paths = paths - super(ConfigurableIpythonPowerline, self).__init__(is_prompt) + super(ConfigurableIpythonPowerline, self).__init__(is_prompt, old_widths) def setup(**kwargs): ip = get_ipython() - prompt_powerline = ConfigurableIpythonPowerline(True, **kwargs) - non_prompt_powerline = ConfigurableIpythonPowerline(False, **kwargs) + old_widths = {} + prompt_powerline = ConfigurableIpythonPowerline(True, old_widths, **kwargs) + non_prompt_powerline = ConfigurableIpythonPowerline(False, old_widths, **kwargs) def late_startup_hook(): - last_in = {'nrspaces': 0, 'prompt_text_len': None} + last_in = {'nrspaces': 0} for attr, prompt_class, powerline, other_powerline in ( ('prompt1', PowerlinePrompt1, prompt_powerline, non_prompt_powerline), ('prompt2', PowerlinePrompt2, prompt_powerline, None), diff --git a/powerline/bindings/shell/powerline.sh b/powerline/bindings/shell/powerline.sh index e78e3625..d197c5be 100644 --- a/powerline/bindings/shell/powerline.sh +++ b/powerline/bindings/shell/powerline.sh @@ -134,7 +134,12 @@ _powerline_set_jobs() { _powerline_prompt() { # Arguments: side, exit_code _powerline_set_jobs - $POWERLINE_COMMAND shell $1 -w "${COLUMNS:-$(_powerline_columns_fallback)}" $_POWERLINE_RENDERER_ARG --last_exit_code=$2 --jobnum=$_POWERLINE_JOBS + $POWERLINE_COMMAND shell $1 \ + --width="${COLUMNS:-$(_powerline_columns_fallback)}" \ + $_POWERLINE_RENDERER_ARG \ + --renderer_arg="client_id=$$" \ + --last_exit_code=$2 \ + --jobnum=$_POWERLINE_JOBS } _powerline_setup_prompt() { diff --git a/powerline/bindings/tcsh/powerline.tcsh b/powerline/bindings/tcsh/powerline.tcsh index 5ac0cf33..48155466 100644 --- a/powerline/bindings/tcsh/powerline.tcsh +++ b/powerline/bindings/tcsh/powerline.tcsh @@ -24,11 +24,11 @@ if ( { $POWERLINE_CONFIG shell --shell=tcsh uses prompt } ) then if ( $?POWERLINE_NO_TCSH_ABOVE || $?POWERLINE_NO_SHELL_ABOVE ) then alias _powerline_above true else - alias _powerline_above '$POWERLINE_COMMAND shell above --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS' + alias _powerline_above '$POWERLINE_COMMAND shell above --renderer_arg=client_id=$$ --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS' endif - alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS`"' - alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS` "' + alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND shell left -r tcsh_prompt --renderer_arg=client_id=$$ --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS`"' + alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND shell right -r tcsh_prompt --renderer_arg=client_id=$$ --last_exit_code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS` "' alias _powerline_set_columns 'set POWERLINE_COLUMNS=`stty size|cut -d" " -f2` ; set POWERLINE_COLUMNS=`expr $POWERLINE_COLUMNS - 2`' alias precmd 'set POWERLINE_STATUS=$? ; '"`alias precmd`"' ; _powerline_set_columns ; _powerline_above ; _powerline_set_prompt ; _powerline_set_rprompt' diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 626ee58c..0bc2c50e 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -129,21 +129,22 @@ _powerline_setup_prompt() { POWERLINE_COMMAND=( "$($POWERLINE_CONFIG shell command)" ) fi - local add_args='--last_exit_code=$?' + local add_args='-r zsh_prompt' + add_args+=' --last_exit_code=$?' add_args+=' --last_pipe_status="$pipestatus"' add_args+=' --renderer_arg="client_id=$$"' add_args+=' --jobnum=$_POWERLINE_JOBNUM' - local new_args_2=' -R parser_state=${(%%):-%_}' - new_args_2+=' -R local_theme=continuation' - local add_args_3=$add_args' -R local_theme=select' + local new_args_2=' --renderer_arg="parser_state=${(%%):-%_}"' + new_args_2+=' --renderer_arg="local_theme=continuation"' + local add_args_3=$add_args' --renderer_arg="local_theme=select"' local add_args_2=$add_args$new_args_2 add_args+=' --width=$(( ${COLUMNS:-$(_powerline_columns_fallback)} - 1 ))' local add_args_r2=$add_args$new_args_2 - PS1='$($POWERLINE_COMMAND shell aboveleft -r zsh_prompt '$add_args')' - RPS1='$($POWERLINE_COMMAND shell right -r zsh_prompt '$add_args')' - PS2='$($POWERLINE_COMMAND shell left -r zsh_prompt '$add_args_2')' - RPS2='$($POWERLINE_COMMAND shell right -r zsh_prompt '$add_args_r2')' - PS3='$($POWERLINE_COMMAND shell left -r zsh_prompt '$add_args_3')' + PS1='$($POWERLINE_COMMAND shell aboveleft '$add_args')' + RPS1='$($POWERLINE_COMMAND shell right '$add_args')' + PS2='$($POWERLINE_COMMAND shell left '$add_args_2')' + RPS2='$($POWERLINE_COMMAND shell right '$add_args_r2')' + PS3='$($POWERLINE_COMMAND shell left '$add_args_3')' fi } diff --git a/powerline/config_files/themes/ipython/in.json b/powerline/config_files/themes/ipython/in.json index 3d471ec5..c0086509 100644 --- a/powerline/config_files/themes/ipython/in.json +++ b/powerline/config_files/themes/ipython/in.json @@ -3,7 +3,8 @@ "segments": { "left": [ { - "name": "virtualenv" + "name": "virtualenv", + "priority": 10 }, { "type": "string", diff --git a/powerline/config_files/themes/shell/default.json b/powerline/config_files/themes/shell/default.json index 25867a30..56e3ce70 100644 --- a/powerline/config_files/themes/shell/default.json +++ b/powerline/config_files/themes/shell/default.json @@ -7,29 +7,36 @@ "name": "mode" }, { - "name": "hostname" + "name": "hostname", + "priority": 10 }, { - "name": "user" + "name": "user", + "priority": 30 }, { - "name": "virtualenv" + "name": "virtualenv", + "priority": 50 }, { - "name": "cwd" + "name": "cwd", + "priority": 10 }, { "module": "powerline.segments.shell", - "name": "jobnum" + "name": "jobnum", + "priority": 20 } ], "right": [ { "module": "powerline.segments.shell", - "name": "last_pipe_status" + "name": "last_pipe_status", + "priority": 10 }, { - "name": "branch" + "name": "branch", + "priority": 40 } ] } diff --git a/powerline/config_files/themes/shell/default_leftonly.json b/powerline/config_files/themes/shell/default_leftonly.json index d6c49de1..ca7dd15d 100644 --- a/powerline/config_files/themes/shell/default_leftonly.json +++ b/powerline/config_files/themes/shell/default_leftonly.json @@ -3,27 +3,34 @@ "segments": { "left": [ { - "name": "hostname" + "name": "hostname", + "priority": 10 }, { - "name": "user" + "name": "user", + "priority": 30 }, { - "name": "virtualenv" + "name": "virtualenv", + "priority": 50 }, { - "name": "branch" + "name": "branch", + "priority": 40 }, { - "name": "cwd" + "name": "cwd", + "priority": 10 }, { "module": "powerline.segments.shell", - "name": "jobnum" + "name": "jobnum", + "priority": 20 }, { + "module": "powerline.segments.shell", "name": "last_status", - "module": "powerline.segments.shell" + "priority": 10 } ] } diff --git a/powerline/ipython.py b/powerline/ipython.py index 80327e72..5fc66b34 100644 --- a/powerline/ipython.py +++ b/powerline/ipython.py @@ -23,12 +23,17 @@ class RewriteResult(object): class IpythonPowerline(Powerline): - def __init__(self, is_prompt): + def __init__(self, is_prompt, old_widths): super(IpythonPowerline, self).__init__( 'ipython', renderer_module=('ipython_prompt' if is_prompt else 'ipython'), use_daemon_threads=True ) + self.old_widths = old_widths + + def create_renderer(self, *args, **kwargs): + super(IpythonPowerline, self).create_renderer(*args, **kwargs) + self.renderer.old_widths = self.old_widths def get_config_paths(self): if self.paths: diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 7d894049..a1749790 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -1279,6 +1279,8 @@ spaces_spec = Spec().unsigned().cmp( ).copy common_theme_spec = Spec( default_module=segment_module_spec().optional(), + cursor_space=Spec().type(int, float).cmp('le', 100).cmp('gt', 0).optional(), + cursor_columns=Spec().type(int).cmp('gt', 0).optional(), ).context_message('Error while loading theme').copy top_theme_spec = common_theme_spec().update( dividers=dividers_spec(), diff --git a/powerline/renderer.py b/powerline/renderer.py index 6ee245aa..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,14 +217,44 @@ 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: Matcher information. Is processed in ``.get_theme()`` method. ''' theme = self.get_theme(matcher_info) - segments = theme.get_segments(side, line, self.get_segment_info(segment_info, mode)) + return self.do_render( + mode=mode, + width=width, + side=side, + line=line, + output_raw=output_raw, + output_width=output_width, + segment_info=segment_info, + theme=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)) # Handle excluded/included segments for the current mode segments = [ self._get_highlighting(segment, segment['mode'] or mode) @@ -234,37 +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')), - }, - } - - length = self._render_length(theme, segments, divider_lengths) + 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 @@ -275,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) @@ -306,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 a2c071cc..7d7e4c55 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -21,7 +21,7 @@ class IpythonRenderer(ShellRenderer): except KeyError: match['theme'] = Theme( theme_config=match['config'], - top_theme_config=self.theme_config, + main_theme_config=self.theme_config, **self.theme_kwargs ) return match['theme'] @@ -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 5358871c..360acafc 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -1,6 +1,6 @@ # vim:fileencoding=utf-8:noet -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import, unicode_literals, division, print_function from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE @@ -23,6 +23,60 @@ class ShellRenderer(Renderer): character_translations = Renderer.character_translations.copy() + def __init__(self, *args, **kwargs): + super(ShellRenderer, self).__init__(*args, **kwargs) + self.old_widths = {} + + def render(self, segment_info, **kwargs): + local_theme = segment_info.get('local_theme') + return super(ShellRenderer, self).render( + matcher_info=local_theme, + segment_info=segment_info, + **kwargs + ) + + def do_render(self, output_width, segment_info, side, theme, width=None, **kwargs): + if isinstance(segment_info, dict): + client_id = segment_info.get('client_id') + else: + client_id = None + local_key = (client_id, side, None if theme is self.theme else id(theme)) + key = (client_id, side, None) + did_width = False + if local_key[-1] != key[-1] and side == 'left': + try: + width = self.old_widths[key] + except KeyError: + pass + else: + did_width = True + if not did_width: + if width is not None: + if theme.cursor_space_multiplier is not None: + width = int(width * theme.cursor_space_multiplier) + elif theme.cursor_columns: + width -= theme.cursor_columns + + if side == 'right': + try: + width -= self.old_widths[(client_id, 'left', local_key[-1])] + except KeyError: + pass + res = super(ShellRenderer, self).do_render( + output_width=True, + width=width, + theme=theme, + segment_info=segment_info, + side=side, + **kwargs + ) + self.old_widths[local_key] = res[-1] + ret = res if output_width else res[:-1] + if len(ret) == 1: + return ret[0] + else: + return ret + def hlstyle(self, fg=None, bg=None, attr=None): '''Highlight a segment. diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 28ff2202..f4c2bf74 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -57,7 +57,7 @@ class VimRenderer(Renderer): try: return match['theme'] except KeyError: - match['theme'] = Theme(theme_config=match['config'], top_theme_config=self.theme_config, **self.theme_kwargs) + match['theme'] = Theme(theme_config=match['config'], main_theme_config=self.theme_config, **self.theme_kwargs) return match['theme'] def get_theme(self, matcher_info): diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index fcbe3e1c..b0bcede6 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -14,34 +14,6 @@ class ZshPromptRenderer(ShellRenderer): character_translations = ShellRenderer.character_translations.copy() character_translations[ord('%')] = '%%' - old_widths = {} - - def render(self, segment_info, *args, **kwargs): - client_id = segment_info.get('client_id') - key = (client_id, kwargs.get('side')) - kwargs = kwargs.copy() - width = kwargs.pop('width', None) - local_theme = segment_info.get('local_theme') - if client_id and local_theme: - output_raw = False - try: - width = self.old_widths[key] - except KeyError: - pass - else: - output_raw = True - ret = super(ShellRenderer, self).render( - output_raw=output_raw, - width=width, - matcher_info=local_theme, - segment_info=segment_info, - *args, **kwargs - ) - if output_raw: - self.old_widths[key] = len(ret[1]) - ret = ret[0] - return ret - def get_theme(self, matcher_info): if not matcher_info: return self.theme @@ -51,7 +23,7 @@ class ZshPromptRenderer(ShellRenderer): except KeyError: match['theme'] = Theme( theme_config=match['config'], - top_theme_config=self.theme_config, + main_theme_config=self.theme_config, **self.theme_kwargs ) return match['theme'] diff --git a/powerline/theme.py b/powerline/theme.py index 9c61ddc7..d03b0a06 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,4 +1,5 @@ # vim:fileencoding=utf-8:noet +from __future__ import division from powerline.segment import gen_segment_getter, process_segment from powerline.lib.unicode import u @@ -28,7 +29,7 @@ class Theme(object): theme_config, common_config, pl, - top_theme_config=None, + main_theme_config=None, run_once=False, shutdown_event=None): self.dividers = theme_config['dividers'] @@ -37,6 +38,11 @@ class Theme(object): for k, v in val.items())) for key, val in self.dividers.items() )) + try: + self.cursor_space_multiplier = 1 - (theme_config['cursor_space'] / 100) + except KeyError: + self.cursor_space_multiplier = None + self.cursor_columns = theme_config.get('cursor_columns') self.spaces = theme_config['spaces'] self.segments = [] self.EMPTY_SEGMENT = { @@ -45,8 +51,8 @@ class Theme(object): } self.pl = pl theme_configs = [theme_config] - if top_theme_config: - theme_configs.append(top_theme_config) + if main_theme_config: + theme_configs.append(main_theme_config) get_segment = gen_segment_getter(pl, ext, common_config, theme_configs, theme_config.get('default_module')) for segdict in itertools.chain((theme_config['segments'],), theme_config['segments'].get('above', ())): diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index e738301f..45cffcf6 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -118,11 +118,11 @@ class TestConfig(TestCase): segment_info = Args(prompt_count=1) - with IpyPowerline(True) as powerline: + with IpyPowerline(True, {}) as powerline: for prompt_type in ['in', 'in2']: powerline.render(matcher_info=prompt_type, segment_info=segment_info) powerline.render(matcher_info=prompt_type, segment_info=segment_info) - with IpyPowerline(False) as powerline: + with IpyPowerline(False, {}) as powerline: for prompt_type in ['out', 'rewrite']: powerline.render(matcher_info=prompt_type, segment_info=segment_info) powerline.render(matcher_info=prompt_type, segment_info=segment_info) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index b7302d89..6e12d257 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -54,10 +54,14 @@ run_test() { run screen -L -c tests/test_shells/screenrc -d -m -S "$SESNAME" \ env LANG=en_US.UTF-8 BINDFILE="$BINDFILE" "${ARGS[@]}" - screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH + while ! screen -S "$SESNAME" -X readreg a tests/test_shells/input.$SH ; do + sleep 0.1s + done # Wait for screen to initialize sleep 1 - screen -S "$SESNAME" -p 0 -X width 300 1 + while ! screen -S "$SESNAME" -p 0 -X width 300 1 ; do + sleep 0.1s + done if test "x${SH}" = "xdash" ; then # If I do not use this hack for dash then output will look like #