From 6c5316a05807fb91453fe107733b9c40011b2a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Tue, 20 Nov 2012 21:16:12 +0100 Subject: [PATCH 1/4] Make vim example work with split and inactive windows This commit introduces a bunch of changes. Most importantly, it ensures that split windows and inactive windows work as expected. This is accomplished with some clever workarounds for vim's statusline limitations. Vim statuslines evaluated using the %! statusline item are always evaluated in the buffer context and not in the statusline's owner window's context, and this made the previous implementation show the same statusline for all windows. The only way to ensure that the correct statusline is rendered to the correct window is to walk through all windows, and setting the statuslines with a reference to the current window number every time the current window is changed. This is accomplished by a set of BufEnter, BufLeave, etc. autocommands. The Syntastic segment has been temporarily removed due to errors when referencing the statusline function before Syntastic has been loaded. --- examples/vim/powerline.py | 156 ++++++++++++++++++++----------------- examples/vim/powerline.vim | 20 ++++- 2 files changed, 101 insertions(+), 75 deletions(-) diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py index 9da9973e..f4951744 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/powerline.py @@ -70,87 +70,103 @@ vim_funcs = { 'col': get_vim_func('col', rettype=int), 'expand': get_vim_func('expand'), 'tbcurtag': get_vim_func('tagbar#currenttag'), - 'sstlflag': get_vim_func('SyntasticStatuslineFlag'), '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 = '⭠ ' + 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) + '%', + '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']()] - try: - branch = vim_funcs['fghead'](5) - 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 = '' + if not current: + mode = None powerline = Powerline([ Segment(mode, 22, 148, attr=Segment.ATTR_BOLD), - Segment(vim.eval('&paste ? "PASTE" : ""'), 231, 166, attr=Segment.ATTR_BOLD), - Segment(branch, 250, 240, priority=10), - Segment(readonly, 196, 240, draw_divider=False), - Segment(filepath, 250, 240, draw_divider=False, priority=5), - Segment(filename, filename_color, 240, attr=Segment.ATTR_BOLD, draw_divider=not len(modified)), - Segment(modified, 220, 240, attr=Segment.ATTR_BOLD), - Segment(currenttag, 246, 236, draw_divider=False, priority=100), + Segment(windata['paste'], 231, 166, attr=Segment.ATTR_BOLD), + Segment(windata['branch'], 250, 240, priority=10), + Segment(windata['readonly'], 196, 240, draw_divider=False), + Segment(windata['filepath'], 250, 240, draw_divider=False, priority=5), + Segment(windata['filename'], windata['filename_color'], 240, attr=Segment.ATTR_BOLD, draw_divider=not len(windata['modified'])), + Segment(windata['modified'], 220, 240, attr=Segment.ATTR_BOLD), + Segment(windata['currenttag'], 246, 236, draw_divider=False, priority=100), Segment(filler=True, fg=236, bg=236), - Segment(syntastic, 214, 236, attr=Segment.ATTR_BOLD, draw_divider=False, priority=100), - Segment(filler=True, fg=236, bg=236), - Segment(vim.eval('&ff'), 247, 236, side='r', priority=50), - Segment(vim.eval('&fenc'), 247, 236, side='r', priority=50), - Segment(vim.eval('&ft'), 247, 236, side='r', priority=50), - Segment(str(line_percent).rjust(3) + '%', line_percent_color, 240, side='r', priority=30), + Segment(windata['fileformat'], 247, 236, side='r', priority=50), + Segment(windata['fileencoding'], 247, 236, side='r', priority=50), + Segment(windata['filetype'], 247, 236, side='r', priority=50), + Segment(windata['line_percent'], windata['line_percent_color'], 240, side='r', priority=30), Segment('⭡ ', 239, 252, side='r'), - 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), + Segment(windata['linecurrent'], 235, 252, attr=Segment.ATTR_BOLD, side='r', draw_divider=False), + Segment(windata['colcurrent'], 244, 252, side='r', priority=30, draw_divider=False), ]) renderer = VimSegmentRenderer() @@ -176,6 +192,4 @@ def statusline(): return stl -statusline() - # vim: ft=python ts=4 sts=4 sw=4 noet diff --git a/examples/vim/powerline.vim b/examples/vim/powerline.vim index 84cdefd5..4babdcb8 100644 --- a/examples/vim/powerline.vim +++ b/examples/vim/powerline.vim @@ -6,7 +6,7 @@ python sys.path.append(vim.eval('expand(":h:h:h")')) python from examples.vim.powerline import statusline if exists('*pyeval') - let s:pyeval=function('pyeval') + let s:pyeval = function('pyeval') else python import json function! s:pyeval(e) @@ -14,8 +14,20 @@ else endfunction endif -function! DynStl() - return s:pyeval('statusline()') +function! Powerline(winnr) + return s:pyeval('statusline('. a:winnr .')') 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 From f4e3d01d07ae4396ed09f1ce04f7434511a3c42b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 21 Nov 2012 09:57:16 +0100 Subject: [PATCH 2/4] Improve rendering performance This commit almost doubles the segment rendering performance. This is accomplished by caching a lot of data like highlighting groups, moving some calculations out of loops, and by performing less function calls overall. When a width is specified the main speed improvement comes from avoiding rendering the raw segments over and over until the statusline is short enough. Instead, the raw rendering is stored as a segment property and the combined length of all these renderings is used when removing low-priority segments instead. This results in a maximum of two rendering passes. Some "less pythonic" solutions have been chosen some places for performance reasons, e.g. joining strings instead of appending and joining lists. Overall this commit appears to make the performance equal or better than the legacy vimscript implementation. Later optimizations (in particular finding another method than remove() for removing low-priority segments) may make this version of Powerline far superior both in terms of functionality and performance. --- examples/vim/powerline.py | 6 +-- lib/colors.py | 96 +++++++++++++++------------------ lib/core.py | 110 ++++++++++++++++++++------------------ lib/renderers/vim.py | 63 +++++++++++----------- 4 files changed, 134 insertions(+), 141 deletions(-) diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py index f4951744..435392ee 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/powerline.py @@ -176,13 +176,13 @@ def statusline(winnr): stl = re.sub('(\w+)\%(?![-{()<=#*%])', '\\1%%', stl) # Create highlighting groups - for group, hl in renderer.hl_groups.items(): - if vim_funcs['hlexists'](group): + 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=group, + group=hl['name'], ctermfg=hl['ctermfg'], guifg='#{0:06x}'.format(hl['guifg']) if hl['guifg'] != 'NONE' else 'NONE', ctermbg=hl['ctermbg'], diff --git a/lib/colors.py b/lib/colors.py index fb128762..c0a34e45 100644 --- a/lib/colors.py +++ b/lib/colors.py @@ -1,54 +1,42 @@ -def cterm_to_hex(cterm_color): - '''Translate a cterm color index into the corresponding hex/RGB color. - ''' - color_dict = { - 16: 0x000000, 17: 0x00005f, 18: 0x000087, 19: 0x0000af, 20: 0x0000d7, 21: 0x0000ff, - 22: 0x005f00, 23: 0x005f5f, 24: 0x005f87, 25: 0x005faf, 26: 0x005fd7, 27: 0x005fff, - 28: 0x008700, 29: 0x00875f, 30: 0x008787, 31: 0x0087af, 32: 0x0087d7, 33: 0x0087ff, - 34: 0x00af00, 35: 0x00af5f, 36: 0x00af87, 37: 0x00afaf, 38: 0x00afd7, 39: 0x00afff, - 40: 0x00d700, 41: 0x00d75f, 42: 0x00d787, 43: 0x00d7af, 44: 0x00d7d7, 45: 0x00d7ff, - 46: 0x00ff00, 47: 0x00ff5f, 48: 0x00ff87, 49: 0x00ffaf, 50: 0x00ffd7, 51: 0x00ffff, - 52: 0x5f0000, 53: 0x5f005f, 54: 0x5f0087, 55: 0x5f00af, 56: 0x5f00d7, 57: 0x5f00ff, - 58: 0x5f5f00, 59: 0x5f5f5f, 60: 0x5f5f87, 61: 0x5f5faf, 62: 0x5f5fd7, 63: 0x5f5fff, - 64: 0x5f8700, 65: 0x5f875f, 66: 0x5f8787, 67: 0x5f87af, 68: 0x5f87d7, 69: 0x5f87ff, - 70: 0x5faf00, 71: 0x5faf5f, 72: 0x5faf87, 73: 0x5fafaf, 74: 0x5fafd7, 75: 0x5fafff, - 76: 0x5fd700, 77: 0x5fd75f, 78: 0x5fd787, 79: 0x5fd7af, 80: 0x5fd7d7, 81: 0x5fd7ff, - 82: 0x5fff00, 83: 0x5fff5f, 84: 0x5fff87, 85: 0x5fffaf, 86: 0x5fffd7, 87: 0x5fffff, - 88: 0x870000, 89: 0x87005f, 90: 0x870087, 91: 0x8700af, 92: 0x8700d7, 93: 0x8700ff, - 94: 0x875f00, 95: 0x875f5f, 96: 0x875f87, 97: 0x875faf, 98: 0x875fd7, 99: 0x875fff, - 100: 0x878700, 101: 0x87875f, 102: 0x878787, 103: 0x8787af, 104: 0x8787d7, 105: 0x8787ff, - 106: 0x87af00, 107: 0x87af5f, 108: 0x87af87, 109: 0x87afaf, 110: 0x87afd7, 111: 0x87afff, - 112: 0x87d700, 113: 0x87d75f, 114: 0x87d787, 115: 0x87d7af, 116: 0x87d7d7, 117: 0x87d7ff, - 118: 0x87ff00, 119: 0x87ff5f, 120: 0x87ff87, 121: 0x87ffaf, 122: 0x87ffd7, 123: 0x87ffff, - 124: 0xaf0000, 125: 0xaf005f, 126: 0xaf0087, 127: 0xaf00af, 128: 0xaf00d7, 129: 0xaf00ff, - 130: 0xaf5f00, 131: 0xaf5f5f, 132: 0xaf5f87, 133: 0xaf5faf, 134: 0xaf5fd7, 135: 0xaf5fff, - 136: 0xaf8700, 137: 0xaf875f, 138: 0xaf8787, 139: 0xaf87af, 140: 0xaf87d7, 141: 0xaf87ff, - 142: 0xafaf00, 143: 0xafaf5f, 144: 0xafaf87, 145: 0xafafaf, 146: 0xafafd7, 147: 0xafafff, - 148: 0xafd700, 149: 0xafd75f, 150: 0xafd787, 151: 0xafd7af, 152: 0xafd7d7, 153: 0xafd7ff, - 154: 0xafff00, 155: 0xafff5f, 156: 0xafff87, 157: 0xafffaf, 158: 0xafffd7, 159: 0xafffff, - 160: 0xd70000, 161: 0xd7005f, 162: 0xd70087, 163: 0xd700af, 164: 0xd700d7, 165: 0xd700ff, - 166: 0xd75f00, 167: 0xd75f5f, 168: 0xd75f87, 169: 0xd75faf, 170: 0xd75fd7, 171: 0xd75fff, - 172: 0xd78700, 173: 0xd7875f, 174: 0xd78787, 175: 0xd787af, 176: 0xd787d7, 177: 0xd787ff, - 178: 0xd7af00, 179: 0xd7af5f, 180: 0xd7af87, 181: 0xd7afaf, 182: 0xd7afd7, 183: 0xd7afff, - 184: 0xd7d700, 185: 0xd7d75f, 186: 0xd7d787, 187: 0xd7d7af, 188: 0xd7d7d7, 189: 0xd7d7ff, - 190: 0xd7ff00, 191: 0xd7ff5f, 192: 0xd7ff87, 193: 0xd7ffaf, 194: 0xd7ffd7, 195: 0xd7ffff, - 196: 0xff0000, 197: 0xff005f, 198: 0xff0087, 199: 0xff00af, 200: 0xff00d7, 201: 0xff00ff, - 202: 0xff5f00, 203: 0xff5f5f, 204: 0xff5f87, 205: 0xff5faf, 206: 0xff5fd7, 207: 0xff5fff, - 208: 0xff8700, 209: 0xff875f, 210: 0xff8787, 211: 0xff87af, 212: 0xff87d7, 213: 0xff87ff, - 214: 0xffaf00, 215: 0xffaf5f, 216: 0xffaf87, 217: 0xffafaf, 218: 0xffafd7, 219: 0xffafff, - 220: 0xffd700, 221: 0xffd75f, 222: 0xffd787, 223: 0xffd7af, 224: 0xffd7d7, 225: 0xffd7ff, - 226: 0xffff00, 227: 0xffff5f, 228: 0xffff87, 229: 0xffffaf, 230: 0xffffd7, 231: 0xffffff, - 232: 0x080808, 233: 0x121212, 234: 0x1c1c1c, 235: 0x262626, 236: 0x303030, 237: 0x3a3a3a, - 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 +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, + 28: 0x008700, 29: 0x00875f, 30: 0x008787, 31: 0x0087af, 32: 0x0087d7, 33: 0x0087ff, + 34: 0x00af00, 35: 0x00af5f, 36: 0x00af87, 37: 0x00afaf, 38: 0x00afd7, 39: 0x00afff, + 40: 0x00d700, 41: 0x00d75f, 42: 0x00d787, 43: 0x00d7af, 44: 0x00d7d7, 45: 0x00d7ff, + 46: 0x00ff00, 47: 0x00ff5f, 48: 0x00ff87, 49: 0x00ffaf, 50: 0x00ffd7, 51: 0x00ffff, + 52: 0x5f0000, 53: 0x5f005f, 54: 0x5f0087, 55: 0x5f00af, 56: 0x5f00d7, 57: 0x5f00ff, + 58: 0x5f5f00, 59: 0x5f5f5f, 60: 0x5f5f87, 61: 0x5f5faf, 62: 0x5f5fd7, 63: 0x5f5fff, + 64: 0x5f8700, 65: 0x5f875f, 66: 0x5f8787, 67: 0x5f87af, 68: 0x5f87d7, 69: 0x5f87ff, + 70: 0x5faf00, 71: 0x5faf5f, 72: 0x5faf87, 73: 0x5fafaf, 74: 0x5fafd7, 75: 0x5fafff, + 76: 0x5fd700, 77: 0x5fd75f, 78: 0x5fd787, 79: 0x5fd7af, 80: 0x5fd7d7, 81: 0x5fd7ff, + 82: 0x5fff00, 83: 0x5fff5f, 84: 0x5fff87, 85: 0x5fffaf, 86: 0x5fffd7, 87: 0x5fffff, + 88: 0x870000, 89: 0x87005f, 90: 0x870087, 91: 0x8700af, 92: 0x8700d7, 93: 0x8700ff, + 94: 0x875f00, 95: 0x875f5f, 96: 0x875f87, 97: 0x875faf, 98: 0x875fd7, 99: 0x875fff, + 100: 0x878700, 101: 0x87875f, 102: 0x878787, 103: 0x8787af, 104: 0x8787d7, 105: 0x8787ff, + 106: 0x87af00, 107: 0x87af5f, 108: 0x87af87, 109: 0x87afaf, 110: 0x87afd7, 111: 0x87afff, + 112: 0x87d700, 113: 0x87d75f, 114: 0x87d787, 115: 0x87d7af, 116: 0x87d7d7, 117: 0x87d7ff, + 118: 0x87ff00, 119: 0x87ff5f, 120: 0x87ff87, 121: 0x87ffaf, 122: 0x87ffd7, 123: 0x87ffff, + 124: 0xaf0000, 125: 0xaf005f, 126: 0xaf0087, 127: 0xaf00af, 128: 0xaf00d7, 129: 0xaf00ff, + 130: 0xaf5f00, 131: 0xaf5f5f, 132: 0xaf5f87, 133: 0xaf5faf, 134: 0xaf5fd7, 135: 0xaf5fff, + 136: 0xaf8700, 137: 0xaf875f, 138: 0xaf8787, 139: 0xaf87af, 140: 0xaf87d7, 141: 0xaf87ff, + 142: 0xafaf00, 143: 0xafaf5f, 144: 0xafaf87, 145: 0xafafaf, 146: 0xafafd7, 147: 0xafafff, + 148: 0xafd700, 149: 0xafd75f, 150: 0xafd787, 151: 0xafd7af, 152: 0xafd7d7, 153: 0xafd7ff, + 154: 0xafff00, 155: 0xafff5f, 156: 0xafff87, 157: 0xafffaf, 158: 0xafffd7, 159: 0xafffff, + 160: 0xd70000, 161: 0xd7005f, 162: 0xd70087, 163: 0xd700af, 164: 0xd700d7, 165: 0xd700ff, + 166: 0xd75f00, 167: 0xd75f5f, 168: 0xd75f87, 169: 0xd75faf, 170: 0xd75fd7, 171: 0xd75fff, + 172: 0xd78700, 173: 0xd7875f, 174: 0xd78787, 175: 0xd787af, 176: 0xd787d7, 177: 0xd787ff, + 178: 0xd7af00, 179: 0xd7af5f, 180: 0xd7af87, 181: 0xd7afaf, 182: 0xd7afd7, 183: 0xd7afff, + 184: 0xd7d700, 185: 0xd7d75f, 186: 0xd7d787, 187: 0xd7d7af, 188: 0xd7d7d7, 189: 0xd7d7ff, + 190: 0xd7ff00, 191: 0xd7ff5f, 192: 0xd7ff87, 193: 0xd7ffaf, 194: 0xd7ffd7, 195: 0xd7ffff, + 196: 0xff0000, 197: 0xff005f, 198: 0xff0087, 199: 0xff00af, 200: 0xff00d7, 201: 0xff00ff, + 202: 0xff5f00, 203: 0xff5f5f, 204: 0xff5f87, 205: 0xff5faf, 206: 0xff5fd7, 207: 0xff5fff, + 208: 0xff8700, 209: 0xff875f, 210: 0xff8787, 211: 0xff87af, 212: 0xff87d7, 213: 0xff87ff, + 214: 0xffaf00, 215: 0xffaf5f, 216: 0xffaf87, 217: 0xffafaf, 218: 0xffafd7, 219: 0xffafff, + 220: 0xffd700, 221: 0xffd75f, 222: 0xffd787, 223: 0xffd7af, 224: 0xffd7d7, 225: 0xffd7ff, + 226: 0xffff00, 227: 0xffff5f, 228: 0xffff87, 229: 0xffffaf, 230: 0xffffd7, 231: 0xffffff, + 232: 0x080808, 233: 0x121212, 234: 0x1c1c1c, 235: 0x262626, 236: 0x303030, 237: 0x3a3a3a, + 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, +} diff --git a/lib/core.py b/lib/core.py index 427ad172..25b57a91 100644 --- a/lib/core.py +++ b/lib/core.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- +from lib.colors import cterm_to_hex + class Powerline: dividers = { @@ -20,6 +22,7 @@ class Powerline: 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. @@ -34,7 +37,7 @@ class Powerline: provided they will fill the remaining space until the desired width is reached. ''' - def render_segments(segments, render_raw=True, render_highlighted=True): + def render_segments(segments, render_highlighted=True): '''Render a segment array. By default this function renders both raw (un-highlighted segments @@ -42,90 +45,94 @@ class Powerline: rendering is used for calculating the total width for dropping low-priority segments. ''' - rendered_raw = '' rendered_highlighted = '' + segments_len = len(segments) + empty_segment = Segment() for idx, segment in enumerate(segments): - prev = segments[idx - 1] if idx > 0 else Segment() - next = segments[idx + 1] if idx < len(segments) - 1 else Segment() + prev = segments[idx - 1] if idx > 0 else empty_segment + next = segments[idx + 1] if idx < segments_len - 1 else empty_segment - compare_segment = next if segment.side == 'l' else prev - divider_type = 'soft' if compare_segment.bg == segment.bg else 'hard' + 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 - segment_format = '{contents}' - elif segment.draw_divider and (divider_type == 'hard' or segment.side == compare_segment.side): + rendered_highlighted += segment.contents + elif segment.draw_divider and (divider_type == 'hard' or segment.side == compare.side): # Draw divider if specified, and if the next segment is on # the opposite side only draw the divider if it's a hard # divider 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: - segment_format = ' {divider_hl}{divider}{segment_hl} {contents}{outer_padding}' + 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 - 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: # Unknown segment type, skip it continue - if render_raw is True and segment.filler is False: - # 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 '', - ) + return rendered_highlighted.decode('utf-8') - if render_highlighted is True: - 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) + 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'] + 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 len(rendered['raw']) > width and len(segments_priority): + while self._total_len() > width and len(segments_priority): + # FIXME The remove method is quite expensive and we should find another way of removing low-priority segments self.segments.remove(segments_priority[0]) segments_priority.pop(0) - rendered = render_segments(self.segments, render_highlighted=False) - # 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 - 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 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 - # Do a final render now that we have handled the cropping and padding - rendered = render_segments(self.segments, render_raw=False) + return render_segments(self.segments) - 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]).decode('utf-8')) class Segment: @@ -144,23 +151,20 @@ class Segment: self.draw_divider = draw_divider self.priority = priority self.filler = filler + self.rendered_raw = '' if self.filler: # Filler segments should never have any dividers self.draw_divider = False try: - if len(self.fg) != 2: - raise TypeError + self.fg = (fg[0], fg[1]) except TypeError: # Only the terminal color is defined, so we need to get the hex color - from lib.colors import cterm_to_hex - self.fg = [self.fg, cterm_to_hex(self.fg)] + self.fg = (self.fg, cterm_to_hex.get(self.fg, 0xffffff)) try: - if len(self.bg) != 2: - raise TypeError + self.bg = (bg[0], bg[1]) except TypeError: # Only the terminal color is defined, so we need to get the hex color - from lib.colors import cterm_to_hex - self.bg = [self.bg, cterm_to_hex(self.bg)] + self.bg = (self.bg, cterm_to_hex.get(self.bg, 0x000000)) diff --git a/lib/renderers/vim.py b/lib/renderers/vim.py index 15f1ef9c..b91fd96c 100644 --- a/lib/renderers/vim.py +++ b/lib/renderers/vim.py @@ -17,43 +17,44 @@ class VimSegmentRenderer(SegmentRenderer): 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. ''' - 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 if not attr and not bg and not fg: return '' - if fg is not None and fg is not False: - hl_group['ctermfg'] = fg[0] - hl_group['guifg'] = fg[1] + if not (fg, bg, attr) in self.hl_groups: + hl_group = { + 'ctermfg': 'NONE', + 'guifg': 'NONE', + 'ctermbg': 'NONE', + 'guibg': 'NONE', + 'attr': ['NONE'], + 'name': '', + } - if bg is not None and bg is not False: - hl_group['ctermbg'] = bg[0] - hl_group['guibg'] = bg[1] + if fg is not None and fg is not False: + hl_group['ctermfg'] = fg[0] + hl_group['guifg'] = fg[1] - if attr is not None and attr is not False and attr != 0: - hl_group['attr'] = [] - if attr & Segment.ATTR_BOLD: - 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') + if bg is not None and bg is not False: + hl_group['ctermbg'] = bg[0] + hl_group['guibg'] = bg[1] - hl_group_name = 'Pl_{ctermfg}_{guifg}_{ctermbg}_{guibg}_{attr}'.format( - ctermfg=hl_group['ctermfg'], - guifg=hl_group['guifg'], - ctermbg=hl_group['ctermbg'], - guibg=hl_group['guibg'], - attr=''.join(attr[0] for attr in hl_group['attr']), - ) + if attr: + hl_group['attr'] = [] + if attr & Segment.ATTR_BOLD: + 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') - 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'] + '#' From 1d3c2590706ad770fd290aa94c98aa89ffac4d60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 21 Nov 2012 11:33:10 +0100 Subject: [PATCH 3/4] Improve rendering performance This change removes the Segment class as this takes forever to remove from the segment array when removing low-priority segments. It has instead been replaced by a wrapper function that works the same and returns a working dict of all segment properties. The regex substitution bottleneck in the vim example has been fixed by using a single-character percent placeholder in vim segments which is later replaced with a double percent using str.replace(). --- examples/terminal/powerline.py | 2 +- examples/vim/powerline.py | 43 ++++++------ lib/core.py | 121 +++++++++++++++------------------ lib/renderers/terminal.py | 4 +- lib/renderers/vim.py | 8 +-- 5 files changed, 85 insertions(+), 93 deletions(-) diff --git a/examples/terminal/powerline.py b/examples/terminal/powerline.py index 0e6ab9fc..d16cdb4f 100755 --- a/examples/terminal/powerline.py +++ b/examples/terminal/powerline.py @@ -5,7 +5,7 @@ import os 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.renderers import TerminalSegmentRenderer diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py index 435392ee..742f5a28 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/powerline.py @@ -4,7 +4,7 @@ import vim import os import re -from lib.core import Powerline, Segment +from lib.core import Powerline, mksegment from lib.renderers import VimSegmentRenderer modes = { @@ -28,6 +28,9 @@ modes = { '!': 'SHELL', } +# We need to replace this private use glyph with a double-percent later +percent_placeholder = ''.decode('utf-8') + if hasattr(vim, 'bindeval'): # This branch is used to avoid invoking vim parser as much as possible @@ -137,7 +140,7 @@ def statusline(winnr): 'fileformat': vim.eval('&ff'), 'fileencoding': vim.eval('&fenc'), 'filetype': vim.eval('&ft'), - 'line_percent': str(line_percent).rjust(3) + '%', + '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), @@ -151,29 +154,29 @@ def statusline(winnr): mode = None powerline = Powerline([ - Segment(mode, 22, 148, attr=Segment.ATTR_BOLD), - Segment(windata['paste'], 231, 166, attr=Segment.ATTR_BOLD), - Segment(windata['branch'], 250, 240, priority=10), - Segment(windata['readonly'], 196, 240, draw_divider=False), - Segment(windata['filepath'], 250, 240, draw_divider=False, priority=5), - Segment(windata['filename'], windata['filename_color'], 240, attr=Segment.ATTR_BOLD, draw_divider=not len(windata['modified'])), - Segment(windata['modified'], 220, 240, attr=Segment.ATTR_BOLD), - Segment(windata['currenttag'], 246, 236, draw_divider=False, priority=100), - Segment(filler=True, fg=236, bg=236), - Segment(windata['fileformat'], 247, 236, side='r', priority=50), - Segment(windata['fileencoding'], 247, 236, side='r', priority=50), - Segment(windata['filetype'], 247, 236, side='r', priority=50), - Segment(windata['line_percent'], windata['line_percent_color'], 240, side='r', priority=30), - Segment('⭡ ', 239, 252, side='r'), - Segment(windata['linecurrent'], 235, 252, attr=Segment.ATTR_BOLD, side='r', draw_divider=False), - Segment(windata['colcurrent'], 244, 252, side='r', priority=30, draw_divider=False), + mksegment(mode, 22, 148, attr=Powerline.ATTR_BOLD), + mksegment(windata['paste'], 231, 166, attr=Powerline.ATTR_BOLD), + mksegment(windata['branch'], 250, 240, priority=10), + mksegment(windata['readonly'], 196, 240, draw_divider=False), + mksegment(windata['filepath'], 250, 240, draw_divider=False, priority=5), + mksegment(windata['filename'], windata['filename_color'], 240, attr=Powerline.ATTR_BOLD, draw_divider=not len(windata['modified'])), + 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), + 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) - # Escape percent chars in the statusline, but only if they aren't part of any stl escape sequence - stl = re.sub('(\w+)\%(?![-{()<=#*%])', '\\1%%', stl) + # Replace percent placeholders + stl = stl.replace(percent_placeholder, '%%') # Create highlighting groups for idx, hl in renderer.hl_groups.items(): diff --git a/lib/core.py b/lib/core.py index 25b57a91..c0baf222 100644 --- a/lib/core.py +++ b/lib/core.py @@ -3,15 +3,19 @@ from lib.colors import cterm_to_hex -class Powerline: +class Powerline(object): + ATTR_BOLD = 1 + ATTR_ITALIC = 2 + ATTR_UNDERLINE = 4 + dividers = { 'l': { - 'hard': '⮀', - 'soft': '⮁', + 'hard': u'⮀', + 'soft': u'⮁', }, 'r': { - 'hard': '⮂', - 'soft': '⮃', + 'hard': u'⮂', + 'soft': u'⮃', }, } @@ -21,7 +25,7 @@ class 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.segments = [segment for segment in segments if segment['contents'] or segment['filler']] self._hl = {} def render(self, renderer, width=None): @@ -45,60 +49,60 @@ class Powerline: rendering is used for calculating the total width for dropping low-priority segments. ''' - rendered_highlighted = '' + rendered_highlighted = u'' segments_len = len(segments) - empty_segment = Segment() + 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 - compare = next if segment.side == 'l' else prev + 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_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) + 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) + 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: + if segment['filler']: # Filler segments shouldn't be padded - rendered_highlighted += segment.contents - elif segment.draw_divider and (divider_type == 'hard' or segment.side == compare.side): + rendered_highlighted += segment['contents'] + elif segment['draw_divider'] and (divider_type == 'hard' or segment['side'] == compare['side']): # Draw divider if specified, and if the next segment is on # the opposite side only draw the divider if it's a hard # divider - if segment.side == 'l': - segment.rendered_raw += outer_padding + segment.contents + ' ' + divider + ' ' - rendered_highlighted += segment_hl + outer_padding + segment.contents + ' ' + divider_hl + divider + ' ' + 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: + 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 + 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 + 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.decode('utf-8') + return rendered_highlighted rendered_highlighted = render_segments(self.segments) @@ -107,22 +111,21 @@ class Powerline: return rendered_highlighted # Create an ordered list of segments that can be dropped - segments_priority = [segment for segment in sorted(self.segments, key=lambda segment: segment.priority, reverse=True) if segment.priority > 0] + segments_priority = [segment for segment in sorted(self.segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0] while self._total_len() > width and len(segments_priority): - # FIXME The remove method is quite expensive and we should find another way of removing low-priority segments self.segments.remove(segments_priority[0]) segments_priority.pop(0) # 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: 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 + segment['contents'] = segments_fillers_contents # Add remainder whitespace to the first filler segment - segments_fillers[0].contents += ' ' * segments_fillers_remainder + segments_fillers[0]['contents'] += ' ' * segments_fillers_remainder return render_segments(self.segments) @@ -132,39 +135,25 @@ class Powerline: 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]).decode('utf-8')) + return len(''.join([segment['rendered_raw'] for segment in self.segments])) -class Segment: - ATTR_BOLD = 1 - ATTR_ITALIC = 2 - ATTR_UNDERLINE = 4 +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'' - 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 '') - self.fg = fg - self.bg = bg - self.attr = attr - self.side = side - self.draw_divider = draw_divider - self.priority = priority - self.filler = filler - self.rendered_raw = '' - - if self.filler: - # Filler segments should never have any dividers - self.draw_divider = False - - try: - self.fg = (fg[0], fg[1]) - except TypeError: - # Only the terminal color is defined, so we need to get the hex color - self.fg = (self.fg, cterm_to_hex.get(self.fg, 0xffffff)) - - try: - self.bg = (bg[0], bg[1]) - except TypeError: - # Only the terminal color is defined, so we need to get the hex color - self.bg = (self.bg, cterm_to_hex.get(self.bg, 0x000000)) + 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, + 'rendered_raw': u'', + } diff --git a/lib/renderers/terminal.py b/lib/renderers/terminal.py index b80c37e1..ff55dff2 100644 --- a/lib/renderers/terminal.py +++ b/lib/renderers/terminal.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from lib.core import Segment +from lib.core import Powerline from lib.renderers import SegmentRenderer @@ -32,7 +32,7 @@ class TerminalSegmentRenderer(SegmentRenderer): if attr is False: ansi += [22] else: - if attr & Segment.ATTR_BOLD: + if attr & Powerline.ATTR_BOLD: ansi += [1] return '[{0}m'.format(';'.join(str(attr) for attr in ansi)) diff --git a/lib/renderers/vim.py b/lib/renderers/vim.py index b91fd96c..07a4dbb2 100644 --- a/lib/renderers/vim.py +++ b/lib/renderers/vim.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from lib.core import Segment +from lib.core import Powerline from lib.renderers import SegmentRenderer @@ -41,11 +41,11 @@ class VimSegmentRenderer(SegmentRenderer): if attr: hl_group['attr'] = [] - if attr & Segment.ATTR_BOLD: + if attr & Powerline.ATTR_BOLD: hl_group['attr'].append('bold') - if attr & Segment.ATTR_ITALIC: + if attr & Powerline.ATTR_ITALIC: hl_group['attr'].append('italic') - if attr & Segment.ATTR_UNDERLINE: + if attr & Powerline.ATTR_UNDERLINE: hl_group['attr'].append('underline') hl_group['name'] = 'Pl_' + \ From addb7ccf7306e21a8b89e56617bd944abd0705b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Silkeb=C3=A6kken?= Date: Wed, 21 Nov 2012 12:21:01 +0100 Subject: [PATCH 4/4] Fix various rendering issues Another rendering pass is necessary before calculating the filler segment's lengths, because center segments may lose their separators after removing low-priority segments. Some unicode and variable assignment issues has been resolved so everything renders correctly. --- examples/vim/powerline.py | 9 ++++----- lib/core.py | 5 ++++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/vim/powerline.py b/examples/vim/powerline.py index 742f5a28..dda44632 100644 --- a/examples/vim/powerline.py +++ b/examples/vim/powerline.py @@ -2,7 +2,6 @@ import vim import os -import re from lib.core import Powerline, mksegment from lib.renderers import VimSegmentRenderer @@ -29,7 +28,7 @@ modes = { } # We need to replace this private use glyph with a double-percent later -percent_placeholder = ''.decode('utf-8') +percent_placeholder = u'' if hasattr(vim, 'bindeval'): # This branch is used to avoid invoking vim parser as much as possible @@ -100,7 +99,7 @@ def statusline(winnr): except TypeError: branch = '' if branch: - branch = '⭠ ' + branch + branch = u'⭠ ' + branch # Fun gradient colored percent segment line_percent_gradient = [160, 166, 172, 178, 184, 190] @@ -156,9 +155,9 @@ def statusline(winnr): 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=10), + mksegment(windata['branch'], 250, 240, priority=60), mksegment(windata['readonly'], 196, 240, draw_divider=False), - mksegment(windata['filepath'], 250, 240, draw_divider=False, priority=5), + mksegment(windata['filepath'], 250, 240, draw_divider=False, priority=40), mksegment(windata['filename'], windata['filename_color'], 240, attr=Powerline.ATTR_BOLD, draw_divider=not len(windata['modified'])), mksegment(windata['modified'], 220, 240, attr=Powerline.ATTR_BOLD), mksegment(windata['currenttag'], 246, 236, draw_divider=False, priority=100), diff --git a/lib/core.py b/lib/core.py index c0baf222..3d3e344d 100644 --- a/lib/core.py +++ b/lib/core.py @@ -57,6 +57,7 @@ class Powerline(object): 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' @@ -117,6 +118,9 @@ class Powerline(object): 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: @@ -155,5 +159,4 @@ def mksegment(contents=None, cterm_fg=False, cterm_bg=False, attr=False, hex_fg= 'draw_divider': False if filler else draw_divider, 'priority': priority, 'filler': filler, - 'rendered_raw': u'', }