Merge branch 'feature/extended-vim-example' into develop

This commit is contained in:
Kim Silkebækken 2012-11-21 12:23:49 +01:00
commit 3be7dc2a40
7 changed files with 279 additions and 266 deletions

View File

@ -5,7 +5,7 @@
import os import os
import sys import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from lib.core import Powerline, Segment from lib.core import Powerline, Segment
from lib.renderers import TerminalSegmentRenderer from lib.renderers import TerminalSegmentRenderer

View File

@ -2,9 +2,8 @@
import vim import vim
import os import os
import re
from lib.core import Powerline, Segment from lib.core import Powerline, mksegment
from lib.renderers import VimSegmentRenderer from lib.renderers import VimSegmentRenderer
modes = { modes = {
@ -28,6 +27,9 @@ modes = {
'!': 'SHELL', '!': 'SHELL',
} }
# We need to replace this private use glyph with a double-percent later
percent_placeholder = u''
if hasattr(vim, 'bindeval'): if hasattr(vim, 'bindeval'):
# This branch is used to avoid invoking vim parser as much as possible # This branch is used to avoid invoking vim parser as much as possible
@ -70,103 +72,119 @@ vim_funcs = {
'col': get_vim_func('col', rettype=int), 'col': get_vim_func('col', rettype=int),
'expand': get_vim_func('expand'), 'expand': get_vim_func('expand'),
'tbcurtag': get_vim_func('tagbar#currenttag'), 'tbcurtag': get_vim_func('tagbar#currenttag'),
'sstlflag': get_vim_func('SyntasticStatuslineFlag'),
'hlexists': get_vim_func('hlexists', rettype=int), 'hlexists': get_vim_func('hlexists', rettype=int),
} }
getwinvar = get_vim_func('getwinvar')
setwinvar = get_vim_func('setwinvar')
def statusline():
winwidth = vim_funcs['winwidth'](0)
# Prepare segment contents 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']()] mode = modes[vim_funcs['mode']()]
try: if not current:
branch = vim_funcs['fghead'](5) mode = None
except vim.error:
vim_funcs['fghead'] = None
branch = ''
except TypeError:
branch = ''
if branch:
branch = '' + branch
line_current = vim_funcs['line']('.')
line_end = vim_funcs['line']('$')
line_percent = line_current * 100 // line_end
# 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 = ''
# The Syntastic segment is center aligned (filler segment on each side) to show off how the filler segments work
# Not necessarily how it's going to look in the final theme
set_global_var('syntastic_stl_format', '%E{ ERRORS (%e) ⭡ %fe }%W{ WARNINGS (%w) ⭡ %fw } ⮁')
try:
syntastic = vim_funcs['sstlflag']()
except vim.error:
vim_funcs['sstlflag'] = None
syntastic = ''
except TypeError:
syntastic = ''
powerline = Powerline([ powerline = Powerline([
Segment(mode, 22, 148, attr=Segment.ATTR_BOLD), mksegment(mode, 22, 148, attr=Powerline.ATTR_BOLD),
Segment(vim.eval('&paste ? "PASTE" : ""'), 231, 166, attr=Segment.ATTR_BOLD), mksegment(windata['paste'], 231, 166, attr=Powerline.ATTR_BOLD),
Segment(branch, 250, 240, priority=10), mksegment(windata['branch'], 250, 240, priority=60),
Segment(readonly, 196, 240, draw_divider=False), mksegment(windata['readonly'], 196, 240, draw_divider=False),
Segment(filepath, 250, 240, draw_divider=False, priority=5), mksegment(windata['filepath'], 250, 240, draw_divider=False, priority=40),
Segment(filename, filename_color, 240, attr=Segment.ATTR_BOLD, draw_divider=not len(modified)), mksegment(windata['filename'], windata['filename_color'], 240, attr=Powerline.ATTR_BOLD, draw_divider=not len(windata['modified'])),
Segment(modified, 220, 240, attr=Segment.ATTR_BOLD), mksegment(windata['modified'], 220, 240, attr=Powerline.ATTR_BOLD),
Segment(currenttag, 246, 236, draw_divider=False, priority=100), mksegment(windata['currenttag'], 246, 236, draw_divider=False, priority=100),
Segment(filler=True, fg=236, bg=236), mksegment(filler=True, cterm_fg=236, cterm_bg=236),
Segment(syntastic, 214, 236, attr=Segment.ATTR_BOLD, draw_divider=False, priority=100), mksegment(windata['fileformat'], 247, 236, side='r', priority=50),
Segment(filler=True, fg=236, bg=236), mksegment(windata['fileencoding'], 247, 236, side='r', priority=50),
Segment(vim.eval('&ff'), 247, 236, side='r', priority=50), mksegment(windata['filetype'], 247, 236, side='r', priority=50),
Segment(vim.eval('&fenc'), 247, 236, side='r', priority=50), mksegment(windata['line_percent'], windata['line_percent_color'], 240, side='r', priority=30),
Segment(vim.eval('&ft'), 247, 236, side='r', priority=50), mksegment(u'', 239, 252, side='r'),
Segment(str(line_percent).rjust(3) + '%', line_percent_color, 240, side='r', priority=30), mksegment(windata['linecurrent'], 235, 252, attr=Powerline.ATTR_BOLD, side='r', draw_divider=False),
Segment('', 239, 252, side='r'), mksegment(windata['colcurrent'], 244, 252, side='r', priority=30, draw_divider=False),
Segment(str(line_current).rjust(3), 235, 252, attr=Segment.ATTR_BOLD, side='r', draw_divider=False),
Segment(':' + str(col_current).ljust(2), 244, 252, side='r', priority=30, draw_divider=False),
]) ])
renderer = VimSegmentRenderer() renderer = VimSegmentRenderer()
stl = powerline.render(renderer, winwidth) stl = powerline.render(renderer, winwidth)
# Escape percent chars in the statusline, but only if they aren't part of any stl escape sequence # Replace percent placeholders
stl = re.sub('(\w+)\%(?![-{()<=#*%])', '\\1%%', stl) stl = stl.replace(percent_placeholder, '%%')
# Create highlighting groups # Create highlighting groups
for group, hl in renderer.hl_groups.items(): for idx, hl in renderer.hl_groups.items():
if vim_funcs['hlexists'](group): if vim_funcs['hlexists'](hl['name']):
# Only create hl group if it doesn't already exist # Only create hl group if it doesn't already exist
continue continue
vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format( vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attr} gui={attr}'.format(
group=group, group=hl['name'],
ctermfg=hl['ctermfg'], ctermfg=hl['ctermfg'],
guifg='#{0:06x}'.format(hl['guifg']) if hl['guifg'] != 'NONE' else 'NONE', guifg='#{0:06x}'.format(hl['guifg']) if hl['guifg'] != 'NONE' else 'NONE',
ctermbg=hl['ctermbg'], ctermbg=hl['ctermbg'],
@ -176,6 +194,4 @@ def statusline():
return stl return stl
statusline()
# vim: ft=python ts=4 sts=4 sw=4 noet # vim: ft=python ts=4 sts=4 sw=4 noet

