# vim:fileencoding=utf-8:noet from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline.lib.watcher import create_file_watcher def list_segment_key_values(segment, theme_configs, segment_data, key, function_name=None, name=None, module=None, default=None): try: yield segment[key] except KeyError: pass found_module_key = False for theme_config in theme_configs: try: segment_data = theme_config['segment_data'] except KeyError: pass else: if function_name and not name: if module: try: yield segment_data[module + '.' + function_name][key] found_module_key = True except KeyError: pass if not found_module_key: try: yield segment_data[function_name][key] except KeyError: pass if name: try: yield segment_data[name][key] except KeyError: pass if segment_data is not None: try: yield segment_data[key] except KeyError: pass yield default def get_segment_key(merge, *args, **kwargs): if merge: ret = None for value in list_segment_key_values(*args, **kwargs): if ret is None: ret = value elif isinstance(ret, dict) and isinstance(value, dict): old_ret = ret ret = value.copy() ret.update(old_ret) else: return ret return ret else: return next(list_segment_key_values(*args, **kwargs)) def get_function(data, segment): function_name = segment['function'] if '.' in function_name: module, function_name = function_name.rpartition('.')[::2] else: module = data['default_module'] function = data['get_module_attr'](module, function_name, prefix='segment_generator') if not function: raise ImportError('Failed to obtain segment function') return None, function, module, function_name, segment.get('name') def get_string(data, segment): name = segment.get('name') return data['get_key'](False, segment, None, None, name, 'contents'), None, None, None, name segment_getters = { 'function': get_function, 'string': get_string, 'segment_list': get_function, } def get_attr_func(contents_func, key, args, is_space_func=False): try: func = getattr(contents_func, key) except AttributeError: return None else: if is_space_func: def expand_func(pl, amount, segment): try: return func(pl=pl, amount=amount, segment=segment, **args) except Exception as e: pl.exception('Exception while computing {0} function: {1}', key, str(e)) return segment['contents'] + (' ' * amount) return expand_func else: return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **args) def process_segment_lister(pl, segment_info, parsed_segments, side, mode, colorscheme, lister, subsegments, patcher_args): subsegments = [ subsegment for subsegment in subsegments if subsegment['display_condition'](pl, segment_info, mode) ] for subsegment_info, subsegment_update in lister(pl=pl, segment_info=segment_info, **patcher_args): draw_inner_divider = subsegment_update.pop('draw_inner_divider', False) old_pslen = len(parsed_segments) for subsegment in subsegments: if subsegment_update: subsegment = subsegment.copy() subsegment.update(subsegment_update) if 'priority_multiplier' in subsegment_update and subsegment['priority']: subsegment['priority'] *= subsegment_update['priority_multiplier'] process_segment( pl, side, subsegment_info, parsed_segments, subsegment, mode, colorscheme, ) new_pslen = len(parsed_segments) while parsed_segments[new_pslen - 1]['literal_contents'][1]: new_pslen -= 1 if new_pslen > old_pslen + 1 and draw_inner_divider is not None: for i in range(old_pslen, new_pslen - 1) if side == 'left' else range(old_pslen + 1, new_pslen): parsed_segments[i]['draw_soft_divider'] = draw_inner_divider return None def set_segment_highlighting(pl, colorscheme, segment, mode): if segment['literal_contents'][1]: return True try: highlight_group_prefix = segment['highlight_group_prefix'] except KeyError: hl_groups = lambda hlgs: hlgs else: hl_groups = lambda hlgs: [highlight_group_prefix + ':' + hlg for hlg in hlgs] + hlgs try: segment['highlight'] = colorscheme.get_highlighting( hl_groups(segment['highlight_groups']), mode, segment.get('gradient_level') ) if segment['divider_highlight_group']: segment['divider_highlight'] = colorscheme.get_highlighting( hl_groups([segment['divider_highlight_group']]), mode ) else: segment['divider_highlight'] = None except Exception as e: pl.exception('Failed to set highlight group: {0}', str(e)) return False else: return True def process_segment(pl, side, segment_info, parsed_segments, segment, mode, colorscheme): segment = segment.copy() pl.prefix = segment['name'] if segment['type'] in ('function', 'segment_list'): try: if segment['type'] == 'function': contents = segment['contents_func'](pl, segment_info) else: contents = segment['contents_func'](pl, segment_info, parsed_segments, side, mode, colorscheme) except Exception as e: pl.exception('Exception while computing segment: {0}', str(e)) return if contents is None: return if isinstance(contents, list): # Needs copying here, but it was performed at the very start of the # function segment_base = segment if contents: draw_divider_position = -1 if side == 'left' else 0 for key, i, newval in ( ('before', 0, ''), ('after', -1, ''), ('draw_soft_divider', draw_divider_position, True), ('draw_hard_divider', draw_divider_position, True), ): try: contents[i][key] = segment_base.pop(key) segment_base[key] = newval except KeyError: pass draw_inner_divider = None if side == 'right': append = parsed_segments.append else: pslen = len(parsed_segments) append = lambda item: parsed_segments.insert(pslen, item) for subsegment in (contents if side == 'right' else reversed(contents)): segment_copy = segment_base.copy() segment_copy.update(subsegment) if draw_inner_divider is not None: segment_copy['draw_soft_divider'] = draw_inner_divider draw_inner_divider = segment_copy.pop('draw_inner_divider', None) if set_segment_highlighting(pl, colorscheme, segment_copy, mode): append(segment_copy) else: segment['contents'] = contents if set_segment_highlighting(pl, colorscheme, segment, mode): parsed_segments.append(segment) elif segment['width'] == 'auto' or (segment['type'] == 'string' and segment['contents'] is not None): if set_segment_highlighting(pl, colorscheme, segment, mode): parsed_segments.append(segment) always_true = lambda pl, segment_info, mode: True get_fallback_segment = { 'name': 'fallback', 'type': 'string', 'highlight_groups': ['background'], 'divider_highlight_group': None, 'before': None, 'after': None, 'contents': '', 'literal_contents': (0, ''), 'priority': None, 'draw_soft_divider': True, 'draw_hard_divider': True, 'draw_inner_divider': True, 'display_condition': always_true, 'width': None, 'align': None, 'expand': None, 'truncate': None, 'startup': None, 'shutdown': None, '_rendered_raw': '', '_rendered_hl': '', '_len': None, '_contents_len': None, }.copy def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, get_module_attr, top_theme): data = { 'default_module': default_module or 'powerline.segments.' + ext, 'get_module_attr': get_module_attr, 'segment_data': None, } def get_key(merge, segment, module, function_name, name, key, default=None): return get_segment_key(merge, segment, theme_configs, data['segment_data'], key, function_name, name, module, default) data['get_key'] = get_key def get_selector(function_name): if '.' in function_name: module, function_name = function_name.rpartition('.')[::2] else: module = 'powerline.selectors.' + ext function = get_module_attr(module, function_name, prefix='segment_generator/selector_function') if not function: pl.error('Failed to get segment selector, ignoring it') return function def get_segment_selector(segment, selector_type): try: function_name = segment[selector_type + '_function'] except KeyError: function = None else: function = get_selector(function_name) try: modes = segment[selector_type + '_modes'] except KeyError: modes = None if modes: if function: return lambda pl, segment_info, mode: ( mode in modes or function(pl=pl, segment_info=segment_info, mode=mode) ) else: return lambda pl, segment_info, mode: mode in modes else: if function: return lambda pl, segment_info, mode: ( function(pl=pl, segment_info=segment_info, mode=mode) ) else: return None def gen_display_condition(segment): include_function = get_segment_selector(segment, 'include') exclude_function = get_segment_selector(segment, 'exclude') if include_function: if exclude_function: return lambda *args: ( include_function(*args) and not exclude_function(*args)) else: return include_function else: if exclude_function: return lambda *args: not exclude_function(*args) else: return always_true def get(segment, side): segment_type = segment.get('type', 'function') try: get_segment_info = segment_getters[segment_type] except KeyError: pl.error('Unknown segment type: {0}', segment_type) return None try: contents, _contents_func, module, function_name, name = get_segment_info(data, segment) except Exception as e: pl.exception('Failed to generate segment from {0!r}: {1}', segment, str(e), prefix='segment_generator') return None if not get_key(False, segment, module, function_name, name, 'display', True): return None segment_datas = getattr(_contents_func, 'powerline_segment_datas', None) if segment_datas: try: data['segment_data'] = segment_datas[top_theme] except KeyError: pass if segment_type == 'function': highlight_groups = [function_name] else: highlight_groups = segment.get('highlight_groups') or [name] if segment_type in ('function', 'segment_list'): args = dict(( (str(k), v) for k, v in get_key(True, segment, module, function_name, name, 'args', {}).items() )) display_condition = gen_display_condition(segment) if segment_type == 'segment_list': # Handle startup and shutdown of _contents_func? subsegments = [ subsegment for subsegment in ( get(subsegment, side) for subsegment in segment['segments'] ) if subsegment ] return { 'name': name or function_name, 'type': segment_type, 'highlight_groups': None, 'divider_highlight_group': None, 'before': None, 'after': None, 'contents_func': lambda pl, segment_info, parsed_segments, side, mode, colorscheme: ( process_segment_lister( pl, segment_info, parsed_segments, side, mode, colorscheme, patcher_args=args, subsegments=subsegments, lister=_contents_func, ) ), 'contents': None, 'literal_contents': None, 'priority': None, 'draw_soft_divider': None, 'draw_hard_divider': None, 'draw_inner_divider': None, 'side': side, 'display_condition': display_condition, 'width': None, 'align': None, 'expand': None, 'truncate': None, 'startup': None, 'shutdown': None, '_rendered_raw': '', '_rendered_hl': '', '_len': None, '_contents_len': None, } if segment_type == 'function': startup_func = get_attr_func(_contents_func, 'startup', args) shutdown_func = getattr(_contents_func, 'shutdown', None) expand_func = get_attr_func(_contents_func, 'expand', args, True) truncate_func = get_attr_func(_contents_func, 'truncate', args, True) if hasattr(_contents_func, 'powerline_requires_filesystem_watcher'): create_watcher = lambda: create_file_watcher(pl, common_config['watcher']) args[str('create_watcher')] = create_watcher if hasattr(_contents_func, 'powerline_requires_segment_info'): contents_func = lambda pl, segment_info: _contents_func(pl=pl, segment_info=segment_info, **args) else: contents_func = lambda pl, segment_info: _contents_func(pl=pl, **args) else: startup_func = None shutdown_func = None contents_func = None expand_func = None truncate_func = None return { 'name': name or function_name, 'type': segment_type, 'highlight_groups': highlight_groups, 'divider_highlight_group': None, 'before': get_key(False, segment, module, function_name, name, 'before', ''), 'after': get_key(False, segment, module, function_name, name, 'after', ''), 'contents_func': contents_func, 'contents': contents, 'literal_contents': (0, ''), 'priority': segment.get('priority', None), 'draw_hard_divider': segment.get('draw_hard_divider', True), 'draw_soft_divider': segment.get('draw_soft_divider', True), 'draw_inner_divider': segment.get('draw_inner_divider', False), 'side': side, 'display_condition': display_condition, 'width': segment.get('width'), 'align': segment.get('align', 'l'), 'expand': expand_func, 'truncate': truncate_func, 'startup': startup_func, 'shutdown': shutdown_func, '_rendered_raw': '', '_rendered_hl': '', '_len': None, '_contents_len': None, } return get