Add support for above lines as described in #462

Support is not mirrored in shell bindings yet
This commit is contained in:
ZyX 2014-06-23 08:39:05 +04:00
parent a65ea01d38
commit 61006d8fe1
5 changed files with 101 additions and 36 deletions

View File

@ -413,6 +413,25 @@ class Powerline(object):
pass pass
return FailedUnicode(safe_unicode(e)) 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): def shutdown(self):
'''Shut down all background threads. Must be run only prior to exiting '''Shut down all background threads. Must be run only prior to exiting
current application. current application.

View File

@ -175,7 +175,20 @@ class Renderer(object):
r['getcwd'] = lambda: r['environ']['PWD'] r['getcwd'] = lambda: r['environ']['PWD']
return r 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. '''Render all segments.
When a width is provided, low-priority segments are dropped one at When a width is provided, low-priority segments are dropped one at
@ -193,6 +206,9 @@ class Renderer(object):
:param str side: :param str side:
One of ``left``, ``right``. Determines which side will be rendered. One of ``left``, ``right``. Determines which side will be rendered.
If not present all sides are 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: :param bool output_raw:
Changes the output: if this parameter is ``True`` then in place of Changes the output: if this parameter is ``True`` then in place of
one string this method outputs a pair ``(colored_string, one string this method outputs a pair ``(colored_string,
@ -203,7 +219,7 @@ class Renderer(object):
Matcher information. Is processed in ``.get_theme()`` method. Matcher information. Is processed in ``.get_theme()`` method.
''' '''
theme = self.get_theme(matcher_info) 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 # Handle excluded/included segments for the current mode
segments = [self._get_highlighting(segment, mode) for segment in segments segments = [self._get_highlighting(segment, mode) for segment in segments

View File

@ -51,7 +51,7 @@ def get_argparser(parser=None, *args, **kwargs):
parser = argparse.ArgumentParser parser = argparse.ArgumentParser
p = parser(*args, **kwargs) p = parser(*args, **kwargs)
p.add_argument('ext', nargs=1) 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('-r', '--renderer_module', metavar='MODULE', type=str)
p.add_argument('-w', '--width', type=int) p.add_argument('-w', '--width', type=int)
p.add_argument('--last_exit_code', metavar='INT', type=int) p.add_argument('--last_exit_code', metavar='INT', type=int)

View File

@ -2,6 +2,7 @@
from powerline.segment import gen_segment_getter from powerline.segment import gen_segment_getter
from powerline.lib.unicode import u from powerline.lib.unicode import u
import itertools
def requires_segment_info(func): def requires_segment_info(func):
@ -9,6 +10,13 @@ def requires_segment_info(func):
return func return func
def new_empty_segment_line():
return {
'left': [],
'right': []
}
class Theme(object): class Theme(object):
def __init__(self, def __init__(self,
ext, ext,
@ -20,10 +28,7 @@ class Theme(object):
shutdown_event=None): shutdown_event=None):
self.dividers = theme_config.get('dividers', common_config['dividers']) self.dividers = theme_config.get('dividers', common_config['dividers'])
self.spaces = theme_config.get('spaces', common_config['spaces']) self.spaces = theme_config.get('spaces', common_config['spaces'])
self.segments = { self.segments = []
'left': [],
'right': [],
}
self.EMPTY_SEGMENT = { self.EMPTY_SEGMENT = {
'contents': None, 'contents': None,
'highlight': {'fg': False, 'bg': False, 'attr': 0} 'highlight': {'fg': False, 'bg': False, 'attr': 0}
@ -33,25 +38,29 @@ class Theme(object):
if top_theme_config: if top_theme_config:
theme_configs.append(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')) get_segment = gen_segment_getter(pl, ext, common_config['paths'], theme_configs, theme_config.get('default_module'))
for side in ['left', 'right']: for segdict in itertools.chain((theme_config['segments'],),
for segment in theme_config['segments'].get(side, []): theme_config['segments'].get('above', ())):
segment = get_segment(segment, side) self.segments.append(new_empty_segment_line())
if not run_once: for side in ['left', 'right']:
if segment['startup']: for segment in segdict.get(side, []):
try: segment = get_segment(segment, side)
segment['startup'](pl, shutdown_event) if not run_once:
except Exception as e: if segment['startup']:
pl.error('Exception during {0} startup: {1}', segment['name'], str(e)) try:
continue segment['startup'](pl, shutdown_event)
self.segments[side].append(segment) 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): def shutdown(self):
for segments in self.segments.values(): for line in self.segments:
for segment in segments: for segments in line.values():
try: for segment in segments:
segment['shutdown']() try:
except TypeError: segment['shutdown']()
pass except TypeError:
pass
def get_divider(self, side='left', type='soft'): def get_divider(self, side='left', type='soft'):
'''Return segment divider.''' '''Return segment divider.'''
@ -60,15 +69,22 @@ class Theme(object):
def get_spaces(self): def get_spaces(self):
return self.spaces 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. '''Return all segments.
Function segments are called, and all segments get their before/after Function segments are called, and all segments get their before/after
and ljust/rjust properties applied. 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']: for side in [side] if side else ['left', 'right']:
parsed_segments = [] parsed_segments = []
for segment in self.segments[side]: for segment in self.segments[line][side]:
if segment['type'] == 'function': if segment['type'] == 'function':
self.pl.prefix = segment['name'] self.pl.prefix = segment['name']
try: try:

View File

@ -10,6 +10,12 @@ except ImportError:
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))) 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 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__': if __name__ == '__main__':
args = get_argparser(description=__doc__).parse_args() args = get_argparser(description=__doc__).parse_args()
finish_args(args) finish_args(args)
@ -17,13 +23,21 @@ if __name__ == '__main__':
segment_info = {'args': args, 'environ': os.environ} segment_info = {'args': args, 'environ': os.environ}
if args.renderer_arg: if args.renderer_arg:
segment_info.update(args.renderer_arg) segment_info.update(args.renderer_arg)
rendered = powerline.render( if args.side.startswith('above'):
width=args.width, for line in powerline.render_above_lines(
side=args.side, width=args.width,
segment_info=segment_info, segment_info=segment_info,
mode=os.environ.get('_POWERLINE_MODE'), mode=os.environ.get('_POWERLINE_MODE'),
) ):
try: write(line)
sys.stdout.write(rendered) sys.stdout.write('\n')
except UnicodeEncodeError: args.side = args.side[len('above'):]
sys.stdout.write(rendered.encode('utf-8'))
if args.side:
rendered = powerline.render(
width=args.width,
side=args.side,
segment_info=segment_info,
mode=os.environ.get('_POWERLINE_MODE'),
)
write(rendered)