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 + <config-themes-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 b267c076..93670555 100644 --- a/powerline/bindings/bash/powerline.sh +++ b/powerline/bindings/bash/powerline.sh @@ -42,7 +42,7 @@ _powerline_init_tmux_support() { _powerline_prompt() { # Arguments: side, last_exit_code, jobnum $POWERLINE_COMMAND shell $1 \ - -w "${COLUMNS:-$(_powerline_columns_fallback)}" \ + --width="${COLUMNS:-$(_powerline_columns_fallback)}" \ -r bash_prompt \ --last_exit_code=$2 \ --jobnum=$3 \ diff --git a/powerline/bindings/ipython/post_0_11.py b/powerline/bindings/ipython/post_0_11.py index f10006b9..ec3a4769 100644 --- a/powerline/bindings/ipython/post_0_11.py +++ b/powerline/bindings/ipython/post_0_11.py @@ -22,15 +22,14 @@ 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 = powerline.render( + side='left', output_width=True, output_raw=not color, - width=width, matcher_info=name, segment_info=self.powerline_segment_info, ) @@ -44,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 @@ -59,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 e10e4d5c..ed6ad75e 100644 --- a/powerline/bindings/ipython/pre_0_11.py +++ b/powerline/bindings/ipython/pre_0_11.py @@ -32,14 +32,14 @@ class PowerlinePrompt(BasePrompt): self.set_p_str() return string(self.p_str) - def set_p_str(self, width=None): + 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, - width=width ) ) @@ -61,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 = self.powerline_prompt_width - 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 @@ -85,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 89da8d8f..d197c5be 100644 --- a/powerline/bindings/shell/powerline.sh +++ b/powerline/bindings/shell/powerline.sh @@ -135,7 +135,7 @@ _powerline_prompt() { # Arguments: side, exit_code _powerline_set_jobs $POWERLINE_COMMAND shell $1 \ - -w "${COLUMNS:-$(_powerline_columns_fallback)}" \ + --width="${COLUMNS:-$(_powerline_columns_fallback)}" \ $_POWERLINE_RENDERER_ARG \ --renderer_arg="client_id=$$" \ --last_exit_code=$2 \ diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 626ee58c..631a6752 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -133,9 +133,9 @@ _powerline_setup_prompt() { 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 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/renderers/shell.py b/powerline/renderers/shell.py index 931c1178..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 @@ -27,31 +27,55 @@ class ShellRenderer(Renderer): super(ShellRenderer, self).__init__(*args, **kwargs) self.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) + def render(self, segment_info, **kwargs): local_theme = segment_info.get('local_theme') - if client_id and local_theme: - output_width = False + 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: - output_width = True - ret = super(ShellRenderer, self).render( - output_width=output_width, + 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, - matcher_info=local_theme, + theme=theme, segment_info=segment_info, - *args, **kwargs + side=side, + **kwargs ) - if output_width: - self.old_widths[key] = ret[1] - ret = ret[0] - return ret + 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/theme.py b/powerline/theme.py index dbb4f3f5..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 @@ -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 = { 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)