diff options
Diffstat (limited to 'powerline/renderers/vim.py')
-rw-r--r-- | powerline/renderers/vim.py | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py new file mode 100644 index 0000000..a92d51c --- /dev/null +++ b/powerline/renderers/vim.py @@ -0,0 +1,188 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys + +import vim + +from powerline.bindings.vim import vim_get_func, vim_getoption, environ, current_tabpage, get_vim_encoding +from powerline.renderer import Renderer +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE +from powerline.theme import Theme +from powerline.lib.unicode import unichr, register_strwidth_error + + +vim_mode = vim_get_func('mode', rettype='unicode') +if int(vim.eval('v:version')) >= 702: + _vim_mode = vim_mode + vim_mode = lambda: _vim_mode(1) + +mode_translations = { + unichr(ord('V') - 0x40): '^V', + unichr(ord('S') - 0x40): '^S', +} + + +class VimRenderer(Renderer): + '''Powerline vim segment renderer.''' + + character_translations = Renderer.character_translations.copy() + character_translations[ord('%')] = '%%' + + segment_info = Renderer.segment_info.copy() + segment_info.update(environ=environ) + + def __init__(self, *args, **kwargs): + if not hasattr(vim, 'strwidth'): + # Hope nobody want to change this at runtime + if vim.eval('&ambiwidth') == 'double': + kwargs = dict(**kwargs) + kwargs['ambigious'] = 2 + super(VimRenderer, self).__init__(*args, **kwargs) + self.hl_groups = {} + self.prev_highlight = None + self.strwidth_error_name = register_strwidth_error(self.strwidth) + self.encoding = get_vim_encoding() + + def shutdown(self): + self.theme.shutdown() + for match in self.local_themes.values(): + if 'theme' in match: + match['theme'].shutdown() + + def add_local_theme(self, matcher, theme): + if matcher in self.local_themes: + raise KeyError('There is already a local theme with given matcher') + self.local_themes[matcher] = theme + + def get_matched_theme(self, match): + try: + return match['theme'] + except KeyError: + match['theme'] = Theme(theme_config=match['config'], main_theme_config=self.theme_config, **self.theme_kwargs) + return match['theme'] + + def get_theme(self, matcher_info): + if matcher_info is None: + return self.get_matched_theme(self.local_themes[None]) + for matcher in self.local_themes.keys(): + if matcher and matcher(matcher_info): + return self.get_matched_theme(self.local_themes[matcher]) + else: + return self.theme + + if hasattr(vim, 'strwidth'): + if sys.version_info < (3,): + def strwidth(self, string): + # Does not work with tabs, but neither is strwidth from default + # renderer + return vim.strwidth(string.encode(self.encoding, 'replace')) + else: + @staticmethod + def strwidth(string): + return vim.strwidth(string) + + def get_segment_info(self, segment_info, mode): + return segment_info or self.segment_info + + def render(self, window=None, window_id=None, winnr=None, is_tabline=False): + '''Render all segments.''' + segment_info = self.segment_info.copy() + + if window is vim.current.window: + mode = vim_mode() + mode = mode_translations.get(mode, mode) + else: + mode = 'nc' + + segment_info.update( + window=window, + mode=mode, + window_id=window_id, + winnr=winnr, + buffer=window.buffer, + tabpage=current_tabpage(), + encoding=self.encoding, + ) + segment_info['tabnr'] = segment_info['tabpage'].number + segment_info['bufnr'] = segment_info['buffer'].number + if is_tabline: + winwidth = int(vim_getoption('columns')) + else: + winwidth = segment_info['window'].width + + statusline = super(VimRenderer, self).render( + mode=mode, + width=winwidth, + segment_info=segment_info, + matcher_info=(None if is_tabline else segment_info), + ) + statusline = statusline.encode(self.encoding, self.strwidth_error_name) + return statusline + + def reset_highlight(self): + self.hl_groups.clear() + + def hlstyle(self, fg=None, bg=None, attrs=None, **kwargs): + '''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. + ''' + # In order not to hit E541 two consequent identical highlighting + # specifiers may be squashed into one. + attrs = attrs or 0 # Normalize `attrs` + if (fg, bg, attrs) == self.prev_highlight: + return '' + self.prev_highlight = (fg, bg, attrs) + + # We don’t need to explicitly reset attributes in vim, so skip those + # calls + if not attrs and not bg and not fg: + return '' + + if not (fg, bg, attrs) in self.hl_groups: + hl_group = { + 'ctermfg': 'NONE', + 'guifg': None, + 'ctermbg': 'NONE', + 'guibg': None, + 'attrs': ['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 attrs: + hl_group['attrs'] = [] + if attrs & ATTR_BOLD: + hl_group['attrs'].append('bold') + if attrs & ATTR_ITALIC: + hl_group['attrs'].append('italic') + if attrs & ATTR_UNDERLINE: + hl_group['attrs'].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['attrs']) + ) + self.hl_groups[(fg, bg, attrs)] = hl_group + vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attrs} gui={attrs}'.format( + group=hl_group['name'], + ctermfg=hl_group['ctermfg'], + guifg='#{0:06x}'.format(hl_group['guifg']) if hl_group['guifg'] is not None else 'NONE', + ctermbg=hl_group['ctermbg'], + guibg='#{0:06x}'.format(hl_group['guibg']) if hl_group['guibg'] is not None else 'NONE', + attrs=','.join(hl_group['attrs']), + )) + return '%#' + self.hl_groups[(fg, bg, attrs)]['name'] + '#' + + +renderer = VimRenderer |