Added local theme support (mainly for buffer-local themes)

This commit is contained in:
ZyX 2012-12-14 18:18:42 +04:00 committed by Kim Silkebækken
parent f550bd37f8
commit da6367a897
9 changed files with 134 additions and 37 deletions

View File

@ -22,7 +22,10 @@
},
"vim": {
"colorscheme": "default",
"theme": "default"
"theme": "default",
"local_themes": {
"help": "help"
}
}
}
}

View File

@ -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'

View File

@ -0,0 +1,5 @@
import vim
def help():
return bool(int(vim.eval('&buftype is# "help"')))

View File

@ -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

24
powerline/matchers.py Normal file
View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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, [])))

View File

@ -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
}
]
}
}