From 61006d8fe130e058f36eff468a0ff7359530fbf8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 23 Jun 2014 08:39:05 +0400 Subject: [PATCH] Add support for above lines as described in #462 Support is not mirrored in shell bindings yet --- powerline/__init__.py | 19 +++++++++++++ powerline/renderer.py | 20 ++++++++++++-- powerline/shell.py | 2 +- powerline/theme.py | 62 +++++++++++++++++++++++++++---------------- scripts/powerline | 34 +++++++++++++++++------- 5 files changed, 101 insertions(+), 36 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index c82163a9..1f7b71cc 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -413,6 +413,25 @@ class Powerline(object): pass return FailedUnicode(safe_unicode(e)) + def render_above_lines(self, *args, **kwargs): + '''Like .render(), but for ``self.renderer.render_above_lines()`` + ''' + try: + self.update_renderer() + for line in self.renderer.render_above_lines(*args, **kwargs): + yield line + except Exception as e: + try: + self.exception('Failed to render: {0}', str(e)) + except Exception as e: + # Updates e variable to new value, masking previous one. + # Normally it is the same exception (due to raise in case pl is + # unset), but it may also show error in logger. Note that latter + # is not logged by logger for obvious reasons, thus this also + # prevents us from seeing logger traceback. + pass + yield FailedUnicode(safe_unicode(e)) + def shutdown(self): '''Shut down all background threads. Must be run only prior to exiting current application. diff --git a/powerline/renderer.py b/powerline/renderer.py index 7445ff0f..002f777c 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -175,7 +175,20 @@ class Renderer(object): r['getcwd'] = lambda: r['environ']['PWD'] return r - def render(self, mode=None, width=None, side=None, output_raw=False, segment_info=None, matcher_info=None): + def render_above_lines(self, **kwargs): + '''Render all segments in the {theme}/segments/above list + + Rendering happens in the reversed order. Parameters are the same as in + .render() method. + + :yield: rendered line. + ''' + + theme = self.get_theme(kwargs.get('matcher_info', None)) + 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): '''Render all segments. When a width is provided, low-priority segments are dropped one at @@ -193,6 +206,9 @@ class Renderer(object): :param str side: One of ``left``, ``right``. Determines which side will be rendered. If not present all sides are rendered. + :param int line: + Line number for which segments should be obtained. Is counted from + zero (botmost line). :param bool output_raw: Changes the output: if this parameter is ``True`` then in place of one string this method outputs a pair ``(colored_string, @@ -203,7 +219,7 @@ class Renderer(object): Matcher information. Is processed in ``.get_theme()`` method. ''' theme = self.get_theme(matcher_info) - segments = theme.get_segments(side, self.get_segment_info(segment_info, mode)) + 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, mode) for segment in segments diff --git a/powerline/shell.py b/powerline/shell.py index 4b817ae1..bffa1df8 100644 --- a/powerline/shell.py +++ b/powerline/shell.py @@ -51,7 +51,7 @@ def get_argparser(parser=None, *args, **kwargs): parser = argparse.ArgumentParser p = parser(*args, **kwargs) p.add_argument('ext', nargs=1) - p.add_argument('side', nargs='?', choices=('left', 'right')) + p.add_argument('side', nargs='?', choices=('left', 'right', 'above', 'aboveleft')) p.add_argument('-r', '--renderer_module', metavar='MODULE', type=str) p.add_argument('-w', '--width', type=int) p.add_argument('--last_exit_code', metavar='INT', type=int) diff --git a/powerline/theme.py b/powerline/theme.py index d6b185f2..c6157e8f 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -2,6 +2,7 @@ from powerline.segment import gen_segment_getter from powerline.lib.unicode import u +import itertools def requires_segment_info(func): @@ -9,6 +10,13 @@ def requires_segment_info(func): return func +def new_empty_segment_line(): + return { + 'left': [], + 'right': [] + } + + class Theme(object): def __init__(self, ext, @@ -20,10 +28,7 @@ class Theme(object): shutdown_event=None): self.dividers = theme_config.get('dividers', common_config['dividers']) self.spaces = theme_config.get('spaces', common_config['spaces']) - self.segments = { - 'left': [], - 'right': [], - } + self.segments = [] self.EMPTY_SEGMENT = { 'contents': None, 'highlight': {'fg': False, 'bg': False, 'attr': 0} @@ -33,25 +38,29 @@ class Theme(object): if top_theme_config: theme_configs.append(top_theme_config) get_segment = gen_segment_getter(pl, ext, common_config['paths'], theme_configs, theme_config.get('default_module')) - for side in ['left', 'right']: - for segment in theme_config['segments'].get(side, []): - segment = get_segment(segment, side) - if not run_once: - if segment['startup']: - try: - segment['startup'](pl, shutdown_event) - except Exception as e: - pl.error('Exception during {0} startup: {1}', segment['name'], str(e)) - continue - self.segments[side].append(segment) + for segdict in itertools.chain((theme_config['segments'],), + theme_config['segments'].get('above', ())): + self.segments.append(new_empty_segment_line()) + for side in ['left', 'right']: + for segment in segdict.get(side, []): + segment = get_segment(segment, side) + if not run_once: + if segment['startup']: + try: + segment['startup'](pl, shutdown_event) + except Exception as e: + pl.error('Exception during {0} startup: {1}', segment['name'], str(e)) + continue + self.segments[-1][side].append(segment) def shutdown(self): - for segments in self.segments.values(): - for segment in segments: - try: - segment['shutdown']() - except TypeError: - pass + for line in self.segments: + for segments in line.values(): + for segment in segments: + try: + segment['shutdown']() + except TypeError: + pass def get_divider(self, side='left', type='soft'): '''Return segment divider.''' @@ -60,15 +69,22 @@ class Theme(object): def get_spaces(self): return self.spaces - def get_segments(self, side=None, segment_info=None): + def get_line_number(self): + return len(self.segments) + + def get_segments(self, side=None, line=0, segment_info=None): '''Return all segments. Function segments are called, and all segments get their before/after and ljust/rjust properties applied. + + :param int line: + Line number for which segments should be obtained. Is counted from + zero (botmost line). ''' for side in [side] if side else ['left', 'right']: parsed_segments = [] - for segment in self.segments[side]: + for segment in self.segments[line][side]: if segment['type'] == 'function': self.pl.prefix = segment['name'] try: diff --git a/scripts/powerline b/scripts/powerline index 74d0fc98..8b125217 100755 --- a/scripts/powerline +++ b/scripts/powerline @@ -10,6 +10,12 @@ except ImportError: sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))) from powerline.shell import ShellPowerline, get_argparser, finish_args # NOQA +def write(output): + try: + sys.stdout.write(output) + except UnicodeEncodeError: + sys.stdout.write(output.encode('utf-8')) + if __name__ == '__main__': args = get_argparser(description=__doc__).parse_args() finish_args(args) @@ -17,13 +23,21 @@ if __name__ == '__main__': segment_info = {'args': args, 'environ': os.environ} if args.renderer_arg: segment_info.update(args.renderer_arg) - rendered = powerline.render( - width=args.width, - side=args.side, - segment_info=segment_info, - mode=os.environ.get('_POWERLINE_MODE'), - ) - try: - sys.stdout.write(rendered) - except UnicodeEncodeError: - sys.stdout.write(rendered.encode('utf-8')) + if args.side.startswith('above'): + for line in powerline.render_above_lines( + width=args.width, + segment_info=segment_info, + mode=os.environ.get('_POWERLINE_MODE'), + ): + write(line) + sys.stdout.write('\n') + args.side = args.side[len('above'):] + + if args.side: + rendered = powerline.render( + width=args.width, + side=args.side, + segment_info=segment_info, + mode=os.environ.get('_POWERLINE_MODE'), + ) + write(rendered)