From 97978eaf77efdcaeaeae9ee9a2351c29750c4537 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 22:18:28 +0400 Subject: [PATCH 1/4] Improve visual_range segment --- .../config_files/themes/vim/default.json | 2 +- powerline/segments/vim.py | 71 +++++++++++++------ 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/powerline/config_files/themes/vim/default.json b/powerline/config_files/themes/vim/default.json index d80010df..bcd65016 100644 --- a/powerline/config_files/themes/vim/default.json +++ b/powerline/config_files/themes/vim/default.json @@ -22,7 +22,7 @@ }, { "name": "visual_range", - "exclude_modes": ["nc"], + "include_modes": ["v", "V", "^V", "s", "S", "^S"], "priority": 10 }, { diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 077bd67e..d2ff9dcf 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -74,7 +74,10 @@ def window_cached(func): if segment_info['mode'] == 'nc': return cache.get(window_id) else: - r = func(**kwargs) + if getattr(func, 'powerline_requires_segment_info', False): + r = func(segment_info=segment_info, **kwargs) + else: + r = func(**kwargs) cache[window_id] = r return r @@ -99,29 +102,57 @@ def mode(pl, segment_info, override=None): return vim_modes[mode] +@window_cached @requires_segment_info -def visual_range(pl, segment_info): +def visual_range(pl, segment_info, CTRL_V_text='{rows} × {vcols}', v_text_oneline='{vcols} cols', v_text_multiline='{rows} rows', V_text='{rows} rows'): '''Return the current visual selection range. - Returns a value similar to `showcmd`. + :param str CTRL_V_text: + Text to display when in block visual or select mode. + :param str v_text_oneline: + Text to display when in charaterwise visual or select mode, assuming + selection occupies only one line. + :param str v_text_multiline: + Text to display when in charaterwise visual or select mode, assuming + selection occupies more then one line. + :param str V_text: + Text to display when in linewise visual or select mode. + + All texts are format strings which are passed the following parameters: + + ========= ============================================================= + Parameter Description + ========= ============================================================= + sline Line number of the first line of the selection + eline Line number of the last line of the selection + scol Column number of the first character of the selection + ecol Column number of the last character of the selection + svcol Virtual column number of the first character of the selection + secol Virtual column number of the last character of the selection + rows Number of lines in the selection + cols Number of columns in the selection + vcols Number of virtual columns in the selection + ========= ============================================================= ''' - if segment_info['mode'] not in ('v', 'V', '^V'): - return None - pos_start = vim_funcs['getpos']('v') - pos_end = vim_funcs['getpos']('.') - # Workaround for vim's "excellent" handling of multibyte characters and display widths - pos_start[2] = vim_funcs['virtcol']([pos_start[1], pos_start[2], pos_start[3]]) - pos_end[2] = vim_funcs['virtcol']([pos_end[1], pos_end[2], pos_end[3]]) - visual_start = (int(pos_start[1]), int(pos_start[2])) - visual_end = (int(pos_end[1]), int(pos_end[2])) - diff_rows = abs(visual_end[0] - visual_start[0]) + 1 - diff_cols = abs(visual_end[1] - visual_start[1]) + 1 - if segment_info['mode'] == '^V': - return '{0} × {1}'.format(diff_rows, diff_cols) - elif segment_info['mode'] == 'V' or diff_rows > 1: - return '{0} rows'.format(diff_rows) - else: - return '{0} cols'.format(diff_cols) + sline, scol, soff = vim_funcs['getpos']("v")[1:] + eline, ecol, eoff = vim_funcs['getpos'](".")[1:] + svcol = vim_funcs['virtcol']([sline, scol, soff]) + evcol = vim_funcs['virtcol']([eline, ecol, eoff]) + rows = abs(eline - sline) + 1 + cols = abs(ecol - scol) + 1 + vcols = abs(evcol - svcol) + 1 + return { + '^': CTRL_V_text, + 's': v_text_oneline if rows == 1 else v_text_multiline, + 'S': V_text, + 'v': v_text_oneline if rows == 1 else v_text_multiline, + 'V': V_text, + }.get(segment_info['mode'][0], '').format( + sline=sline, eline=eline, + scol=scol, ecol=ecol, + svcol=svcol, evcol=evcol, + rows=rows, cols=cols, vcols=vcols, + ) @requires_segment_info From 702f43858b4f33bbc79617718f7c089e54ebf8a8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 22:44:29 +0400 Subject: [PATCH 2/4] =?UTF-8?q?Do=20not=20use=20=E2=80=9Crows=E2=80=9D=20a?= =?UTF-8?q?nd=20=E2=80=9Ccols=E2=80=9D=20in=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reason: string “1 rows” is incorrect and I do not want to implement proper number handling (*proper* handling is *not* limited to plural/singular form like in English). --- powerline/segments/vim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index d2ff9dcf..57572c1b 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -104,7 +104,7 @@ def mode(pl, segment_info, override=None): @window_cached @requires_segment_info -def visual_range(pl, segment_info, CTRL_V_text='{rows} × {vcols}', v_text_oneline='{vcols} cols', v_text_multiline='{rows} rows', V_text='{rows} rows'): +def visual_range(pl, segment_info, CTRL_V_text='{rows} × {vcols}', v_text_oneline='C:{vcols}', v_text_multiline='L:{rows}', V_text='L:{rows}'): '''Return the current visual selection range. :param str CTRL_V_text: From 117661e18675886f6226b10f865a75f3fe2862af Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 22:49:53 +0400 Subject: [PATCH 3/4] Add tests for visual_range --- tests/test_segments.py | 46 ++++++++++++++++++++++++++++++++++++++++-- tests/vim.py | 30 +++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/tests/test_segments.py b/tests/test_segments.py index fc729858..345a6ccc 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -589,8 +589,50 @@ class TestVim(TestCase): self.assertEqual(vim.mode(pl=pl, segment_info=segment_info, override={'^V': 'VBLK'}), 'VBLK') def test_visual_range(self): - # TODO - pass + pl = Pl() + vr = partial(vim.visual_range, pl=pl) + vim_module.current.window.cursor = [0, 0] + try: + with vim_module._with('mode', 'i') as segment_info: + self.assertEqual(vr(segment_info=segment_info), '') + with vim_module._with('mode', '^V') as segment_info: + self.assertEqual(vr(segment_info=segment_info), '1 × 1') + with vim_module._with('vpos', line=5, col=5, off=0): + self.assertEqual(vr(segment_info=segment_info), '5 × 5') + with vim_module._with('vpos', line=5, col=4, off=0): + self.assertEqual(vr(segment_info=segment_info), '5 × 4') + with vim_module._with('mode', '^S') as segment_info: + self.assertEqual(vr(segment_info=segment_info), '1 × 1') + with vim_module._with('vpos', line=5, col=5, off=0): + self.assertEqual(vr(segment_info=segment_info), '5 × 5') + with vim_module._with('vpos', line=5, col=4, off=0): + self.assertEqual(vr(segment_info=segment_info), '5 × 4') + with vim_module._with('mode', 'V') as segment_info: + self.assertEqual(vr(segment_info=segment_info), 'L:1') + with vim_module._with('vpos', line=5, col=5, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('vpos', line=5, col=4, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('mode', 'S') as segment_info: + self.assertEqual(vr(segment_info=segment_info), 'L:1') + with vim_module._with('vpos', line=5, col=5, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('vpos', line=5, col=4, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('mode', 'v') as segment_info: + self.assertEqual(vr(segment_info=segment_info), 'C:1') + with vim_module._with('vpos', line=5, col=5, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('vpos', line=5, col=4, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('mode', 's') as segment_info: + self.assertEqual(vr(segment_info=segment_info), 'C:1') + with vim_module._with('vpos', line=5, col=5, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + with vim_module._with('vpos', line=5, col=4, off=0): + self.assertEqual(vr(segment_info=segment_info), 'L:5') + finally: + vim_module._close(1) def test_modified_indicator(self): pl = Pl() diff --git a/tests/vim.py b/tests/vim.py index c7db4230..7867f563 100644 --- a/tests/vim.py +++ b/tests/vim.py @@ -342,15 +342,22 @@ def _emul_setwinvar(winnr, varname, value): @_vim def _emul_virtcol(expr): - if expr == '.' or isinstance(expr, list): + if expr == '.': return current.window.cursor[1] + 1 + if isinstance(expr, list) and len(expr) == 3: + return expr[-2] + expr[-1] raise NotImplementedError +_v_pos = None + + @_vim def _emul_getpos(expr): - if expr == '.' or expr == 'v': + if expr == '.': return [0, current.window.cursor[0] + 1, current.window.cursor[1] + 1, 0] + if expr == 'v': + return _v_pos or [0, current.window.cursor[0] + 1, current.window.cursor[1] + 1, 0] raise NotImplementedError @@ -848,6 +855,23 @@ class _WithNewTabPage(object): self.tab._close() +class _WithGlobal(object): + def __init__(self, **kwargs): + self.kwargs = kwargs + + def __enter__(self): + self.empty = object() + self.old = dict(((key, globals().get(key, self.empty)) for key in self.kwargs)) + globals().update(self.kwargs) + + def __exit__(self, *args): + for k, v in self.old.items(): + if v is self.empty: + globals().pop(k, None) + else: + globals()[k] = v + + @_vim def _with(key, *args, **kwargs): if key == 'buffer': @@ -870,6 +894,8 @@ def _with(key, *args, **kwargs): return _WithSplit() elif key == 'tabpage': return _WithNewTabPage(*args, **kwargs) + elif key == 'vpos': + return _WithGlobal(_v_pos=[0, kwargs['line'], kwargs['col'], kwargs['off']]) class error(Exception): From f4b5c6a63f388c1316a3f91d0b637206ea72b41f Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 5 Aug 2014 23:03:59 +0400 Subject: [PATCH 4/4] Add fix for old Vims where getpos does not return list of integers --- powerline/segments/vim.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index 57572c1b..b8ebb286 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -134,8 +134,8 @@ def visual_range(pl, segment_info, CTRL_V_text='{rows} × {vcols}', v_text_oneli vcols Number of virtual columns in the selection ========= ============================================================= ''' - sline, scol, soff = vim_funcs['getpos']("v")[1:] - eline, ecol, eoff = vim_funcs['getpos'](".")[1:] + sline, scol, soff = [int(v) for v in vim_funcs['getpos']("v")[1:]] + eline, ecol, eoff = [int(v) for v in vim_funcs['getpos'](".")[1:]] svcol = vim_funcs['virtcol']([sline, scol, soff]) evcol = vim_funcs['virtcol']([eline, ecol, eoff]) rows = abs(eline - sline) + 1