diff options
Diffstat (limited to '')
-rw-r--r-- | powerline/renderers/__init__.py | 0 | ||||
-rw-r--r-- | powerline/renderers/i3bar.py | 36 | ||||
-rw-r--r-- | powerline/renderers/ipython/__init__.py | 34 | ||||
-rw-r--r-- | powerline/renderers/ipython/pre_5.py | 56 | ||||
-rw-r--r-- | powerline/renderers/ipython/since_5.py | 130 | ||||
-rw-r--r-- | powerline/renderers/ipython/since_7.py | 91 | ||||
-rw-r--r-- | powerline/renderers/lemonbar.py | 61 | ||||
-rw-r--r-- | powerline/renderers/pango_markup.py | 39 | ||||
-rw-r--r-- | powerline/renderers/pdb.py | 50 | ||||
-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 | ||||
-rw-r--r-- | powerline/renderers/tmux.py | 81 | ||||
-rw-r--r-- | powerline/renderers/vim.py | 188 |
18 files changed, 1131 insertions, 0 deletions
diff --git a/powerline/renderers/__init__.py b/powerline/renderers/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/powerline/renderers/__init__.py diff --git a/powerline/renderers/i3bar.py b/powerline/renderers/i3bar.py new file mode 100644 index 0000000..3eab61f --- /dev/null +++ b/powerline/renderers/i3bar.py @@ -0,0 +1,36 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import json + +from powerline.renderer import Renderer + + +class I3barRenderer(Renderer): + '''I3bar Segment Renderer. + + Currently works only for i3bgbar (i3 bar with custom patches). + ''' + + @staticmethod + def hlstyle(*args, **kwargs): + # We don’t need to explicitly reset attributes, so skip those calls + return '' + + def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs): + segment = { + 'full_text': contents, + 'separator': False, + 'separator_block_width': 0, # no separators + } + + if fg is not None: + if fg is not False and fg[1] is not False: + segment['color'] = '#{0:06x}'.format(fg[1]) + if bg is not None: + if bg is not False and bg[1] is not False: + segment['background'] = '#{0:06x}'.format(bg[1]) + return json.dumps(segment) + ',' + + +renderer = I3barRenderer diff --git a/powerline/renderers/ipython/__init__.py b/powerline/renderers/ipython/__init__.py new file mode 100644 index 0000000..8f463b5 --- /dev/null +++ b/powerline/renderers/ipython/__init__.py @@ -0,0 +1,34 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.theme import Theme +from powerline.renderers.shell import PromptRenderer + + +class IPythonRenderer(PromptRenderer): + '''Powerline ipython segment renderer.''' + def get_segment_info(self, segment_info, mode): + r = self.segment_info.copy() + r['ipython'] = segment_info + return r + + def get_theme(self, matcher_info): + if matcher_info == 'in': + return self.theme + else: + 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'] + + def shutdown(self): + self.theme.shutdown() + for match in self.local_themes.values(): + if 'theme' in match: + match['theme'].shutdown() diff --git a/powerline/renderers/ipython/pre_5.py b/powerline/renderers/ipython/pre_5.py new file mode 100644 index 0000000..9fc8c21 --- /dev/null +++ b/powerline/renderers/ipython/pre_5.py @@ -0,0 +1,56 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell import ShellRenderer +from powerline.renderers.shell.readline import ReadlineRenderer +from powerline.renderers.ipython import IPythonRenderer + + +class IPythonPre50Renderer(IPythonRenderer, ShellRenderer): + '''Powerline ipython segment renderer for pre-5.0 IPython versions.''' + def render(self, **kwargs): + # XXX super(ShellRenderer), *not* super(IPythonPre50Renderer) + return super(ShellRenderer, self).render(**kwargs) + + def do_render(self, segment_info, **kwargs): + segment_info.update(client_id='ipython') + return super(IPythonPre50Renderer, self).do_render( + segment_info=segment_info, + **kwargs + ) + + +class IPythonPromptRenderer(IPythonPre50Renderer, ReadlineRenderer): + '''Powerline ipython prompt (in and in2) renderer''' + pass + + +class IPythonNonPromptRenderer(IPythonPre50Renderer): + '''Powerline ipython non-prompt (out and rewrite) renderer''' + pass + + +class RendererProxy(object): + '''Powerline IPython renderer proxy which chooses appropriate renderer + + Instantiates two renderer objects: one will be used for prompts and the + other for non-prompts. + ''' + def __init__(self, **kwargs): + old_widths = {} + self.non_prompt_renderer = IPythonNonPromptRenderer(old_widths=old_widths, **kwargs) + self.prompt_renderer = IPythonPromptRenderer(old_widths=old_widths, **kwargs) + + def render_above_lines(self, *args, **kwargs): + return self.non_prompt_renderer.render_above_lines(*args, **kwargs) + + def render(self, is_prompt, *args, **kwargs): + return (self.prompt_renderer if is_prompt else self.non_prompt_renderer).render( + *args, **kwargs) + + def shutdown(self, *args, **kwargs): + self.prompt_renderer.shutdown(*args, **kwargs) + self.non_prompt_renderer.shutdown(*args, **kwargs) + + +renderer = RendererProxy diff --git a/powerline/renderers/ipython/since_5.py b/powerline/renderers/ipython/since_5.py new file mode 100644 index 0000000..88c7625 --- /dev/null +++ b/powerline/renderers/ipython/since_5.py @@ -0,0 +1,130 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import operator + +from collections import defaultdict + +try: + from __builtin__ import reduce +except ImportError: + from functools import reduce + +from pygments.token import Token +from prompt_toolkit.styles import DynamicStyle, Attrs + +from powerline.renderers.ipython import IPythonRenderer +from powerline.ipython import IPythonInfo +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE + + +PowerlinePromptToken = Token.Generic.Prompt.Powerline + + +# Note: since 2.7 there is dict.__missing__ with same purpose. But in 2.6 one +# must use defaultdict to get __missing__ working. +class PowerlineStyleDict(defaultdict): + '''Dictionary used for getting pygments style for Powerline groups + ''' + def __new__(cls, missing_func): + return defaultdict.__new__(cls) + + def __init__(self, missing_func): + super(PowerlineStyleDict, self).__init__() + self.missing_func = missing_func + + def __missing__(self, key): + return self.missing_func(key) + + +class PowerlinePromptStyle(DynamicStyle): + def get_attrs_for_token(self, token): + if ( + token not in PowerlinePromptToken + or len(token) != len(PowerlinePromptToken) + 1 + or not token[-1].startswith('Pl') + or token[-1] == 'Pl' + ): + return super(PowerlinePromptStyle, self).get_attrs_for_token(token) + ret = { + 'color': None, + 'bgcolor': None, + 'bold': None, + 'underline': None, + 'italic': None, + 'reverse': False, + 'blink': False, + } + for prop in token[-1][3:].split('_'): + if prop[0] == 'a': + ret[prop[1:]] = True + elif prop[0] == 'f': + ret['color'] = prop[1:] + elif prop[0] == 'b': + ret['bgcolor'] = prop[1:] + return Attrs(**ret) + + def get_token_to_attributes_dict(self): + dct = super(PowerlinePromptStyle, self).get_token_to_attributes_dict() + + def fallback(key): + try: + return dct[key] + except KeyError: + return self.get_attrs_for_token(key) + + return PowerlineStyleDict(fallback) + + def invalidation_hash(self): + return super(PowerlinePromptStyle, self).invalidation_hash() + 1 + + +class IPythonPygmentsRenderer(IPythonRenderer): + reduce_initial = [] + + def get_segment_info(self, segment_info, mode): + return super(IPythonPygmentsRenderer, self).get_segment_info( + IPythonInfo(segment_info), mode) + + @staticmethod + def hl_join(segments): + return reduce(operator.iadd, segments, []) + + def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs): + '''Output highlighted chunk. + + This implementation outputs a list containing a single pair + (:py:class:`pygments.token.Token`, + :py:class:`powerline.lib.unicode.unicode`). + ''' + guifg = None + guibg = None + attrs = [] + if fg is not None and fg is not False: + guifg = fg[1] + if bg is not None and bg is not False: + guibg = bg[1] + if attrs: + attrs = [] + if attrs & ATTR_BOLD: + attrs.append('bold') + if attrs & ATTR_ITALIC: + attrs.append('italic') + if attrs & ATTR_UNDERLINE: + attrs.append('underline') + name = ( + 'Pl' + + ''.join(('_a' + attr for attr in attrs)) + + (('_f%6x' % guifg) if guifg is not None else '') + + (('_b%6x' % guibg) if guibg is not None else '') + ) + return [(getattr(Token.Generic.Prompt.Powerline, name), contents)] + + def hlstyle(self, **kwargs): + return [] + + def get_client_id(self, segment_info): + return id(self) + + +renderer = IPythonPygmentsRenderer diff --git a/powerline/renderers/ipython/since_7.py b/powerline/renderers/ipython/since_7.py new file mode 100644 index 0000000..ca2ea0e --- /dev/null +++ b/powerline/renderers/ipython/since_7.py @@ -0,0 +1,91 @@ +# vim:fileencoding=utf-8:noet +import operator + +try: + from __builtin__ import reduce +except ImportError: + from functools import reduce + +from pygments.token import Token +from prompt_toolkit.styles import DynamicStyle + +from powerline.renderers.ipython import IPythonRenderer +from powerline.ipython import IPythonInfo +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE + +used_styles = [] +seen = set() + +class PowerlinePromptStyle(DynamicStyle): + @property + def style_rules(self): + return (self.get_style() or self._dummy).style_rules + used_styles + + def invalidation_hash(self): + return (h + 1 for h in tuple(super(PowerlinePromptStyle, self).invalidation_hash())) + + +class IPythonPygmentsRenderer(IPythonRenderer): + reduce_initial = [] + + def __init__(self, **kwargs): + super(IPythonPygmentsRenderer, self).__init__(**kwargs) + self.character_translations[ord(' ')] = ' ' + + def get_segment_info(self, segment_info, mode): + return super(IPythonPygmentsRenderer, self).get_segment_info( + IPythonInfo(segment_info), mode) + + @staticmethod + def hl_join(segments): + return reduce(operator.iadd, segments, []) + + def hl(self, escaped_contents, fg=None, bg=None, attrs=None, *args, **kwargs): + '''Output highlighted chunk. + + This implementation outputs a list containing a single pair + (:py:class:`string`, + :py:class:`powerline.lib.unicode.unicode`). + ''' + guifg = None + guibg = None + att = [] + if fg is not None and fg is not False: + guifg = fg[1] + if bg is not None and bg is not False: + guibg = bg[1] + if attrs: + att = [] + if attrs & ATTR_BOLD: + att.append('bold') + if attrs & ATTR_ITALIC: + att.append('italic') + if attrs & ATTR_UNDERLINE: + att.append('underline') + + fg = (('%06x' % guifg) if guifg is not None else '') + bg = (('%06x' % guibg) if guibg is not None else '') + name = ( + 'pl' + + ''.join(('_a' + attr for attr in att)) + + '_f' + fg + '_b' + bg + ) + + global seen + if not (name in seen): + global used_styles + used_styles += [('pygments.' + name, + ''.join((' ' + attr for attr in att)) + + (' fg:#' + fg if fg != '' else ' fg:') + + (' bg:#' + bg if bg != '' else ' bg:'))] + seen.add(name) + return [((name,), escaped_contents)] + + def hlstyle(self, *args, **kwargs): + return [] + + def get_client_id(self, segment_info): + return id(self) + + +renderer = IPythonPygmentsRenderer diff --git a/powerline/renderers/lemonbar.py b/powerline/renderers/lemonbar.py new file mode 100644 index 0000000..8156807 --- /dev/null +++ b/powerline/renderers/lemonbar.py @@ -0,0 +1,61 @@ +# 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_UNDERLINE + + +class LemonbarRenderer(Renderer): + '''lemonbar (formerly bar/bar ain't recursive) renderer + + + See documentation of `lemonbar <https://github.com/LemonBoy/bar>`_ and :ref:`the usage instructions <lemonbar-usage>` + ''' + + character_translations = Renderer.character_translations.copy() + character_translations[ord('%')] = '%%{}' + + @staticmethod + def hlstyle(*args, **kwargs): + # We don’t need to explicitly reset attributes, so skip those calls + return '' + + def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs): + text = '' + + if fg is not None: + if fg is not False and fg[1] is not False: + text += '%{{F#ff{0:06x}}}'.format(fg[1]) + if bg is not None: + if bg is not False and bg[1] is not False: + text += '%{{B#ff{0:06x}}}'.format(bg[1]) + + if attrs & ATTR_UNDERLINE: + text += '%{+u}' + + return text + contents + '%{F-B--u}' + + def render(self, *args, **kwargs): + return '%{{l}}{0}%{{r}}{1}'.format( + super(LemonbarRenderer, self).render(side='left', segment_info={'output': kwargs.get('matcher_info')}, *args, **kwargs), + super(LemonbarRenderer, self).render(side='right', segment_info={'output': kwargs.get('matcher_info')}, *args, **kwargs), + ) + + def get_theme(self, matcher_info): + if not matcher_info or matcher_info not in self.local_themes: + 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 = LemonbarRenderer diff --git a/powerline/renderers/pango_markup.py b/powerline/renderers/pango_markup.py new file mode 100644 index 0000000..3c1a675 --- /dev/null +++ b/powerline/renderers/pango_markup.py @@ -0,0 +1,39 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from xml.sax.saxutils import escape as _escape + +from powerline.renderer import Renderer +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE + + +class PangoMarkupRenderer(Renderer): + '''Powerline Pango markup segment renderer.''' + + @staticmethod + def hlstyle(*args, **kwargs): + # We don’t need to explicitly reset attributes, so skip those calls + return '' + + def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs): + '''Highlight a segment.''' + awesome_attr = [] + if fg is not None: + if fg is not False and fg[1] is not False: + awesome_attr += ['foreground="#{0:06x}"'.format(fg[1])] + if bg is not None: + if bg is not False and bg[1] is not False: + awesome_attr += ['background="#{0:06x}"'.format(bg[1])] + if attrs is not None and attrs is not False: + if attrs & ATTR_BOLD: + awesome_attr += ['font_weight="bold"'] + if attrs & ATTR_ITALIC: + awesome_attr += ['font_style="italic"'] + if attrs & ATTR_UNDERLINE: + awesome_attr += ['underline="single"'] + return '<span ' + ' '.join(awesome_attr) + '>' + contents + '</span>' + + escape = staticmethod(_escape) + + +renderer = PangoMarkupRenderer diff --git a/powerline/renderers/pdb.py b/powerline/renderers/pdb.py new file mode 100644 index 0000000..040f0e1 --- /dev/null +++ b/powerline/renderers/pdb.py @@ -0,0 +1,50 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import platform + +from powerline.renderers.shell.readline import ReadlineRenderer +from powerline.renderer import Renderer + + +class PDBRenderer(ReadlineRenderer): + '''PDB-specific powerline renderer + ''' + pdb = None + initial_stack_length = None + + def get_segment_info(self, segment_info, mode): + r = self.segment_info.copy() + r['pdb'] = self.pdb + r['initial_stack_length'] = self.initial_stack_length + r['curframe'] = self.pdb.curframe + return r + + def set_pdb(self, pdb): + '''Record currently used :py:class:`pdb.Pdb` instance + + Must be called before first calling :py:meth:`render` method. + + :param pdb.Pdb pdb: + Used :py:class:`pdb.Pdb` instance. This instance will later be used + by :py:meth:`get_segment_info` for patching :ref:`segment_info + <dev-segments-info>` dictionary. + ''' + self.pdb = pdb + + def render(self, **kwargs): + if self.initial_stack_length is None: + self.initial_stack_length = len(self.pdb.stack) - 1 + return Renderer.render(self, **kwargs) + + if sys.version_info < (3,) and platform.python_implementation() == 'PyPy': + def do_render(self, **kwargs): + # Make sure that only ASCII characters survive + ret = super(PDBRenderer, self).do_render(**kwargs) + ret = ret.encode('ascii', 'replace') + ret = ret.decode('ascii') + return ret + + +renderer = PDBRenderer 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 diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py new file mode 100644 index 0000000..fc3282a --- /dev/null +++ b/powerline/renderers/tmux.py @@ -0,0 +1,81 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderer import Renderer +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE + + +def attrs_to_tmux_attrs(attrs): + if attrs is False: + return ['nobold', 'noitalics', 'nounderscore'] + else: + ret = [] + if attrs & ATTR_BOLD: + ret += ['bold'] + else: + ret += ['nobold'] + if attrs & ATTR_ITALIC: + ret += ['italics'] + else: + ret += ['noitalics'] + if attrs & ATTR_UNDERLINE: + ret += ['underscore'] + else: + ret += ['nounderscore'] + return ret + + +class TmuxRenderer(Renderer): + '''Powerline tmux segment renderer.''' + + character_translations = Renderer.character_translations.copy() + character_translations[ord('#')] = '##[]' + + def render(self, width=None, segment_info={}, **kwargs): + if width and segment_info: + width -= segment_info.get('width_adjust', 0) + if width < 10: + width = 10 + return super(TmuxRenderer, self).render(width=width, segment_info=segment_info, **kwargs) + + def hlstyle(self, fg=None, bg=None, attrs=None, **kwargs): + '''Highlight a segment.''' + # We don’t need to explicitly reset attributes, so skip those calls + if not attrs and not bg and not fg: + return '' + tmux_attrs = [] + if fg is not None: + if fg is False or fg[0] is False: + tmux_attrs += ['fg=default'] + else: + if self.term_truecolor and fg[1]: + tmux_attrs += ['fg=#{0:06x}'.format(int(fg[1]))] + else: + tmux_attrs += ['fg=colour' + str(fg[0])] + if bg is not None: + if bg is False or bg[0] is False: + tmux_attrs += ['bg=default'] + else: + if self.term_truecolor and bg[1]: + tmux_attrs += ['bg=#{0:06x}'.format(int(bg[1]))] + else: + tmux_attrs += ['bg=colour' + str(bg[0])] + if attrs is not None: + tmux_attrs += attrs_to_tmux_attrs(attrs) + return '#[' + ','.join(tmux_attrs) + ']' + + def get_segment_info(self, segment_info, mode): + r = self.segment_info.copy() + if segment_info: + r.update(segment_info) + if 'pane_current_path' in r: + r['getcwd'] = lambda: r['pane_current_path'] + elif 'pane_id' in r: + varname = 'TMUX_PWD_' + str(r['pane_id']) + if varname in r['environ']: + r['getcwd'] = lambda: r['environ'][varname] + r['mode'] = mode + return r + + +renderer = TmuxRenderer diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py new file mode 100644 index 0000000..a92d51c --- /dev/null +++ b/powerline/renderers/vim.py @@ -0,0 +1,188 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys + +import vim + +from powerline.bindings.vim import vim_get_func, vim_getoption, environ, current_tabpage, get_vim_encoding +from powerline.renderer import Renderer +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE +from powerline.theme import Theme +from powerline.lib.unicode import unichr, register_strwidth_error + + +vim_mode = vim_get_func('mode', rettype='unicode') +if int(vim.eval('v:version')) >= 702: + _vim_mode = vim_mode + vim_mode = lambda: _vim_mode(1) + +mode_translations = { + unichr(ord('V') - 0x40): '^V', + unichr(ord('S') - 0x40): '^S', +} + + +class VimRenderer(Renderer): + '''Powerline vim segment renderer.''' + + character_translations = Renderer.character_translations.copy() + character_translations[ord('%')] = '%%' + + segment_info = Renderer.segment_info.copy() + segment_info.update(environ=environ) + + def __init__(self, *args, **kwargs): + if not hasattr(vim, 'strwidth'): + # Hope nobody want to change this at runtime + if vim.eval('&ambiwidth') == 'double': + kwargs = dict(**kwargs) + kwargs['ambigious'] = 2 + super(VimRenderer, self).__init__(*args, **kwargs) + self.hl_groups = {} + self.prev_highlight = None + self.strwidth_error_name = register_strwidth_error(self.strwidth) + self.encoding = get_vim_encoding() + + def shutdown(self): + self.theme.shutdown() + for match in self.local_themes.values(): + if 'theme' in match: + match['theme'].shutdown() + + def add_local_theme(self, matcher, theme): + if matcher in self.local_themes: + raise KeyError('There is already a local theme with given matcher') + self.local_themes[matcher] = theme + + def get_matched_theme(self, match): + 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'] + + def get_theme(self, matcher_info): + if matcher_info is None: + return self.get_matched_theme(self.local_themes[None]) + for matcher in self.local_themes.keys(): + if matcher and matcher(matcher_info): + return self.get_matched_theme(self.local_themes[matcher]) + else: + return self.theme + + if hasattr(vim, 'strwidth'): + if sys.version_info < (3,): + def strwidth(self, string): + # Does not work with tabs, but neither is strwidth from default + # renderer + return vim.strwidth(string.encode(self.encoding, 'replace')) + else: + @staticmethod + def strwidth(string): + return vim.strwidth(string) + + def get_segment_info(self, segment_info, mode): + return segment_info or self.segment_info + + def render(self, window=None, window_id=None, winnr=None, is_tabline=False): + '''Render all segments.''' + segment_info = self.segment_info.copy() + + if window is vim.current.window: + mode = vim_mode() + mode = mode_translations.get(mode, mode) + else: + mode = 'nc' + + segment_info.update( + window=window, + mode=mode, + window_id=window_id, + winnr=winnr, + buffer=window.buffer, + tabpage=current_tabpage(), + encoding=self.encoding, + ) + segment_info['tabnr'] = segment_info['tabpage'].number + segment_info['bufnr'] = segment_info['buffer'].number + if is_tabline: + winwidth = int(vim_getoption('columns')) + else: + winwidth = segment_info['window'].width + + statusline = super(VimRenderer, self).render( + mode=mode, + width=winwidth, + segment_info=segment_info, + matcher_info=(None if is_tabline else segment_info), + ) + statusline = statusline.encode(self.encoding, self.strwidth_error_name) + return statusline + + def reset_highlight(self): + self.hl_groups.clear() + + def hlstyle(self, fg=None, bg=None, attrs=None, **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 vim highlight group. + ''' + # In order not to hit E541 two consequent identical highlighting + # specifiers may be squashed into one. + attrs = attrs or 0 # Normalize `attrs` + if (fg, bg, attrs) == self.prev_highlight: + return '' + self.prev_highlight = (fg, bg, attrs) + + # We don’t need to explicitly reset attributes in vim, so skip those + # calls + if not attrs and not bg and not fg: + return '' + + if not (fg, bg, attrs) in self.hl_groups: + hl_group = { + 'ctermfg': 'NONE', + 'guifg': None, + 'ctermbg': 'NONE', + 'guibg': None, + 'attrs': ['NONE'], + 'name': '', + } + if fg is not None and fg is not False: + hl_group['ctermfg'] = fg[0] + hl_group['guifg'] = fg[1] + if bg is not None and bg is not False: + hl_group['ctermbg'] = bg[0] + hl_group['guibg'] = bg[1] + if attrs: + hl_group['attrs'] = [] + if attrs & ATTR_BOLD: + hl_group['attrs'].append('bold') + if attrs & ATTR_ITALIC: + hl_group['attrs'].append('italic') + if attrs & ATTR_UNDERLINE: + hl_group['attrs'].append('underline') + hl_group['name'] = ( + 'Pl_' + + str(hl_group['ctermfg']) + '_' + + str(hl_group['guifg']) + '_' + + str(hl_group['ctermbg']) + '_' + + str(hl_group['guibg']) + '_' + + ''.join(hl_group['attrs']) + ) + self.hl_groups[(fg, bg, attrs)] = hl_group + vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attrs} gui={attrs}'.format( + group=hl_group['name'], + ctermfg=hl_group['ctermfg'], + guifg='#{0:06x}'.format(hl_group['guifg']) if hl_group['guifg'] is not None else 'NONE', + ctermbg=hl_group['ctermbg'], + guibg='#{0:06x}'.format(hl_group['guibg']) if hl_group['guibg'] is not None else 'NONE', + attrs=','.join(hl_group['attrs']), + )) + return '%#' + self.hl_groups[(fg, bg, attrs)]['name'] + '#' + + +renderer = VimRenderer |