Make the renderer work with new theme/colorscheme API
This commit is contained in:
parent
d7ff3f72a6
commit
011bacb8c3
|
@ -49,6 +49,23 @@ class Colorscheme(object):
|
|||
'attr': group_attr_flag,
|
||||
}
|
||||
|
||||
def get_group_highlighting(self, group):
|
||||
'''Return highlighting information for all modes of a highlighting group.
|
||||
'''
|
||||
group_highlighting = {}
|
||||
for mode, mode_group in self.modes_groups.items():
|
||||
try:
|
||||
group_highlighting[mode] = mode_group[group]
|
||||
except TypeError:
|
||||
for try_group in group:
|
||||
if try_group in self.modes_groups[mode]:
|
||||
group_highlighting[mode] = mode_group[try_group]
|
||||
break
|
||||
finally:
|
||||
if mode not in group_highlighting:
|
||||
raise KeyError('Highlighting groups not found in colorscheme: {0}'.format(group))
|
||||
return group_highlighting
|
||||
|
||||
def get_highlighting(self, group, mode=None):
|
||||
'''Return highlighting information for a highlighting group and mode.
|
||||
|
||||
|
@ -58,6 +75,14 @@ class Colorscheme(object):
|
|||
if not mode or mode not in self.modes_groups:
|
||||
mode = self.default_mode_key
|
||||
|
||||
try:
|
||||
return self.modes_groups[mode][group]
|
||||
except TypeError:
|
||||
for try_group in group:
|
||||
if try_group in self.modes_groups[mode]:
|
||||
return self.modes_groups[mode][try_group]
|
||||
raise KeyError('Highlighting groups not found in colorscheme: {0}'.format(group))
|
||||
|
||||
return self.modes_groups[mode][group]
|
||||
|
||||
def _get_attr_flag(self, attributes):
|
||||
|
|
|
@ -29,17 +29,17 @@ class Powerline(object):
|
|||
|
||||
# Load and initialize colorscheme
|
||||
colorscheme_config = self._load_json_config(os.path.join('colorschemes', self.config_ext['colorscheme']))
|
||||
self.colorscheme = Colorscheme(colorscheme_config)
|
||||
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, theme_config, self.config)
|
||||
self.theme = Theme(ext, colorscheme, theme_config, self.config)
|
||||
|
||||
# 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.colorscheme, self.theme)
|
||||
self.renderer = renderer_class(self.theme)
|
||||
|
||||
def _load_json_config(self, config_file):
|
||||
config_file += '.json'
|
||||
|
@ -50,3 +50,6 @@ class Powerline(object):
|
|||
return json.load(config_file_fp)
|
||||
|
||||
raise IOError('Config file not found in search path: {0}'.format(config_file))
|
||||
|
||||
def render(self, mode, width=None):
|
||||
return self.renderer.render(mode, width)
|
||||
|
|
|
@ -6,8 +6,8 @@ from powerline.renderer import Renderer
|
|||
class VimRenderer(Renderer):
|
||||
'''Powerline vim segment renderer.
|
||||
'''
|
||||
def __init__(self, colorscheme, theme):
|
||||
super(VimRenderer, self).__init__(colorscheme, theme)
|
||||
def __init__(self, theme):
|
||||
super(VimRenderer, self).__init__(theme)
|
||||
self.hl_groups = {}
|
||||
|
||||
def hl(self, fg=None, bg=None, attr=None):
|
||||
|
@ -41,11 +41,11 @@ class VimRenderer(Renderer):
|
|||
|
||||
if attr:
|
||||
hl_group['attr'] = []
|
||||
if attr & Renderer.ATTR_BOLD:
|
||||
if attr & self.ATTR_BOLD:
|
||||
hl_group['attr'].append('bold')
|
||||
if attr & Renderer.ATTR_ITALIC:
|
||||
if attr & self.ATTR_ITALIC:
|
||||
hl_group['attr'].append('italic')
|
||||
if attr & Renderer.ATTR_UNDERLINE:
|
||||
if attr & self.ATTR_UNDERLINE:
|
||||
hl_group['attr'].append('underline')
|
||||
|
||||
hl_group['name'] = 'Pl_' + \
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import vim
|
||||
|
||||
from bindings import vim_get_func
|
||||
from powerline.ext.vim.bindings import vim_get_func
|
||||
|
||||
vim_funcs = {
|
||||
'col': vim_get_func('col', rettype=int),
|
||||
|
@ -48,12 +49,12 @@ def mode(override=None):
|
|||
mode = vim_funcs['mode']()
|
||||
|
||||
if not override:
|
||||
return (mode, vim_modes[mode])
|
||||
return vim_modes[mode]
|
||||
|
||||
try:
|
||||
return (mode, override[mode])
|
||||
return override[mode]
|
||||
except IndexError:
|
||||
return (mode, vim_modes[mode])
|
||||
return vim_modes[mode]
|
||||
|
||||
|
||||
def modified_indicator(text=u'+'):
|
||||
|
@ -93,7 +94,7 @@ def branch():
|
|||
def file_directory():
|
||||
'''Return file directory (head component of the file path).
|
||||
'''
|
||||
return vim_funcs['expand']('%:~:.:h')
|
||||
return vim_funcs['expand']('%:~:.:h') + os.sep
|
||||
|
||||
|
||||
def file_name():
|
||||
|
@ -126,13 +127,20 @@ def file_type():
|
|||
return vim.eval('&filetype') or None
|
||||
|
||||
|
||||
def line_percent():
|
||||
def line_percent(gradient=False):
|
||||
'''Return the cursor position in the file as a percentage.
|
||||
'''
|
||||
line_current = vim_funcs['line']('.')
|
||||
line_last = vim_funcs['line']('$')
|
||||
percentage = int(line_current * 100 // line_last)
|
||||
|
||||
return line_current * 100 // line_last
|
||||
if not gradient:
|
||||
return percentage
|
||||
|
||||
return {
|
||||
'contents': percentage,
|
||||
'highlight': 'line_percent_gradient' + str(int(5 * percentage // 100) + 1),
|
||||
}
|
||||
|
||||
|
||||
def line_current():
|
||||
|
|
|
@ -1,23 +1,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from segment import mksegment
|
||||
|
||||
|
||||
class Renderer(object):
|
||||
ATTR_BOLD = 1
|
||||
ATTR_ITALIC = 2
|
||||
ATTR_UNDERLINE = 4
|
||||
|
||||
|
||||
def __init__(self, colorscheme, theme):
|
||||
pass
|
||||
def __init__(self, theme):
|
||||
self.segments = []
|
||||
self.theme = theme
|
||||
|
||||
def render(self, mode, width=None):
|
||||
'''Render all the segments with the specified renderer.
|
||||
|
||||
This method loops through the segment array and compares the
|
||||
foreground/background colors and divider properties and returns the
|
||||
rendered statusline as a string.
|
||||
'''Render all segments.
|
||||
|
||||
When a width is provided, low-priority segments are dropped one at
|
||||
a time until the line is shorter than the width, or only segments
|
||||
|
@ -25,71 +19,8 @@ class Renderer(object):
|
|||
provided they will fill the remaining space until the desired width is
|
||||
reached.
|
||||
'''
|
||||
def render_segments(segments, render_highlighted=True):
|
||||
'''Render a segment array.
|
||||
|
||||
By default this function renders both raw (un-highlighted segments
|
||||
used for calculating final width) and highlighted segments. The raw
|
||||
rendering is used for calculating the total width for dropping
|
||||
low-priority segments.
|
||||
'''
|
||||
rendered_highlighted = u''
|
||||
segments_len = len(segments)
|
||||
empty_segment = mksegment()
|
||||
|
||||
for idx, segment in enumerate(segments):
|
||||
prev = segments[idx - 1] if idx > 0 else empty_segment
|
||||
next = segments[idx + 1] if idx < segments_len - 1 else empty_segment
|
||||
|
||||
segment['rendered_raw'] = u''
|
||||
compare = next if segment['side'] == 'l' else prev
|
||||
outer_padding = ' ' if idx == 0 or idx == segments_len - 1 else ''
|
||||
divider_type = 'soft' if compare['bg'] == segment['bg'] else 'hard'
|
||||
divider = self.dividers[segment['side']][divider_type]
|
||||
divider_hl = ''
|
||||
segment_hl = ''
|
||||
|
||||
if render_highlighted:
|
||||
# Generate and cache renderer highlighting
|
||||
if divider_type == 'hard':
|
||||
hl_key = (segment['bg'], compare['bg'])
|
||||
if not hl_key in self._hl:
|
||||
self._hl[hl_key] = self.hl(*hl_key)
|
||||
divider_hl = self._hl[hl_key]
|
||||
|
||||
hl_key = (segment['fg'], segment['bg'], segment['attr'])
|
||||
if not hl_key in self._hl:
|
||||
self._hl[hl_key] = self.hl(*hl_key)
|
||||
segment_hl = self._hl[hl_key]
|
||||
|
||||
if segment['filler']:
|
||||
# Filler segments shouldn't be padded
|
||||
rendered_highlighted += segment['contents']
|
||||
elif segment['draw_divider'] or divider_type == 'hard':
|
||||
# Draw divider if specified, or if it's a hard divider
|
||||
# Note: Hard dividers are always drawn, regardless of
|
||||
# the draw_divider option
|
||||
if segment['side'] == 'l':
|
||||
segment['rendered_raw'] += outer_padding + segment['contents'] + ' ' + divider + ' '
|
||||
rendered_highlighted += segment_hl + outer_padding + segment['contents'] + ' ' + divider_hl + divider + ' '
|
||||
else:
|
||||
segment['rendered_raw'] += ' ' + divider + ' ' + segment['contents'] + outer_padding
|
||||
rendered_highlighted += ' ' + divider_hl + divider + segment_hl + ' ' + segment['contents'] + outer_padding
|
||||
elif segment['contents']:
|
||||
# Segments without divider
|
||||
if segment['side'] == 'l':
|
||||
segment['rendered_raw'] += outer_padding + segment['contents']
|
||||
rendered_highlighted += segment_hl + outer_padding + segment['contents']
|
||||
else:
|
||||
segment['rendered_raw'] += segment['contents'] + outer_padding
|
||||
rendered_highlighted += segment_hl + segment['contents'] + outer_padding
|
||||
else:
|
||||
# Unknown segment type, skip it
|
||||
continue
|
||||
|
||||
return rendered_highlighted
|
||||
|
||||
rendered_highlighted = render_segments(self.segments)
|
||||
self.segments = self.theme.get_segments()
|
||||
rendered_highlighted = self._render_segments(mode)
|
||||
|
||||
if not width:
|
||||
# No width specified, so we don't need to crop or pad anything
|
||||
|
@ -103,10 +34,10 @@ class Renderer(object):
|
|||
segments_priority.pop(0)
|
||||
|
||||
# Do another render pass so we can calculate the correct amount of filler space
|
||||
render_segments(self.segments)
|
||||
self._render_segments(mode, False)
|
||||
|
||||
# Distribute the remaining space on the filler segments
|
||||
segments_fillers = [segment for segment in self.segments if segment['filler'] is True]
|
||||
segments_fillers = [segment for segment in self.segments if segment['type'] == 'filler']
|
||||
if segments_fillers:
|
||||
segments_fillers_len, segments_fillers_remainder = divmod((width - self._total_len()), len(segments_fillers))
|
||||
segments_fillers_contents = ' ' * segments_fillers_len
|
||||
|
@ -115,7 +46,65 @@ class Renderer(object):
|
|||
# Add remainder whitespace to the first filler segment
|
||||
segments_fillers[0]['contents'] += ' ' * segments_fillers_remainder
|
||||
|
||||
return render_segments(self.segments)
|
||||
return self._render_segments(mode)
|
||||
|
||||
def _render_segments(self, mode, render_highlighted=True):
|
||||
'''Internal segment rendering method.
|
||||
|
||||
This method loops through the segment array and compares the
|
||||
foreground/background colors and divider properties and returns the
|
||||
rendered statusline as a string.
|
||||
|
||||
The method always renders the raw segment contents (i.e. without
|
||||
highlighting strings added), and only renders the highlighted
|
||||
statusline if render_highlighted is True.
|
||||
'''
|
||||
rendered_highlighted = u''
|
||||
segments_len = len(self.segments)
|
||||
|
||||
for index, segment in enumerate(self.segments):
|
||||
prev_segment = self.segments[index - 1] if index > 0 else None
|
||||
next_segment = self.segments[index + 1] if index < segments_len - 1 else None
|
||||
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_hl = ''
|
||||
segment_hl = ''
|
||||
|
||||
if render_highlighted:
|
||||
if divider_type == 'hard':
|
||||
divider_hl = self.hl(segment['highlight'][mode]['bg'], compare_segment['highlight'][mode]['bg'], False)
|
||||
|
||||
segment_hl = self.hl(**segment['highlight'][mode])
|
||||
|
||||
if segment['type'] == 'filler':
|
||||
rendered_highlighted += segment['contents'] or ''
|
||||
elif segment['draw_divider'] or divider_type == 'hard':
|
||||
# Draw divider if specified, or if it's a hard divider
|
||||
# Note: Hard dividers are always drawn, regardless of
|
||||
# the draw_divider option
|
||||
if segment['side'] == 'left':
|
||||
segment['rendered_raw'] += outer_padding + segment['contents'] + ' ' + divider + ' '
|
||||
rendered_highlighted += segment_hl + outer_padding + segment['contents'] + ' ' + divider_hl + divider + ' '
|
||||
else:
|
||||
segment['rendered_raw'] += ' ' + divider + ' ' + segment['contents'] + outer_padding
|
||||
rendered_highlighted += ' ' + divider_hl + divider + segment_hl + ' ' + segment['contents'] + outer_padding
|
||||
elif segment['contents']:
|
||||
# Segments without divider
|
||||
if segment['side'] == 'left':
|
||||
segment['rendered_raw'] += outer_padding + segment['contents']
|
||||
rendered_highlighted += segment_hl + outer_padding + segment['contents']
|
||||
else:
|
||||
segment['rendered_raw'] += segment['contents'] + outer_padding
|
||||
rendered_highlighted += segment_hl + segment['contents'] + outer_padding
|
||||
else:
|
||||
raise ValueError('Unknown segment type')
|
||||
|
||||
return rendered_highlighted
|
||||
|
||||
def _total_len(self):
|
||||
'''Return total/rendered length of all segments.
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from colorscheme import cterm_to_hex
|
||||
|
||||
|
||||
def mksegment(contents=None, cterm_fg=False, cterm_bg=False, attr=False, hex_fg=False, hex_bg=False, side='l', draw_divider=True, priority=-1, filler=False):
|
||||
'''Convenience wrapper for segment generation.
|
||||
'''
|
||||
if contents is not None or filler:
|
||||
try:
|
||||
contents = unicode(contents or u'')
|
||||
except UnicodeDecodeError:
|
||||
contents = contents.decode('utf-8') or u''
|
||||
|
||||
return {
|
||||
'contents': contents,
|
||||
'fg': (cterm_fg, hex_fg or cterm_to_hex.get(cterm_fg, 0xffffff)),
|
||||
'bg': (cterm_bg, hex_bg or cterm_to_hex.get(cterm_bg, 0x000000)),
|
||||
'attr': attr,
|
||||
'side': side,
|
||||
'draw_divider': False if filler else draw_divider,
|
||||
'priority': priority,
|
||||
'filler': filler,
|
||||
}
|
|
@ -4,20 +4,22 @@ import importlib
|
|||
|
||||
|
||||
class Theme(object):
|
||||
def __init__(self, ext, theme_config, common_config):
|
||||
def __init__(self, ext, colorscheme, theme_config, common_config):
|
||||
self.colorscheme = colorscheme
|
||||
self.dividers = theme_config.get('dividers', common_config['dividers'])
|
||||
self.segments = []
|
||||
|
||||
for side in ['left', 'right']:
|
||||
for segment in theme_config['segments'].get(side, []):
|
||||
contents = None
|
||||
contents_func = None
|
||||
segment_type = segment.get('type', 'function')
|
||||
|
||||
if segment_type == 'function':
|
||||
# Import segment function and assign it to the contents
|
||||
function_module = 'powerline.ext.{0}.segments'.format(ext)
|
||||
function_name = segment['name']
|
||||
contents = getattr(importlib.import_module(function_module), function_name)
|
||||
contents_func = getattr(importlib.import_module(function_module), function_name)
|
||||
elif segment_type == 'string':
|
||||
contents = segment.get('contents')
|
||||
elif segment_type == 'filler':
|
||||
|
@ -25,11 +27,14 @@ class Theme(object):
|
|||
else:
|
||||
raise TypeError('Unknown segment type: {0}'.format(segment_type))
|
||||
|
||||
highlighting_group = segment.get('highlight', segment.get('name'))
|
||||
|
||||
self.segments.append({
|
||||
'type': segment_type,
|
||||
'highlight': segment.get('highlight', segment.get('name')),
|
||||
'before': segment.get('before'),
|
||||
'after': segment.get('after'),
|
||||
'highlight': self.colorscheme.get_group_highlighting(highlighting_group),
|
||||
'before': segment.get('before', ''),
|
||||
'after': segment.get('after', ''),
|
||||
'contents_func': contents_func,
|
||||
'contents': contents,
|
||||
'args': segment.get('args', {}),
|
||||
'ljust': segment.get('ljust', False),
|
||||
|
@ -42,7 +47,38 @@ class Theme(object):
|
|||
})
|
||||
|
||||
def get_divider(self, side='left', type='soft'):
|
||||
'''Return segment divider.
|
||||
'''
|
||||
return self.dividers[side][type]
|
||||
|
||||
def get_segments(self):
|
||||
return self.segments
|
||||
'''Return all segments.
|
||||
|
||||
Function segments are called, and all segments get their before/after
|
||||
and ljust/rjust properties applied.
|
||||
'''
|
||||
return_segments = []
|
||||
for segment in self.segments:
|
||||
if segment['type'] == 'function':
|
||||
contents_func_ret = segment['contents_func'](**segment['args'])
|
||||
|
||||
if contents_func_ret is None:
|
||||
continue
|
||||
|
||||
try:
|
||||
segment['highlight'] = self.colorscheme.get_group_highlighting(contents_func_ret['highlight'])
|
||||
segment['contents'] = contents_func_ret['contents']
|
||||
except TypeError:
|
||||
segment['contents'] = contents_func_ret
|
||||
elif segment['type'] == 'filler' or (segment['type'] == 'string' and segment['contents'] is not None):
|
||||
pass
|
||||
else:
|
||||
continue
|
||||
|
||||
segment['contents'] = unicode(segment['before'] + unicode(segment['contents']) + segment['after'])\
|
||||
.ljust(segment['ljust'])\
|
||||
.rjust(segment['rjust'])
|
||||
|
||||
return_segments.append(segment)
|
||||
|
||||
return return_segments
|
||||
|
|
Loading…
Reference in New Issue