Added local theme support (mainly for buffer-local themes)
This commit is contained in:
parent
f550bd37f8
commit
da6367a897
|
@ -22,7 +22,10 @@
|
|||
},
|
||||
"vim": {
|
||||
"colorscheme": "default",
|
||||
"theme": "default"
|
||||
"theme": "default",
|
||||
"local_themes": {
|
||||
"help": "help"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import vim
|
||||
|
||||
|
||||
def help():
|
||||
return bool(int(vim.eval('&buftype is# "help"')))
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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, [])))
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue