Merge branch 'feature/project-restructuring' into develop
This commit is contained in:
commit
98337d26ec
|
@ -7,16 +7,17 @@ import os
|
|||
import sys
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from lib.core import Powerline, mksegment
|
||||
from lib.renderers import TerminalSegmentRenderer
|
||||
from powerline.core import Powerline
|
||||
from powerline.segment import mksegment
|
||||
from powerline.ext.terminal import TerminalRenderer
|
||||
|
||||
powerline = Powerline([
|
||||
mksegment('⭤ SSH', 220, 166, attr=Powerline.ATTR_BOLD),
|
||||
mksegment('⭤ SSH', 220, 166, attr=TerminalRenderer.ATTR_BOLD),
|
||||
mksegment('username', 153, 31),
|
||||
mksegment('~', 248, 239),
|
||||
mksegment('projects', 248, 239),
|
||||
mksegment('powerline', 231, 239, attr=Powerline.ATTR_BOLD),
|
||||
mksegment('powerline', 231, 239, attr=TerminalRenderer.ATTR_BOLD),
|
||||
mksegment(filler=True),
|
||||
])
|
||||
|
||||
print(powerline.render(TerminalSegmentRenderer()))
|
||||
print(powerline.render(TerminalRenderer))
|
|
@ -9,15 +9,16 @@ import os
|
|||
import sys
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from lib.core import Powerline, mksegment
|
||||
from lib.renderers import TmuxSegmentRenderer
|
||||
from powerline.core import Powerline
|
||||
from powerline.segment import mksegment
|
||||
from powerline.ext.tmux import TmuxRenderer
|
||||
|
||||
powerline = Powerline([
|
||||
mksegment('⭤ SSH', 220, 166, attr=Powerline.ATTR_BOLD),
|
||||
mksegment('⭤ SSH', 220, 166, attr=TmuxRenderer.ATTR_BOLD),
|
||||
mksegment('username', 153, 31),
|
||||
mksegment('23:45', 248, 239),
|
||||
mksegment('10.0.0.110', 231, 239, attr=Powerline.ATTR_BOLD),
|
||||
mksegment('10.0.0.110', 231, 239, attr=TmuxRenderer.ATTR_BOLD),
|
||||
mksegment(filler=True, cterm_fg=236, cterm_bg=236),
|
||||
])
|
||||
|
||||
print(powerline.render(TmuxSegmentRenderer()).encode('utf-8'))
|
||||
print(powerline.render(TmuxRenderer).encode('utf-8'))
|
|
@ -1,197 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import vim
|
||||
import os
|
||||
|
||||
from lib.core import Powerline, mksegment
|
||||
from lib.renderers import VimSegmentRenderer
|
||||
|
||||
modes = {
|
||||
'n': 'NORMAL',
|
||||
'no': 'N·OPER',
|
||||
'v': 'VISUAL',
|
||||
'V': 'V·LINE',
|
||||
'': 'V·BLCK',
|
||||
's': 'SELECT',
|
||||
'S': 'S·LINE',
|
||||
'': 'S·BLCK',
|
||||
'i': 'INSERT',
|
||||
'R': 'REPLACE',
|
||||
'Rv': 'V·RPLCE',
|
||||
'c': 'COMMND',
|
||||
'cv': 'VIM EX',
|
||||
'ce': 'EX',
|
||||
'r': 'PROMPT',
|
||||
'rm': 'MORE',
|
||||
'r?': 'CONFIRM',
|
||||
'!': 'SHELL',
|
||||
}
|
||||
|
||||
# We need to replace this private use glyph with a double-percent later
|
||||
percent_placeholder = u''
|
||||
|
||||
if hasattr(vim, 'bindeval'):
|
||||
# This branch is used to avoid invoking vim parser as much as possible
|
||||
|
||||
def get_vim_func(f, rettype=None):
|
||||
try:
|
||||
return vim.bindeval('function("' + f + '")')
|
||||
except vim.error:
|
||||
return None
|
||||
|
||||
vim_globals = vim.bindeval('g:')
|
||||
|
||||
def set_global_var(var, val):
|
||||
vim_globals[var] = val
|
||||
else:
|
||||
import json
|
||||
|
||||
class VimFunc(object):
|
||||
__slots__ = ('f', 'rettype')
|
||||
|
||||
def __init__(self, f, rettype=None):
|
||||
self.f = f
|
||||
self.rettype = rettype
|
||||
|
||||
def __call__(self, *args):
|
||||
r = vim.eval(self.f + '(' + json.dumps(args)[1:-1] + ')')
|
||||
if self.rettype:
|
||||
return self.rettype(r)
|
||||
return r
|
||||
|
||||
get_vim_func = VimFunc
|
||||
|
||||
def set_global_var(var, val): # NOQA
|
||||
vim.command('let g:{0}={1}'.format(var, json.dumps(val)))
|
||||
|
||||
vim_funcs = {
|
||||
'winwidth': get_vim_func('winwidth', rettype=int),
|
||||
'mode': get_vim_func('mode'),
|
||||
'fghead': get_vim_func('fugitive#head'),
|
||||
'line': get_vim_func('line', rettype=int),
|
||||
'col': get_vim_func('col', rettype=int),
|
||||
'expand': get_vim_func('expand'),
|
||||
'tbcurtag': get_vim_func('tagbar#currenttag'),
|
||||
'hlexists': get_vim_func('hlexists', rettype=int),
|
||||
}
|
||||
|
||||
getwinvar = get_vim_func('getwinvar')
|
||||
setwinvar = get_vim_func('setwinvar')
|
||||
|
||||
|
||||
def statusline(winnr):
|
||||
winwidth = vim_funcs['winwidth'](winnr)
|
||||
|
||||
current = getwinvar(winnr, 'current')
|
||||
windata = getwinvar(winnr, 'powerline')
|
||||
|
||||
if current:
|
||||
# Recreate segment data for each redraw if we're in the current window
|
||||
line_current = vim_funcs['line']('.')
|
||||
line_end = vim_funcs['line']('$')
|
||||
line_percent = line_current * 100 // line_end
|
||||
|
||||
try:
|
||||
branch = vim_funcs['fghead'](5)
|
||||
except vim.error:
|
||||
vim_funcs['fghead'] = None
|
||||
branch = ''
|
||||
except TypeError:
|
||||
branch = ''
|
||||
if branch:
|
||||
branch = u'⭠ ' + branch
|
||||
|
||||
# Fun gradient colored percent segment
|
||||
line_percent_gradient = [160, 166, 172, 178, 184, 190]
|
||||
line_percent_color = line_percent_gradient[int((len(line_percent_gradient) - 1) * line_percent / 100)]
|
||||
|
||||
col_current = vim_funcs['col']('.')
|
||||
|
||||
filepath, filename = os.path.split(vim_funcs['expand']('%:~:.'))
|
||||
filename_color = 231
|
||||
if filepath:
|
||||
filepath += os.sep
|
||||
|
||||
if not filename:
|
||||
filename = '[No Name]'
|
||||
filename_color = 250
|
||||
|
||||
readonly = vim.eval('&ro ? "⭤ " : ""')
|
||||
modified = vim.eval('&mod ? " +" : ""')
|
||||
|
||||
try:
|
||||
currenttag = vim_funcs['tbcurtag']('%s', '')
|
||||
except vim.error:
|
||||
vim_funcs['tbcurtag'] = None
|
||||
currenttag = ''
|
||||
except TypeError:
|
||||
currenttag = ''
|
||||
|
||||
windata = {
|
||||
'paste': vim.eval('&paste ? "PASTE" : ""'),
|
||||
'branch': branch,
|
||||
'readonly': readonly,
|
||||
'filepath': filepath,
|
||||
'filename': filename,
|
||||
'filename_color': filename_color,
|
||||
'modified': modified,
|
||||
'currenttag': currenttag,
|
||||
'fileformat': vim.eval('&ff'),
|
||||
'fileencoding': vim.eval('&fenc'),
|
||||
'filetype': vim.eval('&ft'),
|
||||
'line_percent': str(line_percent).rjust(3) + percent_placeholder,
|
||||
'line_percent_color': line_percent_color,
|
||||
'linecurrent': str(line_current).rjust(3),
|
||||
'colcurrent': ':' + str(col_current).ljust(2),
|
||||
}
|
||||
|
||||
setwinvar(winnr, 'powerline', windata)
|
||||
|
||||
mode = modes[vim_funcs['mode']()]
|
||||
|
||||
if not current:
|
||||
mode = None
|
||||
|
||||
powerline = Powerline([
|
||||
mksegment(mode, 22, 148, attr=Powerline.ATTR_BOLD),
|
||||
mksegment(windata['paste'], 231, 166, attr=Powerline.ATTR_BOLD),
|
||||
mksegment(windata['branch'], 250, 240, priority=60),
|
||||
mksegment(windata['readonly'], 196, 240, draw_divider=False),
|
||||
mksegment(windata['filepath'], 250, 240, draw_divider=False, priority=40),
|
||||
mksegment(windata['filename'], windata['filename_color'], 240, attr=Powerline.ATTR_BOLD, draw_divider=False),
|
||||
mksegment(windata['modified'], 220, 240, attr=Powerline.ATTR_BOLD),
|
||||
mksegment(windata['currenttag'], 246, 236, draw_divider=False, priority=100),
|
||||
mksegment(filler=True, cterm_fg=236, cterm_bg=236),
|
||||
mksegment(windata['fileformat'], 247, 236, side='r', priority=50, draw_divider=False),
|
||||
mksegment(windata['fileencoding'], 247, 236, side='r', priority=50),
|
||||
mksegment(windata['filetype'], 247, 236, side='r', priority=50),
|
||||
mksegment(windata['line_percent'], windata['line_percent_color'], 240, side='r', priority=30),
|
||||
mksegment(u'⭡ ', 239, 252, side='r'),
|
||||
mksegment(windata['linecurrent'], 235, 252, attr=Powerline.ATTR_BOLD, side='r', draw_divider=False),
|
||||
mksegment(windata['colcurrent'], 244, 252, side='r', priority=30, draw_divider=False),
|
||||
])
|
||||
|
||||
renderer = VimSegmentRenderer()
|
||||
stl = powerline.render(renderer, winwidth)
|
||||
|
||||
# Replace percent placeholders
|
||||
stl = stl.replace(percent_placeholder, '%%')
|
||||
|
||||
# Create highlighting groups
|
||||
for idx, hl in renderer.hl_groups.items():
|
||||
if vim_funcs['hlexists'](hl['name']):
|
||||
# Only create hl group if it doesn't already exist
|
||||
continue
|
||||
|
||||
vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format(
|
||||
group=hl['name'],
|
||||
ctermfg=hl['ctermfg'],
|
||||
guifg='#{0:06x}'.format(hl['guifg']) if hl['guifg'] != 'NONE' else 'NONE',
|
||||
ctermbg=hl['ctermbg'],
|
||||
guibg='#{0:06x}'.format(hl['guibg']) if hl['guibg'] != 'NONE' else 'NONE',
|
||||
attr=','.join(hl['attr']),
|
||||
))
|
||||
|
||||
return stl
|
||||
|
||||
# vim: ft=python ts=4 sts=4 sw=4 noet
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
python import sys, vim, os
|
||||
python sys.path.append(vim.eval('expand("<sfile>:h:h:h")'))
|
||||
python from examples.vim.powerline import statusline
|
||||
python from examples.vim.pl import statusline
|
||||
python from powerline.core import Powerline
|
||||
python pl = Powerline('vim')
|
||||
|
||||
if exists('*pyeval')
|
||||
let s:pyeval = function('pyeval')
|
||||
|
@ -15,7 +17,7 @@ else
|
|||
endif
|
||||
|
||||
function! Powerline(winnr)
|
||||
return s:pyeval('statusline('. a:winnr .')')
|
||||
return s:pyeval('pl.renderer.render('. a:winnr .')')
|
||||
endfunction
|
||||
|
||||
function! s:WinDoPowerline()
|
||||
|
|
162
lib/core.py
162
lib/core.py
|
@ -1,162 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from lib.colors import cterm_to_hex
|
||||
|
||||
|
||||
class Powerline(object):
|
||||
ATTR_BOLD = 1
|
||||
ATTR_ITALIC = 2
|
||||
ATTR_UNDERLINE = 4
|
||||
|
||||
dividers = {
|
||||
'l': {
|
||||
'hard': u'⮀',
|
||||
'soft': u'⮁',
|
||||
},
|
||||
'r': {
|
||||
'hard': u'⮂',
|
||||
'soft': u'⮃',
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, segments):
|
||||
'''Create a new Powerline.
|
||||
|
||||
Segments that have empty contents and aren't filler segments are
|
||||
dropped from the segment array.
|
||||
'''
|
||||
self.segments = [segment for segment in segments if segment['contents'] or segment['filler']]
|
||||
self._hl = {}
|
||||
|
||||
def render(self, renderer, 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.
|
||||
|
||||
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
|
||||
with a negative priority are left. If one or more filler segments are
|
||||
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] = renderer.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] = renderer.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)
|
||||
|
||||
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]
|
||||
|
||||
while self._total_len() > width and len(segments_priority):
|
||||
self.segments.remove(segments_priority[0])
|
||||
segments_priority.pop(0)
|
||||
|
||||
# Do another render pass so we can calculate the correct amount of filler space
|
||||
render_segments(self.segments)
|
||||
|
||||
# Distribute the remaining space on the filler segments
|
||||
segments_fillers = [segment for segment in self.segments if segment['filler'] is True]
|
||||
if segments_fillers:
|
||||
segments_fillers_len, segments_fillers_remainder = divmod((width - self._total_len()), 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 render_segments(self.segments)
|
||||
|
||||
def _total_len(self):
|
||||
'''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]))
|
||||
|
||||
|
||||
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.
|
||||
'''
|
||||
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,
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
class SegmentRenderer:
|
||||
def hl(self, fg=None, bg=None, attr=None):
|
||||
raise NotImplementedError
|
||||
|
||||
from lib.renderers.terminal import TerminalSegmentRenderer
|
||||
from lib.renderers.tmux import TmuxSegmentRenderer
|
||||
from lib.renderers.vim import VimSegmentRenderer
|
|
@ -1,60 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from lib.core import Powerline
|
||||
from lib.renderers import SegmentRenderer
|
||||
|
||||
|
||||
class VimSegmentRenderer(SegmentRenderer):
|
||||
'''Powerline vim segment renderer.
|
||||
'''
|
||||
def __init__(self):
|
||||
self.hl_groups = {}
|
||||
|
||||
def hl(self, fg=None, bg=None, attr=None):
|
||||
'''Highlight a segment.
|
||||
|
||||
If an argument is None, the argument is ignored. If an argument is
|
||||
False, the argument is reset to the terminal defaults. If an argument
|
||||
is a valid color or attribute, it's added to the vim highlight group.
|
||||
'''
|
||||
# We don't need to explicitly reset attributes in vim, so skip those calls
|
||||
if not attr and not bg and not fg:
|
||||
return ''
|
||||
|
||||
if not (fg, bg, attr) in self.hl_groups:
|
||||
hl_group = {
|
||||
'ctermfg': 'NONE',
|
||||
'guifg': 'NONE',
|
||||
'ctermbg': 'NONE',
|
||||
'guibg': 'NONE',
|
||||
'attr': ['NONE'],
|
||||
'name': '',
|
||||
}
|
||||
|
||||
if fg is not None and fg is not False:
|
||||
hl_group['ctermfg'] = fg[0]
|
||||
hl_group['guifg'] = fg[1]
|
||||
|
||||
if bg is not None and bg is not False:
|
||||
hl_group['ctermbg'] = bg[0]
|
||||
hl_group['guibg'] = bg[1]
|
||||
|
||||
if attr:
|
||||
hl_group['attr'] = []
|
||||
if attr & Powerline.ATTR_BOLD:
|
||||
hl_group['attr'].append('bold')
|
||||
if attr & Powerline.ATTR_ITALIC:
|
||||
hl_group['attr'].append('italic')
|
||||
if attr & Powerline.ATTR_UNDERLINE:
|
||||
hl_group['attr'].append('underline')
|
||||
|
||||
hl_group['name'] = 'Pl_' + \
|
||||
str(hl_group['ctermfg']) + '_' + \
|
||||
str(hl_group['guifg']) + '_' + \
|
||||
str(hl_group['ctermbg']) + '_' + \
|
||||
str(hl_group['guibg']) + '_' + \
|
||||
''.join(hl_group['attr'])
|
||||
|
||||
self.hl_groups[(fg, bg, attr)] = hl_group
|
||||
|
||||
return '%#' + self.hl_groups[(fg, bg, attr)]['name'] + '#'
|
|
@ -1,3 +1,105 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
class Colorscheme(object):
|
||||
default_mode_key = '__default__'
|
||||
|
||||
def __init__(self, colorscheme):
|
||||
'''Initialize a colorscheme.
|
||||
'''
|
||||
self.colors = {}
|
||||
self.modes_groups = {
|
||||
self.default_mode_key: {}
|
||||
}
|
||||
|
||||
# Create a dict of color tuples with both a cterm and hex value
|
||||
for color_name, color in colorscheme['colors'].items():
|
||||
try:
|
||||
self.colors[color_name] = (color[0], color[1])
|
||||
except TypeError:
|
||||
self.colors[color_name] = (color, cterm_to_hex[color])
|
||||
|
||||
# Create highlighting groups for all modes
|
||||
for group_name, group_props in colorscheme['groups'].items():
|
||||
group_attr_flag = self._get_attr_flag(group_props.get('attr', []))
|
||||
|
||||
self.modes_groups[self.default_mode_key][group_name] = {
|
||||
'fg': self.colors[group_props['fg']],
|
||||
'bg': self.colors[group_props['bg']],
|
||||
'attr': group_attr_flag,
|
||||
}
|
||||
|
||||
# Create mode-specific highlighting for this group
|
||||
for mode, translations in colorscheme['mode_translations'].items():
|
||||
if not mode in self.modes_groups:
|
||||
self.modes_groups[mode] = {}
|
||||
|
||||
if group_name in translations['groups']:
|
||||
# Override entire group if present in the translations group dict
|
||||
self.modes_groups[mode][group_name] = {
|
||||
'fg': self.colors[translations['groups'][group_name]['fg']],
|
||||
'bg': self.colors[translations['groups'][group_name]['bg']],
|
||||
'attr': self._get_attr_flag(translations['groups'][group_name].get('attr', [])),
|
||||
}
|
||||
else:
|
||||
# Fallback to color translations from the translations colors dict
|
||||
self.modes_groups[mode][group_name] = {
|
||||
'fg': self.colors[translations['colors'].get(group_props['fg'], group_props['fg'])],
|
||||
'bg': self.colors[translations['colors'].get(group_props['bg'], group_props['bg'])],
|
||||
'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.
|
||||
|
||||
If no mode is specified, or the mode doesn't exist, highlighting for
|
||||
the default mode is returned.
|
||||
'''
|
||||
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):
|
||||
'''Convert an attribute array to a renderer flag.
|
||||
'''
|
||||
from powerline.renderer import Renderer
|
||||
|
||||
attr_flag = 0
|
||||
if 'bold' in attributes:
|
||||
attr_flag |= Renderer.ATTR_BOLD
|
||||
if 'italic' in attributes:
|
||||
attr_flag |= Renderer.ATTR_ITALIC
|
||||
if 'underline' in attributes:
|
||||
attr_flag |= Renderer.ATTR_UNDERLINE
|
||||
|
||||
return attr_flag
|
||||
|
||||
cterm_to_hex = {
|
||||
16: 0x000000, 17: 0x00005f, 18: 0x000087, 19: 0x0000af, 20: 0x0000d7, 21: 0x0000ff,
|
||||
22: 0x005f00, 23: 0x005f5f, 24: 0x005f87, 25: 0x005faf, 26: 0x005fd7, 27: 0x005fff,
|
|
@ -0,0 +1,126 @@
|
|||
{
|
||||
"name": "default",
|
||||
"colors": {
|
||||
"black": 16,
|
||||
"white": 231,
|
||||
|
||||
"darkestgreen": 22,
|
||||
"darkgreen": 28,
|
||||
"mediumgreen": 70,
|
||||
"brightgreen": 148,
|
||||
|
||||
"darkestcyan": 23,
|
||||
"mediumcyan": 117,
|
||||
|
||||
"darkestblue": 24,
|
||||
"darkblue": 31,
|
||||
|
||||
"darkestred": 52,
|
||||
"darkred": 88,
|
||||
"mediumred": 124,
|
||||
"brightred": 160,
|
||||
"brightestred": 196,
|
||||
|
||||
"darkestpurple": 55,
|
||||
"mediumpurple": 98,
|
||||
"brightpurple": 189,
|
||||
|
||||
"mediumorange": 166,
|
||||
"brightorange": 208,
|
||||
"brightestorange": 214,
|
||||
|
||||
"brightyellow": 220,
|
||||
|
||||
"gray0": 233,
|
||||
"gray1": 235,
|
||||
"gray2": 236,
|
||||
"gray3": 239,
|
||||
"gray4": 240,
|
||||
"gray5": 241,
|
||||
"gray6": 244,
|
||||
"gray7": 245,
|
||||
"gray8": 247,
|
||||
"gray9": 250,
|
||||
"gray10": 252,
|
||||
|
||||
"gradient1": 190,
|
||||
"gradient2": 184,
|
||||
"gradient3": 178,
|
||||
"gradient4": 172,
|
||||
"gradient5": 166,
|
||||
"gradient6": 160
|
||||
},
|
||||
"groups": {
|
||||
"background": { "fg": "white", "bg": "gray2" },
|
||||
"mode": { "fg": "darkestgreen", "bg": "brightgreen", "attr": ["bold"] },
|
||||
"modified_indicator": { "fg": "brightyellow", "bg": "gray4", "attr": ["bold"] },
|
||||
"paste_indicator": { "fg": "white", "bg": "mediumorange", "attr": ["bold"] },
|
||||
"readonly_indicator": { "fg": "brightestred", "bg": "gray4" },
|
||||
"branch": { "fg": "gray9", "bg": "gray4" },
|
||||
"file_directory": { "fg": "gray9", "bg": "gray4" },
|
||||
"file_name": { "fg": "white", "bg": "gray4", "attr": ["bold"] },
|
||||
"file_name_empty": { "fg": "gray9", "bg": "gray4" },
|
||||
"file_format": { "fg": "gray8", "bg": "gray2" },
|
||||
"file_encoding": { "fg": "gray8", "bg": "gray2" },
|
||||
"file_type": { "fg": "gray8", "bg": "gray2" },
|
||||
"line_percent": { "fg": "gray9", "bg": "gray4" },
|
||||
"line_percent_gradient1": { "fg": "gradient1", "bg": "gray4" },
|
||||
"line_percent_gradient2": { "fg": "gradient2", "bg": "gray4" },
|
||||
"line_percent_gradient3": { "fg": "gradient3", "bg": "gray4" },
|
||||
"line_percent_gradient4": { "fg": "gradient4", "bg": "gray4" },
|
||||
"line_percent_gradient5": { "fg": "gradient5", "bg": "gray4" },
|
||||
"line_percent_gradient6": { "fg": "gradient6", "bg": "gray4" },
|
||||
"line_current": { "fg": "gray1", "bg": "gray10", "attr": ["bold"] },
|
||||
"line_current_symbol": { "fg": "gray1", "bg": "gray10" },
|
||||
"col_current": { "fg": "gray6", "bg": "gray10" }
|
||||
},
|
||||
"mode_translations": {
|
||||
"nc": {
|
||||
"colors": {
|
||||
"gray0": "gray0",
|
||||
"gray1": "gray0",
|
||||
"gray2": "gray0",
|
||||
"gray3": "gray1",
|
||||
"gray4": "gray1",
|
||||
"gray5": "gray1",
|
||||
"gray6": "gray1",
|
||||
"gray7": "gray4",
|
||||
"gray8": "gray4",
|
||||
"gray9": "gray4",
|
||||
"gray10": "gray5",
|
||||
"white": "gray6",
|
||||
"gradient1": "gray5",
|
||||
"gradient2": "gray5",
|
||||
"gradient3": "gray5",
|
||||
"gradient4": "gray5",
|
||||
"gradient5": "gray5",
|
||||
"gradient6": "gray5"
|
||||
},
|
||||
"groups": {}
|
||||
},
|
||||
"i": {
|
||||
"colors": {
|
||||
"gray0": "darkestblue",
|
||||
"gray1": "darkestblue",
|
||||
"gray2": "darkestblue",
|
||||
"gray3": "darkblue",
|
||||
"gray4": "darkblue",
|
||||
"gray5": "darkestcyan",
|
||||
"gray6": "darkestcyan",
|
||||
"gray7": "darkestcyan",
|
||||
"gray8": "mediumcyan",
|
||||
"gray9": "mediumcyan",
|
||||
"gray10": "mediumcyan",
|
||||
"gradient1": "mediumcyan",
|
||||
"gradient2": "mediumcyan",
|
||||
"gradient3": "mediumcyan",
|
||||
"gradient4": "mediumcyan",
|
||||
"gradient5": "mediumcyan",
|
||||
"gradient6": "mediumcyan"
|
||||
},
|
||||
"groups": {
|
||||
"mode": { "fg": "darkestcyan", "bg": "white", "attr": ["bold"] }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"common": {
|
||||
"dividers": {
|
||||
"left": {
|
||||
"hard": "⮀",
|
||||
"soft": "⮁"
|
||||
},
|
||||
"right": {
|
||||
"hard": "⮂",
|
||||
"soft": "⮃"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ext": {
|
||||
"vim": {
|
||||
"colorscheme": "default",
|
||||
"theme": "default"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import importlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
from colorscheme import Colorscheme
|
||||
from theme import Theme
|
||||
|
||||
|
||||
class Powerline(object):
|
||||
def __init__(self, ext):
|
||||
try:
|
||||
config_home = os.environ['XDG_CONFIG_HOME']
|
||||
except KeyError:
|
||||
config_home = os.path.expanduser('~/.config')
|
||||
|
||||
config_path = os.path.join(config_home, 'powerline')
|
||||
plugin_path = os.path.realpath(os.path.dirname(__file__))
|
||||
self.search_paths = [config_path, plugin_path]
|
||||
|
||||
sys.path[:0] = self.search_paths
|
||||
|
||||
# Load main config file
|
||||
config = self._load_json_config('config')
|
||||
self.config = config['common']
|
||||
self.config_ext = config['ext'][ext]
|
||||
|
||||
# Load and initialize colorscheme
|
||||
colorscheme_config = self._load_json_config(os.path.join('colorschemes', self.config_ext['colorscheme']))
|
||||
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)
|
||||
|
||||
# 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)
|
||||
|
||||
def _load_json_config(self, config_file):
|
||||
config_file += '.json'
|
||||
for path in self.search_paths:
|
||||
config_file_path = os.path.join(path, config_file)
|
||||
if os.path.isfile(config_file_path):
|
||||
with open(config_file_path, 'rb') as config_file_fp:
|
||||
return json.load(config_file_fp)
|
||||
|
||||
raise IOError('Config file not found in search path: {0}'.format(config_file))
|
|
@ -0,0 +1 @@
|
|||
from renderer import TerminalRenderer # NOQA
|
|
@ -1,10 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from lib.core import Powerline
|
||||
from lib.renderers import SegmentRenderer
|
||||
from powerline.renderer import Renderer
|
||||
|
||||
|
||||
class TerminalSegmentRenderer(SegmentRenderer):
|
||||
class TerminalRenderer(Renderer):
|
||||
'''Powerline terminal segment renderer.
|
||||
'''
|
||||
def hl(self, fg=None, bg=None, attr=None):
|
||||
|
@ -32,7 +31,7 @@ class TerminalSegmentRenderer(SegmentRenderer):
|
|||
if attr is False:
|
||||
ansi += [22]
|
||||
else:
|
||||
if attr & Powerline.ATTR_BOLD:
|
||||
if attr & Renderer.ATTR_BOLD:
|
||||
ansi += [1]
|
||||
|
||||
return '[{0}m'.format(';'.join(str(attr) for attr in ansi))
|
|
@ -0,0 +1 @@
|
|||
from renderer import TmuxRenderer # NOQA
|
|
@ -1,10 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from lib.core import Powerline
|
||||
from lib.renderers import SegmentRenderer
|
||||
from powerline.renderer import Renderer
|
||||
|
||||
|
||||
class TmuxSegmentRenderer(SegmentRenderer):
|
||||
class TmuxRenderer(Renderer):
|
||||
'''Powerline tmux segment renderer.
|
||||
'''
|
||||
def hl(self, fg=None, bg=None, attr=None):
|
||||
|
@ -22,15 +21,15 @@ class TmuxSegmentRenderer(SegmentRenderer):
|
|||
if attr is False:
|
||||
tmux_attr += ['nobold', 'noitalics', 'nounderscore']
|
||||
else:
|
||||
if attr & Powerline.ATTR_BOLD:
|
||||
if attr & Renderer.ATTR_BOLD:
|
||||
tmux_attr += ['bold']
|
||||
else:
|
||||
tmux_attr += ['nobold']
|
||||
if attr & Powerline.ATTR_ITALIC:
|
||||
if attr & Renderer.ATTR_ITALIC:
|
||||
tmux_attr += ['italics']
|
||||
else:
|
||||
tmux_attr += ['noitalics']
|
||||
if attr & Powerline.ATTR_UNDERLINE:
|
||||
if attr & Renderer.ATTR_UNDERLINE:
|
||||
tmux_attr += ['underscore']
|
||||
else:
|
||||
tmux_attr += ['nounderscore']
|
|
@ -0,0 +1 @@
|
|||
from renderer import VimRenderer # NOQA
|
|
@ -0,0 +1,107 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from powerline.ext.vim.bindings import vim_get_func
|
||||
from powerline.renderer import Renderer
|
||||
|
||||
import vim
|
||||
|
||||
vim_mode = vim_get_func('mode')
|
||||
vim_winwidth = vim_get_func('winwidth', rettype=int)
|
||||
vim_getwinvar = vim_get_func('getwinvar')
|
||||
vim_setwinvar = vim_get_func('setwinvar')
|
||||
|
||||
|
||||
class VimRenderer(Renderer):
|
||||
'''Powerline vim segment renderer.
|
||||
'''
|
||||
PERCENT_PLACEHOLDER = u''
|
||||
|
||||
def __init__(self, theme):
|
||||
super(VimRenderer, self).__init__(theme)
|
||||
self.hl_groups = {}
|
||||
|
||||
def render(self, winnr):
|
||||
'''Render all segments.
|
||||
|
||||
This method handles replacing of the percent placeholder for vim
|
||||
statuslines, and it caches segment contents which are retrieved and
|
||||
used in non-current windows.
|
||||
'''
|
||||
current = vim_getwinvar(winnr, 'current')
|
||||
winwidth = vim_winwidth(winnr)
|
||||
|
||||
if current or not vim_getwinvar(winnr, 'powerline'):
|
||||
contents_cached = {segment['key']: segment['contents'] for segment in self.segments if segment['type'] == 'function'}
|
||||
vim_setwinvar(winnr, 'powerline', contents_cached)
|
||||
|
||||
if current:
|
||||
mode = vim_mode()
|
||||
contents_override = None
|
||||
else:
|
||||
mode = 'nc'
|
||||
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 = statusline.replace(self.PERCENT_PLACEHOLDER, '%%')
|
||||
|
||||
return statusline
|
||||
|
||||
def hl(self, fg=None, bg=None, attr=None):
|
||||
'''Highlight a segment.
|
||||
|
||||
If an argument is None, the argument is ignored. If an argument is
|
||||
False, the argument is reset to the terminal defaults. If an argument
|
||||
is a valid color or attribute, it's added to the vim highlight group.
|
||||
'''
|
||||
# We don't need to explicitly reset attributes in vim, so skip those calls
|
||||
if not attr and not bg and not fg:
|
||||
return ''
|
||||
|
||||
if not (fg, bg, attr) in self.hl_groups:
|
||||
hl_group = {
|
||||
'ctermfg': 'NONE',
|
||||
'guifg': 'NONE',
|
||||
'ctermbg': 'NONE',
|
||||
'guibg': 'NONE',
|
||||
'attr': ['NONE'],
|
||||
'name': '',
|
||||
}
|
||||
|
||||
if fg is not None and fg is not False:
|
||||
hl_group['ctermfg'] = fg[0]
|
||||
hl_group['guifg'] = fg[1]
|
||||
|
||||
if bg is not None and bg is not False:
|
||||
hl_group['ctermbg'] = bg[0]
|
||||
hl_group['guibg'] = bg[1]
|
||||
|
||||
if attr:
|
||||
hl_group['attr'] = []
|
||||
if attr & self.ATTR_BOLD:
|
||||
hl_group['attr'].append('bold')
|
||||
if attr & self.ATTR_ITALIC:
|
||||
hl_group['attr'].append('italic')
|
||||
if attr & self.ATTR_UNDERLINE:
|
||||
hl_group['attr'].append('underline')
|
||||
|
||||
hl_group['name'] = 'Pl_' + \
|
||||
str(hl_group['ctermfg']) + '_' + \
|
||||
str(hl_group['guifg']) + '_' + \
|
||||
str(hl_group['ctermbg']) + '_' + \
|
||||
str(hl_group['guibg']) + '_' + \
|
||||
''.join(hl_group['attr'])
|
||||
|
||||
self.hl_groups[(fg, bg, attr)] = hl_group
|
||||
|
||||
# Create highlighting group in vim
|
||||
vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format(
|
||||
group=hl_group['name'],
|
||||
ctermfg=hl_group['ctermfg'],
|
||||
guifg='#{0:06x}'.format(hl_group['guifg']) if hl_group['guifg'] != 'NONE' else 'NONE',
|
||||
ctermbg=hl_group['ctermbg'],
|
||||
guibg='#{0:06x}'.format(hl_group['guibg']) if hl_group['guibg'] != 'NONE' else 'NONE',
|
||||
attr=','.join(hl_group['attr']),
|
||||
))
|
||||
|
||||
return '%#' + self.hl_groups[(fg, bg, attr)]['name'] + '#'
|
|
@ -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),
|
||||
|
@ -15,24 +16,24 @@ vim_funcs = {
|
|||
}
|
||||
|
||||
vim_modes = {
|
||||
'n': 'NORMAL',
|
||||
'no': 'N·OPER',
|
||||
'v': 'VISUAL',
|
||||
'V': 'V·LINE',
|
||||
'': 'V·BLCK',
|
||||
's': 'SELECT',
|
||||
'S': 'S·LINE',
|
||||
'': 'S·BLCK',
|
||||
'i': 'INSERT',
|
||||
'R': 'REPLACE',
|
||||
'Rv': 'V·RPLCE',
|
||||
'c': 'COMMND',
|
||||
'cv': 'VIM EX',
|
||||
'ce': 'EX',
|
||||
'r': 'PROMPT',
|
||||
'rm': 'MORE',
|
||||
'r?': 'CONFIRM',
|
||||
'!': 'SHELL',
|
||||
'n': u'NORMAL',
|
||||
'no': u'N·OPER',
|
||||
'v': u'VISUAL',
|
||||
'V': u'V·LINE',
|
||||
'': u'V·BLCK',
|
||||
's': u'SELECT',
|
||||
'S': u'S·LINE',
|
||||
'': u'S·BLCK',
|
||||
'i': u'INSERT',
|
||||
'R': u'REPLACE',
|
||||
'Rv': u'V·RPLCE',
|
||||
'c': u'COMMND',
|
||||
'cv': u'VIM EX',
|
||||
'ce': u'EX',
|
||||
'r': u'PROMPT',
|
||||
'rm': u'MORE',
|
||||
'r?': u'CONFIRM',
|
||||
'!': u'SHELL',
|
||||
}
|
||||
|
||||
|
||||
|
@ -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'+'):
|
||||
|
@ -74,7 +75,7 @@ def readonly_indicator(text=u'⭤'):
|
|||
return text if int(vim.eval('&readonly')) else None
|
||||
|
||||
|
||||
def branch(symbol=u'⭠'):
|
||||
def branch():
|
||||
'''Return VCS branch.
|
||||
|
||||
TODO: Expand this function to handle several VCS plugins.
|
||||
|
@ -87,13 +88,13 @@ def branch(symbol=u'⭠'):
|
|||
except TypeError:
|
||||
pass
|
||||
|
||||
return symbol + ' ' + branch if branch else None
|
||||
return branch if branch else None
|
||||
|
||||
|
||||
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():
|
|
@ -0,0 +1,121 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from colorscheme import Colorscheme
|
||||
|
||||
|
||||
class Renderer(object):
|
||||
ATTR_BOLD = 1
|
||||
ATTR_ITALIC = 2
|
||||
ATTR_UNDERLINE = 4
|
||||
|
||||
def __init__(self, theme):
|
||||
self.segments = []
|
||||
self.theme = theme
|
||||
|
||||
def render(self, mode, width=None, contents_override=None):
|
||||
'''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
|
||||
with a negative priority are left. If one or more filler segments are
|
||||
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)
|
||||
|
||||
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]
|
||||
|
||||
while self._total_len() > width and len(segments_priority):
|
||||
self.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)
|
||||
|
||||
# Distribute the remaining space on the filler segments
|
||||
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
|
||||
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)
|
||||
|
||||
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)
|
||||
mode = mode if mode in self.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 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.
|
||||
|
||||
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]))
|
||||
|
||||
def hl(self, fg=None, bg=None, attr=None):
|
||||
raise NotImplementedError
|
|
@ -0,0 +1,92 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import importlib
|
||||
|
||||
|
||||
class Theme(object):
|
||||
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')
|
||||
segment_module = segment.get('module', 'core')
|
||||
|
||||
if segment_type == 'function':
|
||||
# Import segment function and assign it to the contents
|
||||
function_module = 'powerline.ext.{0}.segments.{1}'.format(ext, segment_module)
|
||||
function_name = segment['name']
|
||||
contents_func = getattr(importlib.import_module(function_module), function_name)
|
||||
elif segment_type == 'string':
|
||||
contents = segment.get('contents')
|
||||
elif segment_type == 'filler':
|
||||
pass
|
||||
else:
|
||||
raise TypeError('Unknown segment type: {0}'.format(segment_type))
|
||||
|
||||
highlighting_group = segment.get('highlight', segment.get('name'))
|
||||
|
||||
self.segments.append({
|
||||
'key': None if segment_type != 'function' else '{0}.{1}'.format(segment_module, function_name),
|
||||
'type': segment_type,
|
||||
'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),
|
||||
'rjust': segment.get('rjust', False),
|
||||
'priority': segment.get('priority', -1),
|
||||
'draw_divider': segment.get('draw_divider', True),
|
||||
'side': side,
|
||||
'exclude_modes': segment.get('exclude_modes', []),
|
||||
'include_modes': segment.get('include_modes', []),
|
||||
})
|
||||
|
||||
def get_divider(self, side='left', type='soft'):
|
||||
'''Return segment divider.
|
||||
'''
|
||||
return self.dividers[side][type]
|
||||
|
||||
def get_segments(self, mode, contents_override=None):
|
||||
'''Return all segments.
|
||||
|
||||
Function segments are called, and all segments get their before/after
|
||||
and ljust/rjust properties applied.
|
||||
'''
|
||||
contents_override = contents_override or {}
|
||||
return_segments = []
|
||||
for segment in self.segments:
|
||||
if mode in segment['exclude_modes'] or (segment['include_modes'] and segment not in segment['include_modes']):
|
||||
continue
|
||||
|
||||
if segment['type'] == 'function':
|
||||
contents = contents_override.get(segment['key'], segment['contents_func'](**segment['args']))
|
||||
|
||||
if contents is None:
|
||||
continue
|
||||
|
||||
try:
|
||||
segment['highlight'] = self.colorscheme.get_group_highlighting(contents['highlight'])
|
||||
segment['contents'] = contents['contents']
|
||||
except TypeError:
|
||||
segment['contents'] = contents
|
||||
elif segment['type'] == 'filler' or (segment['type'] == 'string' and segment['contents'] is not None):
|
||||
pass
|
||||
else:
|
||||
continue
|
||||
|
||||
if not segment['key'] in contents_override:
|
||||
# Only apply before/after/just to non-overridden segments
|
||||
segment['contents'] = unicode(segment['before'] + unicode(segment['contents']) + segment['after'])\
|
||||
.ljust(segment['ljust'])\
|
||||
.rjust(segment['rjust'])
|
||||
|
||||
return_segments.append(segment)
|
||||
|
||||
return return_segments
|
|
@ -0,0 +1,88 @@
|
|||
{
|
||||
"name": "default",
|
||||
"segments": {
|
||||
"left": [
|
||||
{
|
||||
"name": "mode",
|
||||
"exclude_modes": ["nc"]
|
||||
},
|
||||
{
|
||||
"name": "paste_indicator",
|
||||
"exclude_modes": ["nc"],
|
||||
"priority": 10
|
||||
},
|
||||
{
|
||||
"name": "branch",
|
||||
"exclude_modes": ["nc"],
|
||||
"priority": 60,
|
||||
"before": "⭠ "
|
||||
},
|
||||
{
|
||||
"name": "readonly_indicator",
|
||||
"exclude_modes": ["nc"],
|
||||
"draw_divider": false
|
||||
},
|
||||
{
|
||||
"name": "file_directory",
|
||||
"priority": 40,
|
||||
"draw_divider": false
|
||||
},
|
||||
{
|
||||
"name": "file_name",
|
||||
"draw_divider": false
|
||||
},
|
||||
{
|
||||
"name": "modified_indicator",
|
||||
"args": { "text": "+" },
|
||||
"exclude_modes": ["nc"],
|
||||
"before": " "
|
||||
},
|
||||
{
|
||||
"type": "filler",
|
||||
"highlight": ["background"]
|
||||
}
|
||||
],
|
||||
"right": [
|
||||
{
|
||||
"name": "file_format",
|
||||
"draw_divider": false,
|
||||
"exclude_modes": ["nc"],
|
||||
"priority": 50
|
||||
},
|
||||
{
|
||||
"name": "file_encoding",
|
||||
"exclude_modes": ["nc"],
|
||||
"priority": 50
|
||||
},
|
||||
{
|
||||
"name": "file_type",
|
||||
"exclude_modes": ["nc"],
|
||||
"priority": 50
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
{
|
||||
"name": "col_current",
|
||||
"draw_divider": false,
|
||||
"priority": 30,
|
||||
"before": ":",
|
||||
"ljust": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
# flake8: noqa
|
||||
|
||||
from core import (mode, modified_indicator, paste_indicator,
|
||||
readonly_indicator, branch, file_directory, file_name, file_format,
|
||||
file_encoding, file_type, line_percent, line_current, col_current)
|
Loading…
Reference in New Issue