Add trailing whitespace segment
Note: by default this segment is disabled. Until #923 it may only be enabled by copying the whole file and changing "enabled" to "true". After #923 it may be enabled by having `~/.config/powerline/themes/vim/default.json` with the following contents: { "segment_data": { "trailing_whitespace": { "display": true } } } Fixes #388
This commit is contained in:
parent
16c01e8d64
commit
9e8c115eea
|
@ -159,4 +159,31 @@ def powerline_vim_strtrans_error(e):
|
|||
codecs.register_error('powerline_vim_strtrans_error', powerline_vim_strtrans_error)
|
||||
|
||||
|
||||
did_autocmd = False
|
||||
buffer_caches = []
|
||||
|
||||
|
||||
def register_buffer_cache(cachedict):
|
||||
global did_autocmd
|
||||
global buffer_caches
|
||||
from powerline.vim import get_default_pycmd, pycmd
|
||||
if not did_autocmd:
|
||||
import __main__
|
||||
__main__.powerline_on_bwipe = on_bwipe
|
||||
vim.command('augroup Powerline')
|
||||
vim.command(' autocmd! BufWipeout * :{pycmd} powerline_on_bwipe()'.format(
|
||||
pycmd=(pycmd or get_default_pycmd())))
|
||||
vim.command('augroup END')
|
||||
did_autocmd = True
|
||||
buffer_caches.append(cachedict)
|
||||
return cachedict
|
||||
|
||||
|
||||
def on_bwipe():
|
||||
global buffer_caches
|
||||
bufnr = int(vim.eval('expand("<abuf>")'))
|
||||
for cachedict in buffer_caches:
|
||||
cachedict.pop(bufnr, None)
|
||||
|
||||
|
||||
environ = VimEnviron()
|
||||
|
|
|
@ -58,6 +58,12 @@
|
|||
"name": "modified_indicator",
|
||||
"before": " "
|
||||
},
|
||||
{
|
||||
"exclude_modes": ["i", "R", "Rv"],
|
||||
"name": "trailing_whitespace",
|
||||
"display": false,
|
||||
"priority": 60
|
||||
},
|
||||
{
|
||||
"exclude_modes": ["nc"],
|
||||
"module": "powerline.segments.plugin.syntastic",
|
||||
|
|
|
@ -8,8 +8,14 @@ try:
|
|||
except ImportError:
|
||||
vim = {} # NOQA
|
||||
|
||||
try:
|
||||
from __builtin__ import xrange as range
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption,
|
||||
buffer_name, vim_getwinvar)
|
||||
buffer_name, vim_getwinvar,
|
||||
register_buffer_cache)
|
||||
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
|
||||
|
@ -17,6 +23,7 @@ from powerline.lib.humanize_bytes import humanize_bytes
|
|||
from powerline.lib import wraps_saveargs as wraps
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
vim_funcs = {
|
||||
'virtcol': vim_get_func('virtcol', rettype=int),
|
||||
'getpos': vim_get_func('getpos'),
|
||||
|
@ -420,3 +427,55 @@ def file_vcs_status(pl, segment_info, create_watcher):
|
|||
'highlight_group': ['file_vcs_status_' + status, 'file_vcs_status'],
|
||||
})
|
||||
return ret
|
||||
|
||||
|
||||
trailing_whitespace_cache = None
|
||||
|
||||
|
||||
@requires_segment_info
|
||||
def trailing_whitespace(pl, segment_info):
|
||||
'''Return the line number for trailing whitespaces
|
||||
|
||||
It is advised not to use this segment in insert mode: in Insert mode it will
|
||||
iterate over all lines in buffer each time you happen to type a character
|
||||
which may cause lags. It will also show you whitespace warning each time you
|
||||
happen to type space.
|
||||
|
||||
Highlight groups used: ``trailing_whitespace`` or ``warning``.
|
||||
'''
|
||||
global trailing_whitespace_cache
|
||||
if trailing_whitespace_cache is None:
|
||||
trailing_whitespace_cache = register_buffer_cache(defaultdict(lambda: (0, None)))
|
||||
bufnr = segment_info['bufnr']
|
||||
changedtick = getbufvar(bufnr, 'changedtick')
|
||||
if trailing_whitespace_cache[bufnr][0] == changedtick:
|
||||
return trailing_whitespace_cache[bufnr][1]
|
||||
else:
|
||||
buf = segment_info['buffer']
|
||||
bws = b' \t'
|
||||
sws = str(bws)
|
||||
for i in range(len(buf)):
|
||||
try:
|
||||
line = buf[i]
|
||||
except UnicodeDecodeError: # May happen in Python 3
|
||||
if hasattr(vim, 'bindeval'):
|
||||
line = vim.bindeval('getbufline({0}, {1})'.format(
|
||||
bufnr, i + 1))
|
||||
has_trailing_ws = (line[-1] in bws)
|
||||
else:
|
||||
line = vim.eval('strtrans(getbufline({0}, {1}))'.format(
|
||||
bufnr, i + 1))
|
||||
has_trailing_ws = (line[-1] in bws)
|
||||
else:
|
||||
has_trailing_ws = (line and line[-1] in sws)
|
||||
if has_trailing_ws:
|
||||
break
|
||||
if has_trailing_ws:
|
||||
ret = [{
|
||||
'contents': str(i + 1),
|
||||
'highlight_group': ['trailing_whitespace', 'warning'],
|
||||
}]
|
||||
else:
|
||||
ret = None
|
||||
trailing_whitespace_cache[bufnr] = (changedtick, ret)
|
||||
return ret
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import sys
|
||||
from powerline.bindings.vim import vim_get_func, vim_getvar
|
||||
from powerline import Powerline
|
||||
from powerline.lib import mergedicts
|
||||
|
@ -155,14 +156,27 @@ class VimPowerline(Powerline):
|
|||
__main__.__dict__)))
|
||||
|
||||
|
||||
pycmd = None
|
||||
|
||||
|
||||
def set_pycmd(new_pycmd):
|
||||
global pycmd
|
||||
pycmd = new_pycmd
|
||||
|
||||
|
||||
def get_default_pycmd():
|
||||
return 'python' if sys.version_info < (3,) else 'python3'
|
||||
|
||||
|
||||
def setup(pyeval=None, pycmd=None, can_replace_pyeval=True):
|
||||
import sys
|
||||
import __main__
|
||||
if not pyeval:
|
||||
pyeval = 'pyeval' if sys.version_info < (3,) else 'py3eval'
|
||||
can_replace_pyeval = True
|
||||
if not pycmd:
|
||||
pycmd = 'python' if sys.version_info < (3,) else 'python3'
|
||||
pycmd = get_default_pycmd()
|
||||
|
||||
set_pycmd(pycmd)
|
||||
|
||||
# pyeval() and vim.bindeval were both introduced in one patch
|
||||
if not hasattr(vim, 'bindeval') and can_replace_pyeval:
|
||||
|
|
|
@ -757,6 +757,26 @@ class TestVim(TestCase):
|
|||
with replace_attr(vim, 'guess', get_dummy_guess(status=lambda file: 'M')):
|
||||
self.assertEqual(file_vcs_status(segment_info=segment_info), None)
|
||||
|
||||
def test_trailing_whitespace(self):
|
||||
pl = Pl()
|
||||
with vim_module._with('buffer', 'tws') as segment_info:
|
||||
trailing_whitespace = partial(vim.trailing_whitespace, pl=pl, segment_info=segment_info)
|
||||
self.assertEqual(trailing_whitespace(), None)
|
||||
self.assertEqual(trailing_whitespace(), None)
|
||||
vim_module.current.buffer[0] = ' '
|
||||
self.assertEqual(trailing_whitespace(), [{
|
||||
'highlight_group': ['trailing_whitespace', 'warning'],
|
||||
'contents': '1',
|
||||
}])
|
||||
self.assertEqual(trailing_whitespace(), [{
|
||||
'highlight_group': ['trailing_whitespace', 'warning'],
|
||||
'contents': '1',
|
||||
}])
|
||||
vim_module.current.buffer[0] = ''
|
||||
self.assertEqual(trailing_whitespace(), None)
|
||||
self.assertEqual(trailing_whitespace(), None)
|
||||
|
||||
|
||||
old_cwd = None
|
||||
|
||||
|
||||
|
|
50
tests/vim.py
50
tests/vim.py
|
@ -167,8 +167,14 @@ def _log_print():
|
|||
sys.stdout.write(repr(entry) + '\n')
|
||||
|
||||
|
||||
_current_group = None
|
||||
_on_wipeout = []
|
||||
|
||||
|
||||
@_vim
|
||||
def command(cmd):
|
||||
global _current_group
|
||||
cmd = cmd.lstrip()
|
||||
if cmd.startswith('let g:'):
|
||||
import re
|
||||
varname, value = re.compile(r'^let g:(\w+)\s*=\s*(.*)').match(cmd).groups()
|
||||
|
@ -179,6 +185,26 @@ def command(cmd):
|
|||
elif cmd.startswith('function! Powerline_plugin_ctrlp'):
|
||||
# Ignore CtrlP updating functions
|
||||
pass
|
||||
elif cmd.startswith('augroup'):
|
||||
augroup = cmd.partition(' ')[2]
|
||||
if augroup.upper() == 'END':
|
||||
_current_group = None
|
||||
else:
|
||||
_current_group = augroup
|
||||
elif cmd.startswith('autocmd'):
|
||||
rest = cmd.partition(' ')[2]
|
||||
auevent, rest = rest.partition(' ')[::2]
|
||||
pattern, aucmd = rest.partition(' ')[::2]
|
||||
if auevent != 'BufWipeout' or pattern != '*':
|
||||
raise NotImplementedError
|
||||
import sys
|
||||
if sys.version_info < (3,):
|
||||
if not aucmd.startswith(':python '):
|
||||
raise NotImplementedError
|
||||
else:
|
||||
if not aucmd.startswith(':python3 '):
|
||||
raise NotImplementedError
|
||||
_on_wipeout.append(aucmd.partition(' ')[2])
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -314,8 +340,9 @@ def _emul_fnamemodify(path, modstring):
|
|||
@_vim
|
||||
@_str_func
|
||||
def _emul_expand(expr):
|
||||
global _abuf
|
||||
if expr == '<abuf>':
|
||||
return _buffer()
|
||||
return _abuf or _buffer()
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
|
@ -398,6 +425,7 @@ class _Window(object):
|
|||
_buf_lines = {}
|
||||
_undostate = {}
|
||||
_undo_written = {}
|
||||
_abuf = None
|
||||
|
||||
|
||||
class _Buffer(object):
|
||||
|
@ -408,7 +436,7 @@ class _Buffer(object):
|
|||
self.number = bufnr
|
||||
# FIXME Use unicode() for python-3
|
||||
self.name = name
|
||||
self.vars = {}
|
||||
self.vars = {'changedtick': 1}
|
||||
self.options = {
|
||||
'modified': 0,
|
||||
'readonly': 0,
|
||||
|
@ -447,12 +475,14 @@ class _Buffer(object):
|
|||
|
||||
def __setitem__(self, line, value):
|
||||
self.options['modified'] = 1
|
||||
self.vars['changedtick'] += 1
|
||||
_buf_lines[self.number][line] = value
|
||||
from copy import copy
|
||||
_undostate[self.number].append(copy(_buf_lines[self.number]))
|
||||
|
||||
def __setslice__(self, *args):
|
||||
self.options['modified'] = 1
|
||||
self.vars['changedtick'] += 1
|
||||
_buf_lines[self.number].__setslice__(*args)
|
||||
from copy import copy
|
||||
_undostate[self.number].append(copy(_buf_lines[self.number]))
|
||||
|
@ -467,7 +497,23 @@ class _Buffer(object):
|
|||
return '<buffer ' + str(self.name) + '>'
|
||||
|
||||
def __del__(self):
|
||||
global _abuf
|
||||
bufnr = self.number
|
||||
try:
|
||||
import __main__
|
||||
except ImportError:
|
||||
pass
|
||||
except RuntimeError:
|
||||
# Module may have already been garbage-collected
|
||||
pass
|
||||
else:
|
||||
if _on_wipeout:
|
||||
_abuf = bufnr
|
||||
try:
|
||||
for event in _on_wipeout:
|
||||
exec(event, __main__.__dict__)
|
||||
finally:
|
||||
_abuf = None
|
||||
if _buf_lines:
|
||||
_buf_lines.pop(bufnr)
|
||||
if _undostate:
|
||||
|
|
Loading…
Reference in New Issue