From da6367a897cab477c9bc2d2f88ba7ba630522a97 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 14 Dec 2012 18:18:42 +0400 Subject: [PATCH] Added local theme support (mainly for buffer-local themes) --- powerline/config.json | 5 ++- powerline/core.py | 30 +++++++++++++++--- powerline/ext/vim/matchers.py | 5 +++ powerline/ext/vim/renderer.py | 6 ++-- powerline/matchers.py | 24 ++++++++++++++ powerline/renderer.py | 57 ++++++++++++++++++++++------------ powerline/segments.py | 5 ++- powerline/theme.py | 6 +--- powerline/themes/vim/help.json | 33 ++++++++++++++++++++ 9 files changed, 134 insertions(+), 37 deletions(-) create mode 100644 powerline/ext/vim/matchers.py create mode 100644 powerline/matchers.py create mode 100644 powerline/themes/vim/help.json diff --git a/powerline/config.json b/powerline/config.json index e93788f4..5f745f9b 100644 --- a/powerline/config.json +++ b/powerline/config.json @@ -22,7 +22,10 @@ }, "vim": { "colorscheme": "default", - "theme": "default" + "theme": "default", + "local_themes": { + "help": "help" + } } } } diff --git a/powerline/core.py b/powerline/core.py index 54922066..85591053 100644 --- a/powerline/core.py +++ b/powerline/core.py @@ -6,7 +6,8 @@ import os import sys from colorscheme import Colorscheme -from theme import Theme +from segments import Segments +from matchers import Matchers class Powerline(object): @@ -29,14 +30,33 @@ class Powerline(object): colorscheme = Colorscheme(colorscheme_config) # Load and initialize extension theme - theme_config = self._load_json_config(os.path.join('themes', ext, self.config_ext['theme'])) - self.theme = Theme(ext, colorscheme, theme_config, self.config) + theme_config = self._load_theme_config(ext, self.config_ext.get('theme', 'default')) + + path = [os.path.expanduser(path) for path in self.config.get('paths', [])] + + get_segment = Segments(ext, path, colorscheme).get + get_matcher = Matchers(ext, path).get + + theme_kwargs = { + 'ext': ext, + 'colorscheme': colorscheme, + 'common_config': self.config, + 'get_segment': get_segment + } + + local_themes = {} + for key, local_theme_name in self.config_ext.get('local_themes', {}).iteritems(): + key = get_matcher(key) + local_themes[key] = {'config': self._load_theme_config(ext, local_theme_name)} # Load and initialize extension renderer renderer_module_name = 'powerline.ext.{0}.renderer'.format(ext) renderer_class_name = '{0}Renderer'.format(ext.capitalize()) - renderer_class = getattr(importlib.import_module(renderer_module_name), renderer_class_name) - self.renderer = renderer_class(self.theme) + Renderer = getattr(importlib.import_module(renderer_module_name), renderer_class_name) + self.renderer = Renderer(theme_config, local_themes, theme_kwargs) + + def _load_theme_config(self, ext, name): + return self._load_json_config(os.path.join('themes', ext, name)) def _load_json_config(self, config_file): config_file += '.json' diff --git a/powerline/ext/vim/matchers.py b/powerline/ext/vim/matchers.py new file mode 100644 index 00000000..4d11c9cd --- /dev/null +++ b/powerline/ext/vim/matchers.py @@ -0,0 +1,5 @@ +import vim + + +def help(): + return bool(int(vim.eval('&buftype is# "help"'))) diff --git a/powerline/ext/vim/renderer.py b/powerline/ext/vim/renderer.py index ea4da670..c948c780 100644 --- a/powerline/ext/vim/renderer.py +++ b/powerline/ext/vim/renderer.py @@ -16,8 +16,8 @@ class VimRenderer(Renderer): ''' PERCENT_PLACEHOLDER = u'' - def __init__(self, theme): - super(VimRenderer, self).__init__(theme) + def __init__(self, *args, **kwargs): + super(VimRenderer, self).__init__(*args, **kwargs) self.hl_groups = {} def render(self, winnr): @@ -40,7 +40,7 @@ class VimRenderer(Renderer): contents_cached = vim_getwinvar(winnr, 'powerline') contents_override = {k: contents_cached[k].decode('utf-8') for k in contents_cached.keys()} - statusline = super(VimRenderer, self).render(mode, winwidth, contents_override) + statusline = super(VimRenderer, self).render(mode, width=winwidth, contents_override=contents_override) statusline = statusline.replace(self.PERCENT_PLACEHOLDER, '%%') return statusline diff --git a/powerline/matchers.py b/powerline/matchers.py new file mode 100644 index 00000000..6d16cb0d --- /dev/null +++ b/powerline/matchers.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +from importlib import import_module +import sys + + +class Matchers(object): + def __init__(self, ext, path): + self.ext = ext + self.path = path + + def get(self, match_name): + match_module, separator, match_function = match_name.rpartition('.') + if not separator: + match_module = 'powerline.ext.{0}.matchers'.format(self.ext) + match_function = match_name + + oldpath = sys.path + sys.path = self.path + sys.path + + try: + return getattr(import_module(match_module), match_function) + finally: + sys.path = oldpath diff --git a/powerline/renderer.py b/powerline/renderer.py index 070594c9..71da016a 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from colorscheme import Colorscheme +from theme import Theme class Renderer(object): @@ -8,9 +9,21 @@ class Renderer(object): ATTR_ITALIC = 2 ATTR_UNDERLINE = 4 - def __init__(self, theme): + def __init__(self, theme_config, local_themes, theme_kwargs): + self.theme = Theme(theme_config=theme_config, **theme_kwargs) + self.local_themes = local_themes + self.theme_kwargs = theme_kwargs self.segments = [] - self.theme = theme + + def get_theme(self): + for matcher in self.local_themes.iterkeys(): + if matcher(): + match = self.local_themes[matcher] + if 'config' in match: + match['theme'] = Theme(theme_config=match.pop('config'), **self.theme_kwargs) + return match['theme'] + else: + return self.theme def render(self, mode, width=None, contents_override=None): '''Render all segments. @@ -21,36 +34,40 @@ class Renderer(object): provided they will fill the remaining space until the desired width is reached. ''' - self.segments = self.theme.get_segments(mode, contents_override) - rendered_highlighted = self._render_segments(mode) + + theme = self.get_theme() + + segments = theme.get_segments(mode, contents_override) + self.segments = segments + rendered_highlighted = self._render_segments(mode, theme, segments) if not width: # No width specified, so we don't need to crop or pad anything return rendered_highlighted # Create an ordered list of segments that can be dropped - segments_priority = [segment for segment in sorted(self.segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0] + segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0] - while self._total_len() > width and len(segments_priority): - self.segments.remove(segments_priority[0]) + while self._total_len(segments) > width and len(segments_priority): + segments.remove(segments_priority[0]) segments_priority.pop(0) # Do another render pass so we can calculate the correct amount of filler space - self._render_segments(mode, False) + self._render_segments(mode, theme, segments, render_highlighted=False) # Distribute the remaining space on the filler segments - segments_fillers = [segment for segment in self.segments if segment['type'] == 'filler'] + segments_fillers = [segment for segment in segments if segment['type'] == 'filler'] if segments_fillers: - segments_fillers_len, segments_fillers_remainder = divmod((width - self._total_len()), len(segments_fillers)) + segments_fillers_len, segments_fillers_remainder = divmod((width - self._total_len(segments)), len(segments_fillers)) segments_fillers_contents = ' ' * segments_fillers_len for segment in segments_fillers: segment['contents'] = segments_fillers_contents # Add remainder whitespace to the first filler segment segments_fillers[0]['contents'] += ' ' * segments_fillers_remainder - return self._render_segments(mode) + return self._render_segments(mode, theme, segments) - def _render_segments(self, mode, render_highlighted=True): + def _render_segments(self, mode, theme, segments, render_highlighted=True): '''Internal segment rendering method. This method loops through the segment array and compares the @@ -62,18 +79,18 @@ class Renderer(object): statusline if render_highlighted is True. ''' rendered_highlighted = u'' - segments_len = len(self.segments) - mode = mode if mode in self.segments[0]['highlight'] else Colorscheme.DEFAULT_MODE_KEY + segments_len = len(segments) + mode = mode if mode in segments[0]['highlight'] else Colorscheme.DEFAULT_MODE_KEY - for index, segment in enumerate(self.segments): - prev_segment = self.segments[index - 1] if index > 0 else self.theme.EMPTY_SEGMENT - next_segment = self.segments[index + 1] if index < segments_len - 1 else self.theme.EMPTY_SEGMENT + for index, segment in enumerate(segments): + prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT + next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT compare_segment = next_segment if segment['side'] == 'left' else prev_segment segment['rendered_raw'] = u'' outer_padding = ' ' if index == 0 or index == segments_len - 1 else '' divider_type = 'soft' if compare_segment['highlight'][mode]['bg'] == segment['highlight'][mode]['bg'] else 'hard' - divider = self.theme.get_divider(segment['side'], divider_type) + divider = theme.get_divider(segment['side'], divider_type) divider_hl = '' segment_hl = '' @@ -109,13 +126,13 @@ class Renderer(object): return rendered_highlighted - def _total_len(self): + def _total_len(self, segments): '''Return total/rendered length of all segments. This method uses the rendered_raw property of the segments and requires that the segments have been rendered using the render() method first. ''' - return len(''.join([segment['rendered_raw'] for segment in self.segments])) + return len(''.join([segment['rendered_raw'] for segment in segments])) def hl(self, fg=None, bg=None, attr=None): raise NotImplementedError diff --git a/powerline/segments.py b/powerline/segments.py index 407065d0..750a564c 100644 --- a/powerline/segments.py +++ b/powerline/segments.py @@ -2,13 +2,12 @@ from importlib import import_module import sys -import os class Segments(object): - def __init__(self, ext, common_config, colorscheme): + def __init__(self, ext, path, colorscheme): self.ext = ext - self.path = [os.path.expanduser(path) for path in common_config.get('paths', [])] + self.path = path self.colorscheme = colorscheme def get_function(self, segment): diff --git a/powerline/theme.py b/powerline/theme.py index e52b7260..c7f8e4fa 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- -from powerline.segments import Segments - class Theme(object): - def __init__(self, ext, colorscheme, theme_config, common_config): + def __init__(self, ext, colorscheme, theme_config, common_config, get_segment): self.colorscheme = colorscheme self.dividers = theme_config.get('dividers', common_config['dividers']) self.segments = [] @@ -14,8 +12,6 @@ class Theme(object): 'highlight': {self.colorscheme.DEFAULT_MODE_KEY: {'fg': (False, False), 'bg': (False, False), 'attr': 0}} } - get_segment = Segments(ext, common_config, colorscheme).get - for side in ['left', 'right']: self.segments.extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, []))) diff --git a/powerline/themes/vim/help.json b/powerline/themes/vim/help.json new file mode 100644 index 00000000..f51a1b87 --- /dev/null +++ b/powerline/themes/vim/help.json @@ -0,0 +1,33 @@ +{ + "segments": { + "left": [ + { + "name": "file_name", + "draw_divider": false + }, + { + "type": "filler", + "highlight": ["background"] + } + ], + "right": [ + { + "name": "line_percent", + "args": { "gradient": true }, + "priority": 30, + "after": "", + "rjust": 4 + }, + { + "type": "string", + "contents": "⭡ ", + "highlight": ["line_current_symbol", "line_current"] + }, + { + "name": "line_current", + "draw_divider": false, + "rjust": 3 + } + ] + } +}