From d638f1d6ea0482e6b5f194d5e2c91c59a2c6f0ff Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Feb 2013 22:10:09 +0400 Subject: [PATCH] Remove non-current window caching API changes done: - memoize additional_key function now accepts all function arguments - get_theme now receives matcher_info - render now receives segment_info and matcher_info, but segments and themes were removed - due to very different ways of obtaining column information col_current splitted back to col_current and virtcol_current. The former should be false in case of horizontal scrollbind (when &scrollopt contains hor) - added requires_segment_info decorator for convenience Other changes: - removed all vim function calls that were possible to remove - removed direct vim.eval calls --- powerline/bindings/vim/__init__.py | 2 + powerline/bindings/vim/plugin/powerline.vim | 2 +- .../config_files/themes/vim/default.json | 3 +- powerline/lib/memoize.py | 2 +- powerline/matchers/vim.py | 12 +- powerline/renderer.py | 13 +- powerline/renderers/vim.py | 21 ++- powerline/segments/vim.py | 156 ++++++++++++------ powerline/theme.py | 5 + 9 files changed, 137 insertions(+), 79 deletions(-) diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index 2291bb40..13af748c 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -46,3 +46,5 @@ except AttributeError: return r vim_get_func = VimFunc + +getbufvar = vim_get_func('getbufvar') diff --git a/powerline/bindings/vim/plugin/powerline.vim b/powerline/bindings/vim/plugin/powerline.vim index cb7fa829..8cf4c32c 100644 --- a/powerline/bindings/vim/plugin/powerline.vim +++ b/powerline/bindings/vim/plugin/powerline.vim @@ -36,7 +36,7 @@ catch finish endtry endtry -exec s:powerline_pycmd 'powerline = Powerline("vim")' +exec s:powerline_pycmd 'powerline = Powerline("vim", segment_info={})' if exists('*'. s:powerline_pyeval) let s:pyeval = function(s:powerline_pyeval) diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index 29fe36b4..ff206cf2 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -87,11 +87,12 @@ "align": "r" }, { - "name": "col_current", + "name": "virtcol_current", "draw_divider": false, "priority": 30, "before": ":", "width": 3, + "highlight_group": ["col_current"], "align": "l" } ] diff --git a/powerline/lib/memoize.py b/powerline/lib/memoize.py index 97cd1103..567ec6ac 100644 --- a/powerline/lib/memoize.py +++ b/powerline/lib/memoize.py @@ -16,7 +16,7 @@ class memoize(object): @wraps(func) def decorated_function(*args, **kwargs): if self.additional_key: - key = (func.__name__, args, tuple(kwargs.items()), self.additional_key()) + key = (func.__name__, args, tuple(kwargs.items()), self.additional_key(*args, **kwargs)) else: key = (func.__name__, args, tuple(kwargs.items())) cached = self._cache.get(key, None) diff --git a/powerline/matchers/vim.py b/powerline/matchers/vim.py index 7a971da6..195f5e40 100644 --- a/powerline/matchers/vim.py +++ b/powerline/matchers/vim.py @@ -2,12 +2,14 @@ from __future__ import absolute_import -import vim +import os +from powerline.bindings.vim import getbufvar -def help(): - return bool(int(vim.eval('&buftype is# "help"'))) +def help(matcher_info): + return getbufvar(matcher_info['bufnr'], '&buftype') == 'help' -def cmdwin(): - return bool(int(vim.eval('bufname("%") is# "[Command Line]"'))) +def cmdwin(matcher_info): + name = matcher_info['buffer'].name + return name and os.path.basename(name) == '[Command Line]' diff --git a/powerline/renderer.py b/powerline/renderer.py index 19f50d19..8c95721f 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -16,9 +16,9 @@ class Renderer(object): raise KeyError('There is already a local theme with given matcher') self.local_themes[matcher] = theme - def get_theme(self): + def get_theme(self, matcher_info): for matcher in self.local_themes.keys(): - if matcher(): + if matcher(matcher_info): match = self.local_themes[matcher] if 'config' in match: match['theme'] = Theme(theme_config=match.pop('config'), **self.theme_kwargs) @@ -26,7 +26,7 @@ class Renderer(object): else: return self.theme - def render(self, mode=None, width=None, theme=None, segments=None, side=None, output_raw=False): + def render(self, mode=None, width=None, side=None, output_raw=False, segment_info=None, matcher_info=None): '''Render all segments. When a width is provided, low-priority segments are dropped one at @@ -35,8 +35,11 @@ class Renderer(object): provided they will fill the remaining space until the desired width is reached. ''' - theme = theme or self.get_theme() - segments = segments or theme.get_segments(side) + theme = self.get_theme(matcher_info) + segments = theme.get_segments(side) + + if segment_info: + theme.segment_info.update(segment_info) # Handle excluded/included segments for the current mode segments = [segment for segment in segments\ diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 798adcf2..fabd535b 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -9,7 +9,6 @@ from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE 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') @@ -20,7 +19,6 @@ class VimRenderer(Renderer): def __init__(self, *args, **kwargs): super(VimRenderer, self).__init__(*args, **kwargs) self.hl_groups = {} - self.window_cache = {} def render(self, winnr, current): '''Render all segments. @@ -30,19 +28,20 @@ class VimRenderer(Renderer): used in non-current windows. ''' window_id = vim_getwinvar(winnr, 'window_id') - winwidth = vim_winwidth(winnr) if current: mode = vim_mode() - theme = self.get_theme() - segments = [segment for segment in theme.get_segments()] - self.window_cache[window_id] = (theme, segments) else: mode = 'nc' - theme, segments = self.window_cache.get(window_id, (None, [])) - for segment in segments: - segment['_space_left'] = 0 - segment['_space_right'] = 0 - statusline = super(VimRenderer, self).render(mode, winwidth, theme, segments) + segment_info = { + 'window': vim.windows[winnr - 1], + 'winnr': winnr, + 'mode': mode, + 'window_id': window_id, + } + segment_info['buffer'] = segment_info['window'].buffer + segment_info['bufnr'] = segment_info['buffer'].number + winwidth = segment_info['window'].width + statusline = super(VimRenderer, self).render(mode, winwidth, segment_info=segment_info, matcher_info=segment_info) return statusline def reset_highlight(self): diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 6ddc00f2..3490506c 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -8,17 +8,16 @@ try: except ImportError: vim = {} -from powerline.bindings.vim import vim_get_func +from powerline.bindings.vim import vim_get_func, getbufvar +from powerline.theme import requires_segment_info from powerline.lib import memoize, humanize_bytes from powerline.lib.vcs import guess vim_funcs = { - 'col': vim_get_func('col', rettype=int), 'virtcol': vim_get_func('virtcol', rettype=int), - 'expand': vim_get_func('expand'), - 'line': vim_get_func('line', rettype=int), - 'mode': vim_get_func('mode'), + 'fnamemodify': vim_get_func('fnamemodify'), 'getfsize': vim_get_func('getfsize', rettype=int), + 'bufnr': vim_get_func('bufnr', rettype=int), } vim_modes = { @@ -48,13 +47,38 @@ mode_translations = { } -def mode(override=None): +def bufnr(segment_info, *args, **kwargs): + '''Used for cache key, returns current buffer number''' + return segment_info['bufnr'] + + +# TODO Remove cache when needed +def window_cached(func): + cache = {} + + def ret(segment_info, *args, **kwargs): + window_id = segment_info['window_id'] + if segment_info['mode'] == 'nc': + return cache.get(window_id) + else: + r = func(*args, **kwargs) + cache[window_id] = r + return r + ret = requires_segment_info(ret) + ret.__name__ = func.__name__ + return ret + + +@requires_segment_info +def mode(segment_info, override=None): '''Return the current vim mode. :param dict override: dict for overriding default mode strings, e.g. ``{ 'n': 'NORM' }`` ''' - mode = vim_funcs['mode']().decode('utf-8') + mode = segment_info['mode'] + if mode == 'nc': + return None mode = mode_translations.get(mode, mode) if not override: return vim_modes[mode] @@ -64,48 +88,54 @@ def mode(override=None): return vim_modes[mode] -def modified_indicator(text=u'+'): +@requires_segment_info +def modified_indicator(segment_info, text=u'+'): '''Return a file modified indicator. :param string text: text to display if the current buffer is modified ''' - return text if int(vim.eval('&modified')) else None + return text if int(getbufvar(segment_info['bufnr'], '&modified')) else None -def paste_indicator(text='PASTE'): +@requires_segment_info +def paste_indicator(segment_info, text='PASTE'): '''Return a paste mode indicator. :param string text: text to display if paste mode is enabled ''' - return text if int(vim.eval('&paste')) else None + return text if int(getbufvar(segment_info['bufnr'], '&paste')) else None -def readonly_indicator(text=u''): +@requires_segment_info +def readonly_indicator(segment_info, text=u''): '''Return a read-only indicator. :param string text: text to display if the current buffer is read-only ''' - return text if int(vim.eval('&readonly')) else None + return text if int(getbufvar(segment_info['bufnr'], '&readonly')) else None -def file_directory(shorten_home=False): +@requires_segment_info +def file_directory(segment_info, shorten_home=False): '''Return file directory (head component of the file path). :param bool shorten_home: shorten all directories in :file:`/home/` to :file:`~user/` instead of :file:`/home/user/`. ''' - file_directory = vim_funcs['expand']('%:~:.:h') - if file_directory is None: + name = segment_info['buffer'].name + if not name: return None + file_directory = vim_funcs['fnamemodify'](name, ':~:.:h') if shorten_home and file_directory.startswith('/home/'): file_directory = '~' + file_directory[6:] return file_directory.decode('utf-8') + os.sep if file_directory else None -def file_name(display_no_file=False, no_file_text='[No file]'): +@requires_segment_info +def file_name(segment_info, display_no_file=False, no_file_text='[No file]'): '''Return file name (tail component of the file path). :param bool display_no_file: @@ -113,19 +143,22 @@ def file_name(display_no_file=False, no_file_text='[No file]'): :param str no_file_text: the string to display if the buffer is missing a file name ''' - file_name = vim_funcs['expand']('%:~:.:t') - if not file_name and not display_no_file: - return None - if not file_name: - return [{ - 'contents': no_file_text, - 'highlight_group': ['file_name_no_file', 'file_name'], - }] + name = segment_info['buffer'].name + if not name: + if display_no_file: + return [{ + 'contents': no_file_text, + 'highlight_group': ['file_name_no_file', 'file_name'], + }] + else: + return None + file_name = vim_funcs['fnamemodify'](name, ':~:.:t') return file_name.decode('utf-8') -@memoize(2) -def file_size(suffix='B', binary_prefix=False): +@requires_segment_info +@memoize(2, additional_key=bufnr) +def file_size(segment_info, suffix='B', binary_prefix=False): '''Return file size. :param str suffix: @@ -134,45 +167,49 @@ def file_size(suffix='B', binary_prefix=False): use binary prefix, e.g. MiB instead of MB :return: file size or None if the file isn't saved or if the size is too big to fit in a number ''' - file_name = vim_funcs['expand']('%') + file_name = segment_info['buffer'].name file_size = vim_funcs['getfsize'](file_name) if file_size < 0: return None return humanize_bytes(file_size, suffix, binary_prefix) -def file_format(): +@requires_segment_info +def file_format(segment_info): '''Return file format (i.e. line ending type). :return: file format or None if unknown or missing file format ''' - return vim.eval('&fileformat') or None + return getbufvar(segment_info['bufnr'], '&fileformat') or None -def file_encoding(): +@requires_segment_info +def file_encoding(segment_info): '''Return file encoding/character set. :return: file encoding/character set or None if unknown or missing file encoding ''' - return vim.eval('&fileencoding') or None + return getbufvar(segment_info['bufnr'], '&fileencoding') or None -def file_type(): +@requires_segment_info +def file_type(segment_info): '''Return file type. :return: file type or None if unknown file type ''' - return vim.eval('&filetype') or None + return getbufvar(segment_info['bufnr'], '&filetype') or None -def line_percent(gradient=False): +@requires_segment_info +def line_percent(segment_info, gradient=False): '''Return the cursor position in the file as a percentage. :param bool gradient: highlight the percentage with a color gradient (by default a green to red gradient) ''' - line_current = vim_funcs['line']('.') - line_last = vim_funcs['line']('$') + line_current = segment_info['window'].cursor[0] + line_last = len(segment_info['buffer']) percentage = int(line_current * 100 // line_last) if not gradient: return percentage @@ -182,18 +219,23 @@ def line_percent(gradient=False): }] -def line_current(): +@requires_segment_info +def line_current(segment_info): '''Return the current cursor line.''' - return vim_funcs['line']('.') + return segment_info['window'].cursor[0] -def col_current(virtcol=True): +@requires_segment_info +def col_current(segment_info): '''Return the current cursor column. - - :param bool virtcol: - return visual column with concealed characters ingored ''' - return vim_funcs['virtcol' if virtcol else 'col']('.') + return segment_info['window'].cursor[1] + 1 + + +@window_cached +def virtcol_current(): + '''Return current visual column with concealed characters ingored''' + return vim_funcs['virtcol']('.') def modified_buffers(text=u'+', join_str=','): @@ -204,30 +246,33 @@ def modified_buffers(text=u'+', join_str=','): :param str join_str: string to use for joining the modified buffer list ''' - buffer_len = int(vim.eval('bufnr("$")')) - buffer_mod = [str(bufnr) for bufnr in range(1, buffer_len + 1) if vim.eval('getbufvar({0}, "&mod")'.format(bufnr)) == '1'] + buffer_len = vim_funcs['bufnr']('$') + buffer_mod = [str(bufnr) for bufnr in range(1, buffer_len + 1) if int(getbufvar(bufnr, '&modified'))] if buffer_mod: return u'{0} {1}'.format(text, join_str.join(buffer_mod)) return None +@requires_segment_info @memoize(2) -def branch(): +def branch(segment_info): '''Return the current working branch.''' - repo = guess(os.path.abspath(vim.current.buffer.name or os.getcwd())) + repo = guess(os.path.abspath(segment_info['buffer'].name or os.getcwd())) if repo: return repo.branch() return None # TODO Drop cache on BufWrite event -@memoize(2, additional_key=lambda: vim.current.buffer.number) -def file_vcs_status(): +@requires_segment_info +@memoize(2, additional_key=bufnr) +def file_vcs_status(segment_info): '''Return the VCS status for this buffer.''' - if vim.current.buffer.name and not vim.eval('&buftype'): - repo = guess(os.path.abspath(vim.current.buffer.name)) + name = segment_info['buffer'].name + if name and not getbufvar(segment_info['bufnr'], '&buftype'): + repo = guess(os.path.abspath(name)) if repo: - status = repo.status(os.path.relpath(vim.current.buffer.name, repo.directory)) + status = repo.status(os.path.relpath(name, repo.directory)) if not status: return None status = status.strip() @@ -242,10 +287,11 @@ def file_vcs_status(): return None +@requires_segment_info @memoize(2) -def repository_status(): +def repository_status(segment_info): '''Return the status for the current repo.''' - repo = guess(os.path.abspath(vim.current.buffer.name or os.getcwd())) + repo = guess(os.path.abspath(segment_info['buffer'].name or os.getcwd())) if repo: return repo.status() return None diff --git a/powerline/theme.py b/powerline/theme.py index 542b635a..a441e55a 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -11,6 +11,11 @@ except NameError: unicode = str +def requires_segment_info(func): + func.requires_powerline_segment_info = True + return func + + class Theme(object): def __init__(self, ext, colorscheme, theme_config, common_config, segment_info=None): self.colorscheme = colorscheme