diff --git a/powerline/bindings/vim/__init__.py b/powerline/bindings/vim/__init__.py index e6edb92a..eec4da3c 100644 --- a/powerline/bindings/vim/__init__.py +++ b/powerline/bindings/vim/__init__.py @@ -136,6 +136,12 @@ else: if hasattr(vim, 'tabpages'): current_tabpage = lambda: vim.current.tabpage list_tabpages = lambda: vim.tabpages + + def list_tabpage_buffers_segment_info(segment_info): + return ( + {'buffer': window.buffer, 'bufnr': window.buffer.number} + for window in segment_info['tabpage'].windows + ) else: class FalseObject(object): @staticmethod @@ -205,6 +211,24 @@ else: def list_tabpages(): # NOQA return [Tabpage(nr) for nr in range(1, _last_tab_nr() + 1)] + class TabBufSegmentInfo(dict): + def __getitem__(self, key): + try: + return super(TabBufSegmentInfo, self).__getitem__(key) + except KeyError: + if key != 'buffer': + raise + else: + buffer = get_buffer(super(TabBufSegmentInfo, self).__getitem__('bufnr')) + self['buffer'] = buffer + return buffer + + def list_tabpage_buffers_segment_info(segment_info): + return ( + TabBufSegmentInfo(bufnr=int(bufnrstr)) + for bufnrstr in vim.eval('tabpagebuflist({0})'.format(segment_info['tabnr'])) + ) + class VimEnviron(object): @staticmethod diff --git a/powerline/config_files/themes/vim/tabline.json b/powerline/config_files/themes/vim/tabline.json index 42aed081..79b145fa 100644 --- a/powerline/config_files/themes/vim/tabline.json +++ b/powerline/config_files/themes/vim/tabline.json @@ -30,6 +30,11 @@ }, "priority": 10 }, + { + "name": "tab_modified_indicator", + "exclude_modes": ["buf", "buf_nc"], + "priority": 5 + }, { "name": "modified_indicator", "exclude_modes": ["tab", "tab_nc"], diff --git a/powerline/renderer.py b/powerline/renderer.py index a2a9d32d..4ac0a9db 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -147,10 +147,17 @@ class Renderer(object): ''' self.theme.shutdown() - def _get_highlighting(self, segment, mode): - segment['highlight'] = self.colorscheme.get_highlighting(segment['highlight_group'], mode, segment.get('gradient_level')) + def _set_highlighting(self, segment): + segment['highlight'] = self.colorscheme.get_highlighting( + segment['highlight_group'], + segment['mode'], + segment.get('gradient_level') + ) if segment['divider_highlight_group']: - segment['divider_highlight'] = self.colorscheme.get_highlighting(segment['divider_highlight_group'], mode) + segment['divider_highlight'] = self.colorscheme.get_highlighting( + segment['divider_highlight_group'], + segment['mode'] + ) else: segment['divider_highlight'] = None return segment @@ -254,19 +261,10 @@ class Renderer(object): def do_render(self, mode, width, side, line, output_raw, output_width, segment_info, theme): '''Like Renderer.render(), but accept theme in place of matcher_info ''' - segments = theme.get_segments(side, line, self.get_segment_info(segment_info, mode)) - # Handle excluded/included segments for the current mode segments = [ - self._get_highlighting(segment, segment_mode) - for segment, segment_mode in ( - (segment, segment['mode'] or mode) - for segment in segments - ) if ( - segment_mode not in segment['exclude_modes'] - and ( - not segment['include_modes'] - or segment_mode in segment['include_modes'] - ) + self._set_highlighting(segment) + for segment in ( + theme.get_segments(side, line, self.get_segment_info(segment_info, mode), mode) ) ] diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 50586194..28cf0ee4 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -84,36 +84,33 @@ class VimRenderer(Renderer): 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): + 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 not None: - if window is vim.current.window: - mode = vim_mode(1) - 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(), - ) - segment_info['tabnr'] = segment_info['tabpage'].number - segment_info['bufnr'] = segment_info['buffer'].number - winwidth = segment_info['window'].width - matcher_info = segment_info + + if window is vim.current.window: + mode = vim_mode(1) + mode = mode_translations.get(mode, mode) else: - mode = None - winwidth = int(vim.eval('&columns')) - matcher_info = None + mode = 'nc' + + segment_info.update( + window=window, + mode=mode, + window_id=window_id, + winnr=winnr, + buffer=window.buffer, + tabpage=current_tabpage(), + ) + segment_info['tabnr'] = segment_info['tabpage'].number + segment_info['bufnr'] = segment_info['buffer'].number + winwidth = segment_info['window'].width + statusline = super(VimRenderer, self).render( mode=mode, width=winwidth, segment_info=segment_info, - matcher_info=matcher_info, + matcher_info=(None if is_tabline else segment_info), ) return statusline diff --git a/powerline/segment.py b/powerline/segment.py index b2a7086c..d75be664 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -1,8 +1,6 @@ # vim:fileencoding=utf-8:noet from __future__ import absolute_import, unicode_literals, division, print_function -import sys - from powerline.lib.watcher import create_file_watcher @@ -90,7 +88,7 @@ def get_attr_func(contents_func, key, args): return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **args) -def process_segment_lister(pl, segment_info, parsed_segments, side, lister, subsegments, patcher_args): +def process_segment_lister(pl, segment_info, parsed_segments, side, mode, lister, subsegments, patcher_args): for subsegment_info, subsegment_update in lister(pl=pl, segment_info=segment_info, **patcher_args): draw_inner_divider = subsegment_update.pop('draw_inner_divider', False) old_pslen = len(parsed_segments) @@ -100,7 +98,18 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, lister, subs subsegment.update(subsegment_update) if subsegment_update['priority_multiplier'] and subsegment['priority']: subsegment['priority'] *= subsegment_update['priority_multiplier'] - process_segment(pl, side, subsegment_info, parsed_segments, subsegment) + + subsegment_mode = subsegment_update.get('mode') + if subsegment_mode and ( + subsegment_mode in subsegment['exclude_modes'] + or ( + subsegment['include_modes'] + and subsegment_mode not in subsegment['include_modes'] + ) + ): + continue + + process_segment(pl, side, subsegment_info, parsed_segments, subsegment, subsegment_mode or mode) new_pslen = len(parsed_segments) if new_pslen > old_pslen + 1 and draw_inner_divider is not None: for i in range(old_pslen, new_pslen - 1) if side == 'left' else range(old_pslen + 1, new_pslen): @@ -108,22 +117,27 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, lister, subs return None -def process_segment(pl, side, segment_info, parsed_segments, segment): +def process_segment(pl, side, segment_info, parsed_segments, segment, mode): + segment = segment.copy() + segment['mode'] = mode if segment['type'] in ('function', 'segment_list'): pl.prefix = segment['name'] try: if segment['type'] == 'function': contents = segment['contents_func'](pl, segment_info) else: - contents = segment['contents_func'](pl, segment_info, parsed_segments, side) + contents = segment['contents_func'](pl, segment_info, parsed_segments, side, mode) except Exception as e: pl.exception('Exception while computing segment: {0}', str(e)) return if contents is None: return + if isinstance(contents, list): - segment_base = segment.copy() + # Needs copying here, but it was performed at the very start of the + # function + segment_base = segment if contents: draw_divider_position = -1 if side == 'left' else 0 for key, i, newval in ( @@ -206,8 +220,8 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, ge 'divider_highlight_group': None, 'before': None, 'after': None, - 'contents_func': lambda pl, segment_info, parsed_segments, side: process_segment_lister( - pl, segment_info, parsed_segments, side, + 'contents_func': lambda pl, segment_info, parsed_segments, side, mode: process_segment_lister( + pl, segment_info, parsed_segments, side, mode, patcher_args=args, subsegments=subsegments, lister=_contents_func, diff --git a/powerline/segments/vim/__init__.py b/powerline/segments/vim/__init__.py index 3e991e4f..65118b2f 100644 --- a/powerline/segments/vim/__init__.py +++ b/powerline/segments/vim/__init__.py @@ -13,9 +13,10 @@ except ImportError: from collections import defaultdict from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, - buffer_name, vim_getwinvar, - register_buffer_cache, current_tabpage, - list_tabpages) + buffer_name, vim_getwinvar, + register_buffer_cache, current_tabpage, + list_tabpages, + list_tabpage_buffers_segment_info) from powerline.theme import requires_segment_info, requires_filesystem_watcher from powerline.lib import add_divider_highlight_group from powerline.lib.vcs import guess, tree_status @@ -167,6 +168,24 @@ def modified_indicator(pl, segment_info, text='+'): return text if int(vim_getbufoption(segment_info, 'modified')) else None +@requires_segment_info +def tab_modified_indicator(pl, segment_info, text='+'): + '''Return a file modified indicator for tabpages. + + :param string text: + text to display if any buffer in the current tab is modified + + Highlight groups used: ``tab_modified_indicator`` or ``modified_indicator``. + ''' + for buf_segment_info in list_tabpage_buffers_segment_info(segment_info): + if int(vim_getbufoption(buf_segment_info, 'modified')): + return [{ + 'contents': text, + 'highlight_group': ['tab_modified_indicator', 'modified_indicator'], + }] + return None + + @requires_segment_info def paste_indicator(pl, segment_info, text='PASTE'): '''Return a paste mode indicator. @@ -427,7 +446,6 @@ def col_current(pl, segment_info): return str(segment_info['window'].cursor[1] + 1) -# TODO Add &textwidth-based gradient @window_cached def virtcol_current(pl, gradient=True): '''Return current visual column with concealed characters ingored diff --git a/powerline/theme.py b/powerline/theme.py index d2617dc5..fe494457 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -90,7 +90,7 @@ class Theme(object): def get_line_number(self): return len(self.segments) - def get_segments(self, side=None, line=0, segment_info=None): + def get_segments(self, side=None, line=0, segment_info=None, mode=None): '''Return all segments. Function segments are called, and all segments get their before/after @@ -103,7 +103,11 @@ class Theme(object): for side in [side] if side else ['left', 'right']: parsed_segments = [] for segment in self.segments[line][side]: - process_segment(self.pl, side, segment_info, parsed_segments, segment) + # No segment-local modes at this point + if mode not in segment['exclude_modes'] and ( + not segment['include_modes'] or mode in segment['include_modes'] + ): + process_segment(self.pl, side, segment_info, parsed_segments, segment, mode) for segment in parsed_segments: segment['contents'] = segment['before'] + u(segment['contents'] if segment['contents'] is not None else '') + segment['after'] # Align segment contents diff --git a/powerline/vim.py b/powerline/vim.py index 0a5fc9f9..ecea537b 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -4,7 +4,7 @@ from __future__ import absolute_import import sys from powerline.bindings.vim import vim_get_func, vim_getvar -from powerline import Powerline +from powerline import Powerline, FailedUnicode from powerline.lib import mergedicts import vim from itertools import count @@ -228,15 +228,14 @@ class VimPowerline(Powerline): def statusline(self, window_id): window, window_id, winnr = self.win_idx(window_id) or (None, None, None) if not window: - return 'No window {0}'.format(window_id) + return FailedUnicode('No window {0}'.format(window_id)) return self.render(window, window_id, winnr) def tabline(self): - return self.render() + return self.render(*self.win_idx(None), is_tabline=True) def new_window(self): - window, window_id, winnr = self.win_idx(None) - return self.render(window, window_id, winnr) + return self.render(*self.win_idx(None)) if not hasattr(vim, 'bindeval'): # Method for PowerlinePyeval function. Is here to reduce the number of diff --git a/tests/test_provided_config_files.py b/tests/test_provided_config_files.py index 3bd41d94..27698cde 100644 --- a/tests/test_provided_config_files.py +++ b/tests/test_provided_config_files.py @@ -53,7 +53,7 @@ class TestConfig(TestCase): outputs[out] = (i, (args, kwargs), mode) with vim_module._with('bufname', '/tmp/foo.txt'): - out = powerline.render() + out = powerline.render(vim_module.current.window, 1, vim_module.current.window.number, is_tabline=True) outputs[out] = (-1, (None, None), 'tab') with vim_module._with('globals', powerline_config_path=cfg_path): exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!')) diff --git a/tests/test_segments.py b/tests/test_segments.py index 60d05574..2acccb46 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -903,6 +903,29 @@ class TestVim(TestCase): self.assertEqual(single_tab(multiple_text='m'), [{'highlight_group': ['single_tab'], 'contents': 'Bufs'}]) self.assertEqual(single_tab(single_text='s'), [{'highlight_group': ['single_tab'], 'contents': 's'}]) + def test_segment_info(self): + pl = Pl() + with vim_module._with('tabpage'): + with vim_module._with('buffer', '1') as segment_info: + self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), None) + vim_module.current.buffer[0] = ' ' + self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), [{ + 'contents': '+', + 'highlight_group': ['tab_modified_indicator', 'modified_indicator'], + }]) + vim_module._undo() + self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), None) + old_buffer = vim_module.current.buffer + vim_module._new('2') + segment_info = vim_module._get_segment_info() + self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), None) + old_buffer[0] = ' ' + self.assertEqual(vim.modified_indicator(pl=pl, segment_info=segment_info), None) + self.assertEqual(vim.tab_modified_indicator(pl=pl, segment_info=segment_info), [{ + 'contents': '+', + 'highlight_group': ['tab_modified_indicator', 'modified_indicator'], + }]) + old_cwd = None