summaryrefslogtreecommitdiffstats
path: root/powerline/renderers/shell
diff options
context:
space:
mode:
Diffstat (limited to 'powerline/renderers/shell')
-rw-r--r--powerline/renderers/shell/__init__.py182
-rw-r--r--powerline/renderers/shell/bash.py96
-rw-r--r--powerline/renderers/shell/ksh.py19
-rw-r--r--powerline/renderers/shell/rcsh.py7
-rw-r--r--powerline/renderers/shell/readline.py14
-rw-r--r--powerline/renderers/shell/tcsh.py31
-rw-r--r--powerline/renderers/shell/zsh.py16
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