View File

@ -6,7 +6,7 @@ python sys.path.append(vim.eval('expand("<sfile>:h:h:h")'))
python from examples.vim.powerline import statusline python from examples.vim.powerline import statusline
if exists('*pyeval') if exists('*pyeval')
let s:pyeval=function('pyeval') let s:pyeval = function('pyeval')
else else
python import json python import json
function! s:pyeval(e) function! s:pyeval(e)
@ -14,8 +14,20 @@ else
endfunction endfunction
endif endif
function! DynStl() function! Powerline(winnr)
return s:pyeval('statusline()') return s:pyeval('statusline('. a:winnr .')')
endfunction endfunction
set stl=%!DynStl() function! s:WinDoPowerline()
if ! exists('w:powerline')
let w:powerline = {}
endif
let &l:stl = '%!Powerline('. winnr() .')'
endfunction
augroup Powerline
autocmd!
autocmd BufEnter,BufWinEnter,WinEnter * let w:current = 1 | let currwin = winnr() | windo call s:WinDoPowerline() | exec currwin . 'wincmd w'
autocmd BufLeave,BufWinLeave,WinLeave * let w:current = 0
augroup END

View File

@ -1,54 +1,42 @@
def cterm_to_hex(cterm_color): cterm_to_hex = {
'''Translate a cterm color index into the corresponding hex/RGB color. 16: 0x000000, 17: 0x00005f, 18: 0x000087, 19: 0x0000af, 20: 0x0000d7, 21: 0x0000ff,
''' 22: 0x005f00, 23: 0x005f5f, 24: 0x005f87, 25: 0x005faf, 26: 0x005fd7, 27: 0x005fff,
color_dict = { 28: 0x008700, 29: 0x00875f, 30: 0x008787, 31: 0x0087af, 32: 0x0087d7, 33: 0x0087ff,
16: 0x000000, 17: 0x00005f, 18: 0x000087, 19: 0x0000af, 20: 0x0000d7, 21: 0x0000ff, 34: 0x00af00, 35: 0x00af5f, 36: 0x00af87, 37: 0x00afaf, 38: 0x00afd7, 39: 0x00afff,
22: 0x005f00, 23: 0x005f5f, 24: 0x005f87, 25: 0x005faf, 26: 0x005fd7, 27: 0x005fff, 40: 0x00d700, 41: 0x00d75f, 42: 0x00d787, 43: 0x00d7af, 44: 0x00d7d7, 45: 0x00d7ff,
28: 0x008700, 29: 0x00875f, 30: 0x008787, 31: 0x0087af, 32: 0x0087d7, 33: 0x0087ff, 46: 0x00ff00, 47: 0x00ff5f, 48: 0x00ff87, 49: 0x00ffaf, 50: 0x00ffd7, 51: 0x00ffff,
34: 0x00af00, 35: 0x00af5f, 36: 0x00af87, 37: 0x00afaf, 38: 0x00afd7, 39: 0x00afff, 52: 0x5f0000, 53: 0x5f005f, 54: 0x5f0087, 55: 0x5f00af, 56: 0x5f00d7, 57: 0x5f00ff,
40: 0x00d700, 41: 0x00d75f, 42: 0x00d787, 43: 0x00d7af, 44: 0x00d7d7, 45: 0x00d7ff, 58: 0x5f5f00, 59: 0x5f5f5f, 60: 0x5f5f87, 61: 0x5f5faf, 62: 0x5f5fd7, 63: 0x5f5fff,
46: 0x00ff00, 47: 0x00ff5f, 48: 0x00ff87, 49: 0x00ffaf, 50: 0x00ffd7, 51: 0x00ffff, 64: 0x5f8700, 65: 0x5f875f, 66: 0x5f8787, 67: 0x5f87af, 68: 0x5f87d7, 69: 0x5f87ff,
52: 0x5f0000, 53: 0x5f005f, 54: 0x5f0087, 55: 0x5f00af, 56: 0x5f00d7, 57: 0x5f00ff, 70: 0x5faf00, 71: 0x5faf5f, 72: 0x5faf87, 73: 0x5fafaf, 74: 0x5fafd7, 75: 0x5fafff,
58: 0x5f5f00, 59: 0x5f5f5f, 60: 0x5f5f87, 61: 0x5f5faf, 62: 0x5f5fd7, 63: 0x5f5fff, 76: 0x5fd700, 77: 0x5fd75f, 78: 0x5fd787, 79: 0x5fd7af, 80: 0x5fd7d7, 81: 0x5fd7ff,
64: 0x5f8700, 65: 0x5f875f, 66: 0x5f8787, 67: 0x5f87af, 68: 0x5f87d7, 69: 0x5f87ff, 82: 0x5fff00, 83: 0x5fff5f, 84: 0x5fff87, 85: 0x5fffaf, 86: 0x5fffd7, 87: 0x5fffff,
70: 0x5faf00, 71: 0x5faf5f, 72: 0x5faf87, 73: 0x5fafaf, 74: 0x5fafd7, 75: 0x5fafff, 88: 0x870000, 89: 0x87005f, 90: 0x870087, 91: 0x8700af, 92: 0x8700d7, 93: 0x8700ff,
76: 0x5fd700, 77: 0x5fd75f, 78: 0x5fd787, 79: 0x5fd7af, 80: 0x5fd7d7, 81: 0x5fd7ff, 94: 0x875f00, 95: 0x875f5f, 96: 0x875f87, 97: 0x875faf, 98: 0x875fd7, 99: 0x875fff,
82: 0x5fff00, 83: 0x5fff5f, 84: 0x5fff87, 85: 0x5fffaf, 86: 0x5fffd7, 87: 0x5fffff, 100: 0x878700, 101: 0x87875f, 102: 0x878787, 103: 0x8787af, 104: 0x8787d7, 105: 0x8787ff,
88: 0x870000, 89: 0x87005f, 90: 0x870087, 91: 0x8700af, 92: 0x8700d7, 93: 0x8700ff, 106: 0x87af00, 107: 0x87af5f, 108: 0x87af87, 109: 0x87afaf, 110: 0x87afd7, 111: 0x87afff,
94: 0x875f00, 95: 0x875f5f, 96: 0x875f87, 97: 0x875faf, 98: 0x875fd7, 99: 0x875fff, 112: 0x87d700, 113: 0x87d75f, 114: 0x87d787, 115: 0x87d7af, 116: 0x87d7d7, 117: 0x87d7ff,
100: 0x878700, 101: 0x87875f, 102: 0x878787, 103: 0x8787af, 104: 0x8787d7, 105: 0x8787ff, 118: 0x87ff00, 119: 0x87ff5f, 120: 0x87ff87, 121: 0x87ffaf, 122: 0x87ffd7, 123: 0x87ffff,
106: 0x87af00, 107: 0x87af5f, 108: 0x87af87, 109: 0x87afaf, 110: 0x87afd7, 111: 0x87afff, 124: 0xaf0000, 125: 0xaf005f, 126: 0xaf0087, 127: 0xaf00af, 128: 0xaf00d7, 129: 0xaf00ff,
112: 0x87d700, 113: 0x87d75f, 114: 0x87d787, 115: 0x87d7af, 116: 0x87d7d7, 117: 0x87d7ff, 130: 0xaf5f00, 131: 0xaf5f5f, 132: 0xaf5f87, 133: 0xaf5faf, 134: 0xaf5fd7, 135: 0xaf5fff,
118: 0x87ff00, 119: 0x87ff5f, 120: 0x87ff87, 121: 0x87ffaf, 122: 0x87ffd7, 123: 0x87ffff, 136: 0xaf8700, 137: 0xaf875f, 138: 0xaf8787, 139: 0xaf87af, 140: 0xaf87d7, 141: 0xaf87ff,
124: 0xaf0000, 125: 0xaf005f, 126: 0xaf0087, 127: 0xaf00af, 128: 0xaf00d7, 129: 0xaf00ff, 142: 0xafaf00, 143: 0xafaf5f, 144: 0xafaf87, 145: 0xafafaf, 146: 0xafafd7, 147: 0xafafff,
130: 0xaf5f00, 131: 0xaf5f5f, 132: 0xaf5f87, 133: 0xaf5faf, 134: 0xaf5fd7, 135: 0xaf5fff, 148: 0xafd700, 149: 0xafd75f, 150: 0xafd787, 151: 0xafd7af, 152: 0xafd7d7, 153: 0xafd7ff,
136: 0xaf8700, 137: 0xaf875f, 138: 0xaf8787, 139: 0xaf87af, 140: 0xaf87d7, 141: 0xaf87ff, 154: 0xafff00, 155: 0xafff5f, 156: 0xafff87, 157: 0xafffaf, 158: 0xafffd7, 159: 0xafffff,
142: 0xafaf00, 143: 0xafaf5f, 144: 0xafaf87, 145: 0xafafaf, 146: 0xafafd7, 147: 0xafafff, 160: 0xd70000, 161: 0xd7005f, 162: 0xd70087, 163: 0xd700af, 164: 0xd700d7, 165: 0xd700ff,
148: 0xafd700, 149: 0xafd75f, 150: 0xafd787, 151: 0xafd7af, 152: 0xafd7d7, 153: 0xafd7ff, 166: 0xd75f00, 167: 0xd75f5f, 168: 0xd75f87, 169: 0xd75faf, 170: 0xd75fd7, 171: 0xd75fff,
154: 0xafff00, 155: 0xafff5f, 156: 0xafff87, 157: 0xafffaf, 158: 0xafffd7, 159: 0xafffff, 172: 0xd78700, 173: 0xd7875f, 174: 0xd78787, 175: 0xd787af, 176: 0xd787d7, 177: 0xd787ff,
160: 0xd70000, 161: 0xd7005f, 162: 0xd70087, 163: 0xd700af, 164: 0xd700d7, 165: 0xd700ff, 178: 0xd7af00, 179: 0xd7af5f, 180: 0xd7af87, 181: 0xd7afaf, 182: 0xd7afd7, 183: 0xd7afff,
166: 0xd75f00, 167: 0xd75f5f, 168: 0xd75f87, 169: 0xd75faf, 170: 0xd75fd7, 171: 0xd75fff, 184: 0xd7d700, 185: 0xd7d75f, 186: 0xd7d787, 187: 0xd7d7af, 188: 0xd7d7d7, 189: 0xd7d7ff,
172: 0xd78700, 173: 0xd7875f, 174: 0xd78787, 175: 0xd787af, 176: 0xd787d7, 177: 0xd787ff, 190: 0xd7ff00, 191: 0xd7ff5f, 192: 0xd7ff87, 193: 0xd7ffaf, 194: 0xd7ffd7, 195: 0xd7ffff,
178: 0xd7af00, 179: 0xd7af5f, 180: 0xd7af87, 181: 0xd7afaf, 182: 0xd7afd7, 183: 0xd7afff, 196: 0xff0000, 197: 0xff005f, 198: 0xff0087, 199: 0xff00af, 200: 0xff00d7, 201: 0xff00ff,
184: 0xd7d700, 185: 0xd7d75f, 186: 0xd7d787, 187: 0xd7d7af, 188: 0xd7d7d7, 189: 0xd7d7ff, 202: 0xff5f00, 203: 0xff5f5f, 204: 0xff5f87, 205: 0xff5faf, 206: 0xff5fd7, 207: 0xff5fff,
190: 0xd7ff00, 191: 0xd7ff5f, 192: 0xd7ff87, 193: 0xd7ffaf, 194: 0xd7ffd7, 195: 0xd7ffff, 208: 0xff8700, 209: 0xff875f, 210: 0xff8787, 211: 0xff87af, 212: 0xff87d7, 213: 0xff87ff,
196: 0xff0000, 197: 0xff005f, 198: 0xff0087, 199: 0xff00af, 200: 0xff00d7, 201: 0xff00ff, 214: 0xffaf00, 215: 0xffaf5f, 216: 0xffaf87, 217: 0xffafaf, 218: 0xffafd7, 219: 0xffafff,
202: 0xff5f00, 203: 0xff5f5f, 204: 0xff5f87, 205: 0xff5faf, 206: 0xff5fd7, 207: 0xff5fff, 220: 0xffd700, 221: 0xffd75f, 222: 0xffd787, 223: 0xffd7af, 224: 0xffd7d7, 225: 0xffd7ff,
208: 0xff8700, 209: 0xff875f, 210: 0xff8787, 211: 0xff87af, 212: 0xff87d7, 213: 0xff87ff, 226: 0xffff00, 227: 0xffff5f, 228: 0xffff87, 229: 0xffffaf, 230: 0xffffd7, 231: 0xffffff,
214: 0xffaf00, 215: 0xffaf5f, 216: 0xffaf87, 217: 0xffafaf, 218: 0xffafd7, 219: 0xffafff, 232: 0x080808, 233: 0x121212, 234: 0x1c1c1c, 235: 0x262626, 236: 0x303030, 237: 0x3a3a3a,
220: 0xffd700, 221: 0xffd75f, 222: 0xffd787, 223: 0xffd7af, 224: 0xffd7d7, 225: 0xffd7ff, 238: 0x444444, 239: 0x4e4e4e, 240: 0x585858, 241: 0x626262, 242: 0x6c6c6c, 243: 0x767676,
226: 0xffff00, 227: 0xffff5f, 228: 0xffff87, 229: 0xffffaf, 230: 0xffffd7, 231: 0xffffff, 244: 0x808080, 245: 0x8a8a8a, 246: 0x949494, 247: 0x9e9e9e, 248: 0xa8a8a8, 249: 0xb2b2b2,
232: 0x080808, 233: 0x121212, 234: 0x1c1c1c, 235: 0x262626, 236: 0x303030, 237: 0x3a3a3a, 250: 0xbcbcbc, 251: 0xc6c6c6, 252: 0xd0d0d0, 253: 0xdadada, 254: 0xe4e4e4, 255: 0xeeeeee,
238: 0x444444, 239: 0x4e4e4e, 240: 0x585858, 241: 0x626262, 242: 0x6c6c6c, 243: 0x767676, }
244: 0x808080, 245: 0x8a8a8a, 246: 0x949494, 247: 0x9e9e9e, 248: 0xa8a8a8, 249: 0xb2b2b2,
250: 0xbcbcbc, 251: 0xc6c6c6, 252: 0xd0d0d0, 253: 0xdadada, 254: 0xe4e4e4, 255: 0xeeeeee,
}
if not cterm_color:
return None
try:
return color_dict[cterm_color]
except KeyError:
import sys
sys.stderr.write('Invalid cterm color index: {0}\n'.format(cterm_color))
return None

