diff options
Diffstat (limited to 'powerline/renderers/shell')
-rw-r--r-- | powerline/renderers/shell/__init__.py | 182 | ||||
-rw-r--r-- | powerline/renderers/shell/bash.py | 96 | ||||
-rw-r--r-- | powerline/renderers/shell/ksh.py | 19 | ||||
-rw-r--r-- | powerline/renderers/shell/rcsh.py | 7 | ||||
-rw-r--r-- | powerline/renderers/shell/readline.py | 14 | ||||
-rw-r--r-- | powerline/renderers/shell/tcsh.py | 31 | ||||
-rw-r--r-- | powerline/renderers/shell/zsh.py | 16 |
7 files changed, 365 insertions, 0 deletions
diff --git a/powerline/renderers/shell/__init__.py b/powerline/renderers/shell/__init__.py new file mode 100644 index 0000000..d7dbf96 --- /dev/null +++ b/powerline/renderers/shell/__init__.py @@ -0,0 +1,182 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderer import Renderer +from powerline.theme import Theme +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE + + +def int_to_rgb(num): + r = (num >> 16) & 0xff + g = (num >> 8) & 0xff + b = num & 0xff + return r, g, b + + +class PromptRenderer(Renderer): + '''Powerline generic prompt segment renderer''' + + def __init__(self, old_widths=None, **kwargs): + super(PromptRenderer, self).__init__(**kwargs) + self.old_widths = old_widths if old_widths is not None else {} + + def get_client_id(self, segment_info): + '''Get client ID given segment info + + This is used by daemon to correctly cache widths for different clients + using a single renderer instance. + + :param dict segment_info: + :ref:`Segment info dictionary <dev-segments-info>`. Out of it only + ``client_id`` key is used. It is OK for this dictionary to not + contain this key. + + :return: Any hashable value or ``None``. + ''' + return segment_info.get('client_id') if isinstance(segment_info, dict) else None + + def do_render(self, output_width, segment_info, side, theme, width=None, **kwargs): + client_id = self.get_client_id(segment_info) + if client_id is not 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 and 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(PromptRenderer, self).do_render( + output_width=True, + width=width, + theme=theme, + segment_info=segment_info, + side=side, + **kwargs + ) + if client_id is not None: + 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 + + +class ShellRenderer(PromptRenderer): + '''Powerline shell segment renderer.''' + escape_hl_start = '' + escape_hl_end = '' + term_truecolor = False + term_escape_style = 'auto' + tmux_escape = False + screen_escape = False + + character_translations = Renderer.character_translations.copy() + + 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, segment_info, **kwargs): + if self.term_escape_style == 'auto': + if segment_info['environ'].get('TERM') == 'fbterm': + self.used_term_escape_style = 'fbterm' + else: + self.used_term_escape_style = 'xterm' + else: + self.used_term_escape_style = self.term_escape_style + return super(ShellRenderer, self).do_render(segment_info=segment_info, **kwargs) + + def hlstyle(self, fg=None, bg=None, attrs=None, escape=True, **kwargs): + '''Highlight a segment. + + If an argument is None, the argument is ignored. If an argument is + False, the argument is reset to the terminal defaults. If an argument + is a valid color or attribute, it’s added to the ANSI escape code. + ''' + ansi = [0] + is_fbterm = self.used_term_escape_style == 'fbterm' + term_truecolor = not is_fbterm and self.term_truecolor + if fg is not None: + if fg is False or fg[0] is False: + ansi += [39] + else: + if term_truecolor: + ansi += [38, 2] + list(int_to_rgb(fg[1])) + else: + ansi += [38, 5, fg[0]] + if bg is not None: + if bg is False or bg[0] is False: + ansi += [49] + else: + if term_truecolor: + ansi += [48, 2] + list(int_to_rgb(bg[1])) + else: + ansi += [48, 5, bg[0]] + if attrs is not None: + if attrs is False: + ansi += [22] + else: + if attrs & ATTR_BOLD: + ansi += [1] + elif attrs & ATTR_ITALIC: + # Note: is likely not to work or even be inverse in place of + # italic. Omit using this in colorschemes. + ansi += [3] + elif attrs & ATTR_UNDERLINE: + ansi += [4] + if is_fbterm: + r = [] + while ansi: + cur_ansi = ansi.pop(0) + if cur_ansi == 38: + ansi.pop(0) + r.append('\033[1;{0}}}'.format(ansi.pop(0))) + elif cur_ansi == 48: + ansi.pop(0) + r.append('\033[2;{0}}}'.format(ansi.pop(0))) + else: + r.append('\033[{0}m'.format(cur_ansi)) + r = ''.join(r) + else: + r = '\033[{0}m'.format(';'.join(str(attr) for attr in ansi)) + if self.tmux_escape: + r = '\033Ptmux;' + r.replace('\033', '\033\033') + '\033\\' + elif self.screen_escape: + r = '\033P' + r.replace('\033', '\033\033') + '\033\\' + return self.escape_hl_start + r + self.escape_hl_end if escape else r + + def get_theme(self, matcher_info): + if not matcher_info: + return self.theme + match = self.local_themes[matcher_info] + try: + return match['theme'] + except KeyError: + match['theme'] = Theme( + theme_config=match['config'], + main_theme_config=self.theme_config, + **self.theme_kwargs + ) + return match['theme'] + + +renderer = ShellRenderer diff --git a/powerline/renderers/shell/bash.py b/powerline/renderers/shell/bash.py new file mode 100644 index 0000000..5ccf206 --- /dev/null +++ b/powerline/renderers/shell/bash.py @@ -0,0 +1,96 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell import ShellRenderer + + +class BashPromptRenderer(ShellRenderer): + '''Powerline bash prompt segment renderer.''' + escape_hl_start = '\\[' + escape_hl_end = '\\]' + + character_translations = ShellRenderer.character_translations.copy() + character_translations[ord('$')] = '\\$' + character_translations[ord('`')] = '\\`' + character_translations[ord('\\')] = '\\\\' + + def do_render(self, side, line, width, output_width, output_raw, hl_args, **kwargs): + + # we are rendering the normal left prompt + if side == 'left' and line == 0 and width is not None: + + # we need left prompt's width to render the raw spacer + output_width = output_width or output_raw + + left = super(BashPromptRenderer, self).do_render( + side=side, + line=line, + output_width=output_width, + width=width, + output_raw=output_raw, + hl_args=hl_args, + **kwargs + ) + left_rendered = left[0] if output_width else left + + # we don't escape color sequences in the right prompt so we can do escaping as a whole + if hl_args: + hl_args = hl_args.copy() + hl_args.update({'escape': False}) + else: + hl_args = {'escape': False} + + right = super(BashPromptRenderer, self).do_render( + side='right', + line=line, + output_width=True, + width=width, + output_raw=output_raw, + hl_args=hl_args, + **kwargs + ) + + ret = [] + if right[-1] > 0: + # if the right prompt is not empty we embed it in the left prompt + # it must be escaped as a whole so readline doesn't see it + ret.append(''.join(( + left_rendered, + self.escape_hl_start, + '\033[s', # save the cursor position + '\033[{0}C'.format(width), # move to the right edge of the terminal + '\033[{0}D'.format(right[-1] - 1), # move back to the right prompt position + right[0], + '\033[u', # restore the cursor position + self.escape_hl_end + ))) + if output_raw: + ret.append(''.join(( + left[1], + ' ' * (width - left[-1] - right[-1]), + right[1] + ))) + else: + ret.append(left_rendered) + if output_raw: + ret.append(left[1]) + if output_width: + ret.append(left[-1]) + if len(ret) == 1: + return ret[0] + else: + return ret + + else: + return super(BashPromptRenderer, self).do_render( + side=side, + line=line, + width=width, + output_width=output_width, + output_raw=output_raw, + hl_args=hl_args, + **kwargs + ) + + +renderer = BashPromptRenderer diff --git a/powerline/renderers/shell/ksh.py b/powerline/renderers/shell/ksh.py new file mode 100644 index 0000000..0828e57 --- /dev/null +++ b/powerline/renderers/shell/ksh.py @@ -0,0 +1,19 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell import ShellRenderer + + +ESCAPE_CHAR = '\001' + + +class KshPromptRenderer(ShellRenderer): + '''Powerline bash prompt segment renderer.''' + escape_hl_start = '\001' + escape_hl_end = '\001' + + def render(self, *args, **kwargs): + return '\001\r' + super(KshPromptRenderer, self).render(*args, **kwargs) + + +renderer = KshPromptRenderer diff --git a/powerline/renderers/shell/rcsh.py b/powerline/renderers/shell/rcsh.py new file mode 100644 index 0000000..75ccb22 --- /dev/null +++ b/powerline/renderers/shell/rcsh.py @@ -0,0 +1,7 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell.readline import ReadlineRenderer + + +renderer = ReadlineRenderer diff --git a/powerline/renderers/shell/readline.py b/powerline/renderers/shell/readline.py new file mode 100644 index 0000000..a72dff0 --- /dev/null +++ b/powerline/renderers/shell/readline.py @@ -0,0 +1,14 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell import ShellRenderer + + +class ReadlineRenderer(ShellRenderer): + '''Renderer useful for some applications that use readline + ''' + escape_hl_start = '\x01' + escape_hl_end = '\x02' + + +renderer = ReadlineRenderer diff --git a/powerline/renderers/shell/tcsh.py b/powerline/renderers/shell/tcsh.py new file mode 100644 index 0000000..bf0697d --- /dev/null +++ b/powerline/renderers/shell/tcsh.py @@ -0,0 +1,31 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell.zsh import ZshPromptRenderer + + +class TcshPromptRenderer(ZshPromptRenderer): + '''Powerline tcsh prompt segment renderer.''' + character_translations = ZshPromptRenderer.character_translations.copy() + character_translations[ord('%')] = '%%' + character_translations[ord('\\')] = '\\\\' + character_translations[ord('^')] = '\\^' + character_translations[ord('!')] = '\\!' + + def do_render(self, **kwargs): + ret = super(TcshPromptRenderer, self).do_render(**kwargs) + nbsp = self.character_translations.get(ord(' '), ' ') + end = self.hlstyle() + assert not ret or ret.endswith(end) + if ret.endswith(nbsp + end): + # Exchange nbsp and highlight end because tcsh removes trailing + # %{%} part of the prompt for whatever reason + ret = ret[:-(len(nbsp) + len(end))] + end + nbsp + else: + # We *must* end prompt with non-%{%} sequence for the reasons + # explained above. So add nbsp if it is not already there. + ret += nbsp + return ret + + +renderer = TcshPromptRenderer diff --git a/powerline/renderers/shell/zsh.py b/powerline/renderers/shell/zsh.py new file mode 100644 index 0000000..a231512 --- /dev/null +++ b/powerline/renderers/shell/zsh.py @@ -0,0 +1,16 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell import ShellRenderer + + +class ZshPromptRenderer(ShellRenderer): + '''Powerline zsh prompt segment renderer.''' + escape_hl_start = '%{' + escape_hl_end = '%}' + + character_translations = ShellRenderer.character_translations.copy() + character_translations[ord('%')] = '%%' + + +renderer = ZshPromptRenderer |