483 lines
11 KiB
Python
483 lines
11 KiB
Python
|
# vim:fileencoding=utf-8:noet
|
||
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||
|
|
||
|
import sys
|
||
|
import codecs
|
||
|
|
||
|
try:
|
||
|
import vim
|
||
|
except ImportError:
|
||
|
vim = object()
|
||
|
|
||
|
from powerline.lib.unicode import unicode
|
||
|
|
||
|
|
||
|
if (
|
||
|
hasattr(vim, 'options')
|
||
|
and hasattr(vim, 'vvars')
|
||
|
and vim.vvars['version'] > 703
|
||
|
):
|
||
|
if sys.version_info < (3,):
|
||
|
def get_vim_encoding():
|
||
|
return vim.options['encoding'] or 'ascii'
|
||
|
else:
|
||
|
def get_vim_encoding():
|
||
|
return vim.options['encoding'].decode('ascii') or 'ascii'
|
||
|
elif hasattr(vim, 'eval'):
|
||
|
def get_vim_encoding():
|
||
|
return vim.eval('&encoding') or 'ascii'
|
||
|
else:
|
||
|
def get_vim_encoding():
|
||
|
return 'utf-8'
|
||
|
|
||
|
get_vim_encoding.__doc__ = (
|
||
|
'''Get encoding used for Vim strings
|
||
|
|
||
|
:return:
|
||
|
Value of ``&encoding``. If it is empty (i.e. Vim is compiled
|
||
|
without +multibyte) returns ``'ascii'``. When building documentation
|
||
|
outputs ``'utf-8'`` unconditionally.
|
||
|
'''
|
||
|
)
|
||
|
|
||
|
|
||
|
vim_encoding = get_vim_encoding()
|
||
|
|
||
|
|
||
|
python_to_vim_types = {
|
||
|
unicode: (
|
||
|
lambda o: b'\'' + (o.translate({
|
||
|
ord('\''): '\'\'',
|
||
|
}).encode(vim_encoding)) + b'\''
|
||
|
),
|
||
|
list: (
|
||
|
lambda o: b'[' + (
|
||
|
b','.join((python_to_vim(i) for i in o))
|
||
|
) + b']'
|
||
|
),
|
||
|
bytes: (lambda o: b'\'' + o.replace(b'\'', b'\'\'') + b'\''),
|
||
|
int: (str if str is bytes else (lambda o: unicode(o).encode('ascii'))),
|
||
|
}
|
||
|
python_to_vim_types[float] = python_to_vim_types[int]
|
||
|
|
||
|
|
||
|
def python_to_vim(o):
|
||
|
return python_to_vim_types[type(o)](o)
|
||
|
|
||
|
|
||
|
if sys.version_info < (3,):
|
||
|
def str_to_bytes(s):
|
||
|
return s
|
||
|
|
||
|
def unicode_eval(expr):
|
||
|
ret = vim.eval(expr)
|
||
|
return ret.decode(vim_encoding, 'powerline_vim_strtrans_error')
|
||
|
else:
|
||
|
def str_to_bytes(s):
|
||
|
return s.encode(vim_encoding)
|
||
|
|
||
|
def unicode_eval(expr):
|
||
|
return vim.eval(expr)
|
||
|
|
||
|
|
||
|
def safe_bytes_eval(expr):
|
||
|
return bytes(bytearray((
|
||
|
int(chunk) for chunk in (
|
||
|
vim.eval(
|
||
|
b'substitute(' + expr + b', ' +
|
||
|
b'\'^.*$\', \'\\=join(map(range(len(submatch(0))), ' +
|
||
|
b'"char2nr(submatch(0)[v:val])"))\', "")'
|
||
|
).split()
|
||
|
)
|
||
|
)))
|
||
|
|
||
|
|
||
|
def eval_bytes(expr):
|
||
|
try:
|
||
|
return str_to_bytes(vim.eval(expr))
|
||
|
except UnicodeDecodeError:
|
||
|
return safe_bytes_eval(expr)
|
||
|
|
||
|
|
||
|
def eval_unicode(expr):
|
||
|
try:
|
||
|
return unicode_eval(expr)
|
||
|
except UnicodeDecodeError:
|
||
|
return safe_bytes_eval(expr).decode(vim_encoding, 'powerline_vim_strtrans_error')
|
||
|
|
||
|
|
||
|
if hasattr(vim, 'bindeval'):
|
||
|
rettype_func = {
|
||
|
None: lambda f: f,
|
||
|
'unicode': (
|
||
|
lambda f: (
|
||
|
lambda *args, **kwargs: (
|
||
|
f(*args, **kwargs).decode(
|
||
|
vim_encoding, 'powerline_vim_strtrans_error'
|
||
|
))))
|
||
|
}
|
||
|
rettype_func['int'] = rettype_func['bytes'] = rettype_func[None]
|
||
|
rettype_func['str'] = rettype_func['bytes'] if str is bytes else rettype_func['unicode']
|
||
|
|
||
|
def vim_get_func(f, rettype=None):
|
||
|
'''Return a vim function binding.'''
|
||
|
try:
|
||
|
func = vim.bindeval('function("' + f + '")')
|
||
|
except vim.error:
|
||
|
return None
|
||
|
else:
|
||
|
return rettype_func[rettype](func)
|
||
|
else:
|
||
|
rettype_eval = {
|
||
|
None: getattr(vim, 'eval', None),
|
||
|
'int': lambda expr: int(vim.eval(expr)),
|
||
|
'bytes': eval_bytes,
|
||
|
'unicode': eval_unicode,
|
||
|
}
|
||
|
rettype_eval['str'] = rettype_eval[None]
|
||
|
|
||
|
class VimFunc(object):
|
||
|
'''Evaluate a vim function using vim.eval().
|
||
|
|
||
|
This is a fallback class for older vim versions.
|
||
|
'''
|
||
|
__slots__ = ('f', 'eval')
|
||
|
|
||
|
def __init__(self, f, rettype=None):
|
||
|
self.f = f.encode('utf-8')
|
||
|
self.eval = rettype_eval[rettype]
|
||
|
|
||
|
def __call__(self, *args):
|
||
|
return self.eval(self.f + b'(' + (b','.join((
|
||
|
python_to_vim(o) for o in args
|
||
|
))) + b')')
|
||
|
|
||
|
vim_get_func = VimFunc
|
||
|
|
||
|
|
||
|
def vim_get_autoload_func(f, rettype=None):
|
||
|
func = vim_get_func(f)
|
||
|
if not func:
|
||
|
vim.command('runtime! ' + f.replace('#', '/')[:f.rindex('#')] + '.vim')
|
||
|
func = vim_get_func(f)
|
||
|
return func
|
||
|
|
||
|
|
||
|
if hasattr(vim, 'Function'):
|
||
|
def vim_func_exists(f):
|
||
|
try:
|
||
|
vim.Function(f)
|
||
|
except ValueError:
|
||
|
return False
|
||
|
else:
|
||
|
return True
|
||
|
else:
|
||
|
def vim_func_exists(f):
|
||
|
try:
|
||
|
return bool(int(vim.eval('exists("*{0}")'.format(f))))
|
||
|
except vim.error:
|
||
|
return False
|
||
|
|
||
|
|
||
|
if type(vim) is object:
|
||
|
vim_get_func = lambda *args, **kwargs: None
|
||
|
|
||
|
|
||
|
_getbufvar = vim_get_func('getbufvar')
|
||
|
_vim_exists = vim_get_func('exists', rettype='int')
|
||
|
|
||
|
|
||
|
# It may crash on some old vim versions and I do not remember in which patch
|
||
|
# I fixed this crash.
|
||
|
if hasattr(vim, 'vvars') and vim.vvars[str('version')] > 703:
|
||
|
_vim_to_python_types = {
|
||
|
getattr(vim, 'Dictionary', None) or type(vim.bindeval('{}')):
|
||
|
lambda value: dict((
|
||
|
(_vim_to_python(k), _vim_to_python(v))
|
||
|
for k, v in value.items()
|
||
|
)),
|
||
|
getattr(vim, 'List', None) or type(vim.bindeval('[]')):
|
||
|
lambda value: [_vim_to_python(item) for item in value],
|
||
|
getattr(vim, 'Function', None) or type(vim.bindeval('function("mode")')):
|
||
|
lambda _: None,
|
||
|
}
|
||
|
|
||
|
def vim_getvar(varname):
|
||
|
return _vim_to_python(vim.vars[str(varname)])
|
||
|
|
||
|
def bufvar_exists(buffer, varname):
|
||
|
buffer = buffer or vim.current.buffer
|
||
|
return varname in buffer.vars
|
||
|
|
||
|
def vim_getwinvar(segment_info, varname):
|
||
|
return _vim_to_python(segment_info['window'].vars[str(varname)])
|
||
|
|
||
|
def vim_global_exists(name):
|
||
|
try:
|
||
|
vim.vars[name]
|
||
|
except KeyError:
|
||
|
return False
|
||
|
else:
|
||
|
return True
|
||
|
else:
|
||
|
_vim_to_python_types = {
|
||
|
dict: (lambda value: dict(((k, _vim_to_python(v)) for k, v in value.items()))),
|
||
|
list: (lambda value: [_vim_to_python(i) for i in value]),
|
||
|
}
|
||
|
|
||
|
def vim_getvar(varname):
|
||
|
varname = 'g:' + varname
|
||
|
if _vim_exists(varname):
|
||
|
return vim.eval(varname)
|
||
|
else:
|
||
|
raise KeyError(varname)
|
||
|
|
||
|
def bufvar_exists(buffer, varname):
|
||
|
if not buffer or buffer.number == vim.current.buffer.number:
|
||
|
return int(vim.eval('exists("b:{0}")'.format(varname)))
|
||
|
else:
|
||
|
return int(vim.eval(
|
||
|
'has_key(getbufvar({0}, ""), {1})'.format(buffer.number, varname)
|
||
|
))
|
||
|
|
||
|
def vim_getwinvar(segment_info, varname):
|
||
|
result = vim.eval('getwinvar({0}, "{1}")'.format(segment_info['winnr'], varname))
|
||
|
if result == '':
|
||
|
if not int(vim.eval('has_key(getwinvar({0}, ""), "{1}")'.format(segment_info['winnr'], varname))):
|
||
|
raise KeyError(varname)
|
||
|
return result
|
||
|
|
||
|
def vim_global_exists(name):
|
||
|
return int(vim.eval('exists("g:' + name + '")'))
|
||
|
|
||
|
|
||
|
def vim_command_exists(name):
|
||
|
return _vim_exists(':' + name)
|
||
|
|
||
|
|
||
|
if sys.version_info < (3,):
|
||
|
getbufvar = _getbufvar
|
||
|
else:
|
||
|
_vim_to_python_types[bytes] = lambda value: value.decode(vim_encoding)
|
||
|
|
||
|
def getbufvar(*args):
|
||
|
return _vim_to_python(_getbufvar(*args))
|
||
|
|
||
|
|
||
|
_id = lambda value: value
|
||
|
|
||
|
|
||
|
def _vim_to_python(value):
|
||
|
return _vim_to_python_types.get(type(value), _id)(value)
|
||
|
|
||
|
|
||
|
if hasattr(vim, 'options'):
|
||
|
def vim_getbufoption(info, option):
|
||
|
return _vim_to_python(info['buffer'].options[str(option)])
|
||
|
|
||
|
def vim_getoption(option):
|
||
|
return vim.options[str(option)]
|
||
|
|
||
|
def vim_setoption(option, value):
|
||
|
vim.options[str(option)] = value
|
||
|
else:
|
||
|
def vim_getbufoption(info, option):
|
||
|
return getbufvar(info['bufnr'], '&' + option)
|
||
|
|
||
|
def vim_getoption(option):
|
||
|
return vim.eval('&g:' + option)
|
||
|
|
||
|
def vim_setoption(option, value):
|
||
|
vim.command('let &g:{option} = {value}'.format(
|
||
|
option=option, value=python_to_vim(value)))
|
||
|
|
||
|
|
||
|
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
|
||
|
def __nonzero__():
|
||
|
return False
|
||
|
|
||
|
__bool__ = __nonzero__
|
||
|
|
||
|
def get_buffer(number):
|
||
|
for buffer in vim.buffers:
|
||
|
if buffer.number == number:
|
||
|
return buffer
|
||
|
raise KeyError(number)
|
||
|
|
||
|
class WindowVars(object):
|
||
|
__slots__ = ('tabnr', 'winnr')
|
||
|
|
||
|
def __init__(self, window):
|
||
|
self.tabnr = window.tabnr
|
||
|
self.winnr = window.number
|
||
|
|
||
|
def __getitem__(self, key):
|
||
|
has_key = vim.eval('has_key(gettabwinvar({0}, {1}, ""), "{2}")'.format(self.tabnr, self.winnr, key))
|
||
|
if has_key == '0':
|
||
|
raise KeyError
|
||
|
return vim.eval('gettabwinvar({0}, {1}, "{2}")'.format(self.tabnr, self.winnr, key))
|
||
|
|
||
|
def get(self, key, default=None):
|
||
|
try:
|
||
|
return self[key]
|
||
|
except KeyError:
|
||
|
return default
|
||
|
|
||
|
class Window(FalseObject):
|
||
|
__slots__ = ('tabnr', 'number', '_vars')
|
||
|
|
||
|
def __init__(self, tabnr, number):
|
||
|
self.tabnr = tabnr
|
||
|
self.number = number
|
||
|
self.vars = WindowVars(self)
|
||
|
|
||
|
@property
|
||
|
def buffer(self):
|
||
|
return get_buffer(int(vim.eval('tabpagebuflist({0})[{1}]'.format(self.tabnr, self.number - 1))))
|
||
|
|
||
|
class Tabpage(FalseObject):
|
||
|
__slots__ = ('number',)
|
||
|
|
||
|
def __init__(self, number):
|
||
|
self.number = number
|
||
|
|
||
|
def __eq__(self, tabpage):
|
||
|
if not isinstance(tabpage, Tabpage):
|
||
|
raise NotImplementedError
|
||
|
return self.number == tabpage.number
|
||
|
|
||
|
@property
|
||
|
def window(self):
|
||
|
return Window(self.number, int(vim.eval('tabpagewinnr({0})'.format(self.number))))
|
||
|
|
||
|
def _last_tab_nr():
|
||
|
return int(vim.eval('tabpagenr("$")'))
|
||
|
|
||
|
def current_tabpage():
|
||
|
return Tabpage(int(vim.eval('tabpagenr()')))
|
||
|
|
||
|
def list_tabpages():
|
||
|
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
|
||
|
def __getitem__(key):
|
||
|
return vim.eval('$' + key)
|
||
|
|
||
|
@staticmethod
|
||
|
def get(key, default=None):
|
||
|
return vim.eval('$' + key) or default
|
||
|
|
||
|
@staticmethod
|
||
|
def __setitem__(key, value):
|
||
|
return vim.command(
|
||
|
'let ${0}="{1}"'.format(
|
||
|
key,
|
||
|
value.replace('"', '\\"')
|
||
|
.replace('\\', '\\\\')
|
||
|
.replace('\n', '\\n')
|
||
|
.replace('\0', '')
|
||
|
)
|
||
|
)
|
||
|
|
||
|
|
||
|
if sys.version_info < (3,):
|
||
|
def buffer_name(segment_info):
|
||
|
return segment_info['buffer'].name
|
||
|
else:
|
||
|
vim_bufname = vim_get_func('bufname', rettype='bytes')
|
||
|
|
||
|
def buffer_name(segment_info):
|
||
|
try:
|
||
|
name = segment_info['buffer'].name
|
||
|
except UnicodeDecodeError:
|
||
|
return vim_bufname(segment_info['bufnr'])
|
||
|
else:
|
||
|
return name.encode(segment_info['encoding']) if name else None
|
||
|
|
||
|
|
||
|
vim_strtrans = vim_get_func('strtrans', rettype='unicode')
|
||
|
|
||
|
|
||
|
def powerline_vim_strtrans_error(e):
|
||
|
if not isinstance(e, UnicodeDecodeError):
|
||
|
raise NotImplementedError
|
||
|
text = vim_strtrans(e.object[e.start:e.end])
|
||
|
return (text, e.end)
|
||
|
|
||
|
|
||
|
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()
|
||
|
|
||
|
|
||
|
def create_ruby_dpowerline():
|
||
|
vim.command((
|
||
|
'''
|
||
|
ruby
|
||
|
if $powerline == nil
|
||
|
class Powerline
|
||
|
end
|
||
|
$powerline = Powerline.new
|
||
|
end
|
||
|
'''
|
||
|
))
|