View File

@ -1,15 +1,21 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from lib.colors import cterm_to_hex
class Powerline(object):
ATTR_BOLD = 1
ATTR_ITALIC = 2
ATTR_UNDERLINE = 4
class Powerline:
dividers = { dividers = {
'l': { 'l': {
'hard': '', 'hard': u'',
'soft': '', 'soft': u'',
}, },
'r': { 'r': {
'hard': '', 'hard': u'',
'soft': '', 'soft': u'',
}, },
} }
@ -19,7 +25,8 @@ class Powerline:
Segments that have empty contents and aren't filler segments are Segments that have empty contents and aren't filler segments are
dropped from the segment array. dropped from the segment array.
''' '''
self.segments = [segment for segment in segments if segment.contents or segment.filler] self.segments = [segment for segment in segments if segment['contents'] or segment['filler']]
self._hl = {}
def render(self, renderer, width=None): def render(self, renderer, width=None):
'''Render all the segments with the specified renderer. '''Render all the segments with the specified renderer.
@ -34,7 +41,7 @@ class Powerline:
provided they will fill the remaining space until the desired width is provided they will fill the remaining space until the desired width is
reached. reached.
''' '''
def render_segments(segments, render_raw=True, render_highlighted=True): def render_segments(segments, render_highlighted=True):
'''Render a segment array. '''Render a segment array.
By default this function renders both raw (un-highlighted segments By default this function renders both raw (un-highlighted segments
@ -42,125 +49,114 @@ class Powerline:
rendering is used for calculating the total width for dropping rendering is used for calculating the total width for dropping
low-priority segments. low-priority segments.
''' '''
rendered_raw = '' rendered_highlighted = u''
rendered_highlighted = '' segments_len = len(segments)
empty_segment = mksegment()
for idx, segment in enumerate(segments): for idx, segment in enumerate(segments):
prev = segments[idx - 1] if idx > 0 else Segment() prev = segments[idx - 1] if idx > 0 else empty_segment
next = segments[idx + 1] if idx < len(segments) - 1 else Segment() next = segments[idx + 1] if idx < segments_len - 1 else empty_segment
compare_segment = next if segment.side == 'l' else prev segment['rendered_raw'] = u''
divider_type = 'soft' if compare_segment.bg == segment.bg else 'hard' compare = next if segment['side'] == 'l' else prev
divider = self.dividers[segment.side][divider_type] 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 segment.filler: 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 # Filler segments shouldn't be padded
segment_format = '{contents}' rendered_highlighted += segment['contents']
elif segment.draw_divider and (divider_type == 'hard' or segment.side == compare_segment.side): elif segment['draw_divider'] and (divider_type == 'hard' or segment['side'] == compare['side']):
# Draw divider if specified, and if the next segment is on # Draw divider if specified, and if the next segment is on
# the opposite side only draw the divider if it's a hard # the opposite side only draw the divider if it's a hard
# divider # divider
if segment.side == 'l': if segment['side'] == 'l':
segment_format = '{segment_hl}{outer_padding}{contents} {divider_hl}{divider} ' segment['rendered_raw'] += outer_padding + segment['contents'] + ' ' + divider + ' '
rendered_highlighted += segment_hl + outer_padding + segment['contents'] + ' ' + divider_hl + divider + ' '
else: else:
segment_format = ' {divider_hl}{divider}{segment_hl} {contents}{outer_padding}' segment['rendered_raw'] += ' ' + divider + ' ' + segment['contents'] + outer_padding
elif segment.contents: rendered_highlighted += ' ' + divider_hl + divider + segment_hl + ' ' + segment['contents'] + outer_padding
elif segment['contents']:
# Segments without divider # Segments without divider
segment_format = '{segment_hl}{contents}{outer_padding}' 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: else:
# Unknown segment type, skip it # Unknown segment type, skip it
continue continue
if render_raw is True and segment.filler is False: return rendered_highlighted
# Filler segments must be empty when used e.g. in vim (the
# %=%< segment which disappears), so they will be skipped
# when calculating the width using the raw rendering
rendered_raw += segment_format.format(
divider=divider,
contents=segment.contents,
divider_hl='',
segment_hl='',
outer_padding=' ' if idx == 0 or idx == len(segments) - 1 else '',
)
if render_highlighted is True: rendered_highlighted = render_segments(self.segments)
rendered_highlighted += segment_format.format(
divider=divider,
contents=segment.contents,
divider_hl='' if divider_type == 'soft' else renderer.hl(segment.bg, compare_segment.bg),
segment_hl=renderer.hl(segment.fg, segment.bg, segment.attr),
outer_padding=' ' if idx == 0 or idx == len(segments) - 1 else '',
)
return {
'highlighted': rendered_highlighted.decode('utf-8'),
'raw': rendered_raw.decode('utf-8'),
}
rendered = render_segments(self.segments)
if not width: if not width:
# No width specified, so we don't need to crop or pad anything # No width specified, so we don't need to crop or pad anything
return rendered['highlighted'] return rendered_highlighted
# Create an ordered list of segments that can be dropped # 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(self.segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0]
while len(rendered['raw']) > width and len(segments_priority): while self._total_len() > width and len(segments_priority):
self.segments.remove(segments_priority[0]) self.segments.remove(segments_priority[0])
segments_priority.pop(0) segments_priority.pop(0)
rendered = render_segments(self.segments, render_highlighted=False) # 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 # 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['filler'] is True]
if segments_fillers: if segments_fillers:
segments_fillers_len, segments_fillers_remainder = divmod((width - len(rendered['raw'])), len(segments_fillers)) segments_fillers_len, segments_fillers_remainder = divmod((width - self._total_len()), len(segments_fillers))
segments_fillers_contents = ' ' * segments_fillers_len segments_fillers_contents = ' ' * segments_fillers_len
for segment in segments_fillers: for segment in segments_fillers:
segment.contents = segments_fillers_contents segment['contents'] = segments_fillers_contents
# Add remainder whitespace to the first filler segment # Add remainder whitespace to the first filler segment
segments_fillers[0].contents += ' ' * segments_fillers_remainder segments_fillers[0]['contents'] += ' ' * segments_fillers_remainder
# Do a final render now that we have handled the cropping and padding return render_segments(self.segments)
rendered = render_segments(self.segments, render_raw=False)
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
class Segment: that the segments have been rendered using the render() method first.
ATTR_BOLD = 1
ATTR_ITALIC = 2
ATTR_UNDERLINE = 4
def __init__(self, contents=None, fg=False, bg=False, attr=False, side='l', draw_divider=True, priority=-1, filler=False):
'''Create a new Powerline segment.
''' '''
self.contents = str(contents or '') return len(''.join([segment['rendered_raw'] for segment in self.segments]))
self.fg = fg
self.bg = bg
self.attr = attr
self.side = side
self.draw_divider = draw_divider
self.priority = priority
self.filler = filler
if self.filler:
# Filler segments should never have any dividers
self.draw_divider = False
try: 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):
if len(self.fg) != 2: '''Convenience wrapper for segment generation.
raise TypeError '''
except TypeError: try:
# Only the terminal color is defined, so we need to get the hex color contents = unicode(contents or u'')
from lib.colors import cterm_to_hex except UnicodeDecodeError:
self.fg = [self.fg, cterm_to_hex(self.fg)] contents = contents.decode('utf-8') or u''
try: return {
if len(self.bg) != 2: 'contents': contents,
raise TypeError 'fg': (cterm_fg, hex_fg or cterm_to_hex.get(cterm_fg, 0xffffff)),
except TypeError: 'bg': (cterm_bg, hex_bg or cterm_to_hex.get(cterm_bg, 0x000000)),
# Only the terminal color is defined, so we need to get the hex color 'attr': attr,
from lib.colors import cterm_to_hex 'side': side,
self.bg = [self.bg, cterm_to_hex(self.bg)] 'draw_divider': False if filler else draw_divider,
'priority': priority,
'filler': filler,
}

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
from lib.core import Segment from lib.core import Powerline
from lib.renderers import SegmentRenderer from lib.renderers import SegmentRenderer
@ -32,7 +32,7 @@ class TerminalSegmentRenderer(SegmentRenderer):
if attr is False: if attr is False:
ansi += [22] ansi += [22]
else: else:
if attr & Segment.ATTR_BOLD: if attr & Powerline.ATTR_BOLD:
ansi += [1] ansi += [1]
return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) return '[{0}m'.format(';'.join(str(attr) for attr in ansi))

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
from lib.core import Segment from lib.core import Powerline
from lib.renderers import SegmentRenderer from lib.renderers import SegmentRenderer
@ -17,43 +17,44 @@ class VimSegmentRenderer(SegmentRenderer):
False, the argument is reset to the terminal defaults. If an argument 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. is a valid color or attribute, it's added to the vim highlight group.
''' '''
hl_group = {
'ctermfg': 'NONE',
'guifg': 'NONE',
'ctermbg': 'NONE',
'guibg': 'NONE',
'attr': ['NONE'],
}
# We don't need to explicitly reset attributes in vim, so skip those calls # We don't need to explicitly reset attributes in vim, so skip those calls
if not attr and not bg and not fg: if not attr and not bg and not fg:
return '' return ''
if fg is not None and fg is not False: if not (fg, bg, attr) in self.hl_groups:
hl_group['ctermfg'] = fg[0] hl_group = {
hl_group['guifg'] = fg[1] 'ctermfg': 'NONE',
'guifg': 'NONE',
'ctermbg': 'NONE',
'guibg': 'NONE',
'attr': ['NONE'],
'name': '',
}
if bg is not None and bg is not False: if fg is not None and fg is not False:
hl_group['ctermbg'] = bg[0] hl_group['ctermfg'] = fg[0]
hl_group['guibg'] = bg[1] hl_group['guifg'] = fg[1]
if attr is not None and attr is not False and attr != 0: if bg is not None and bg is not False:
hl_group['attr'] = [] hl_group['ctermbg'] = bg[0]
if attr & Segment.ATTR_BOLD: hl_group['guibg'] = bg[1]
hl_group['attr'].append('bold')
if attr & Segment.ATTR_ITALIC:
hl_group['attr'].append('italic')
if attr & Segment.ATTR_UNDERLINE:
hl_group['attr'].append('underline')
hl_group_name = 'Pl_{ctermfg}_{guifg}_{ctermbg}_{guibg}_{attr}'.format( if attr:
ctermfg=hl_group['ctermfg'], hl_group['attr'] = []
guifg=hl_group['guifg'], if attr & Powerline.ATTR_BOLD:
ctermbg=hl_group['ctermbg'], hl_group['attr'].append('bold')
guibg=hl_group['guibg'], if attr & Powerline.ATTR_ITALIC:
attr=''.join(attr[0] for attr in hl_group['attr']), hl_group['attr'].append('italic')
) if attr & Powerline.ATTR_UNDERLINE:
hl_group['attr'].append('underline')
self.hl_groups[hl_group_name] = hl_group hl_group['name'] = 'Pl_' + \
str(hl_group['ctermfg']) + '_' + \
str(hl_group['guifg']) + '_' + \
str(hl_group['ctermbg']) + '_' + \
str(hl_group['guibg']) + '_' + \
''.join(hl_group['attr'])
return '%#{0}#'.format(hl_group_name) self.hl_groups[(fg, bg, attr)] = hl_group
return '%#' + self.hl_groups[(fg, bg, attr)]['name'] + '#'