0
0
.dotfiles/powerline-bin/powerline/vim.py

348 lines
11 KiB
Python
Raw Normal View History

2024-09-07 11:49:53 -04:00
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
import sys
import json
import logging
from itertools import count
try:
import vim
except ImportError:
vim = object()
from powerline.bindings.vim import vim_get_func, vim_getvar, get_vim_encoding, python_to_vim
from powerline import Powerline, FailedUnicode, finish_common_config
from powerline.lib.dict import mergedicts
from powerline.lib.unicode import u
def _override_from(config, override_varname, key=None):
try:
overrides = vim_getvar(override_varname)
except KeyError:
return config
if key is not None:
try:
overrides = overrides[key]
except KeyError:
return config
mergedicts(config, overrides)
return config
class VimVarHandler(logging.Handler, object):
'''Vim-specific handler which emits messages to Vim global variables
:param str varname:
Variable where
'''
def __init__(self, varname):
super(VimVarHandler, self).__init__()
utf_varname = u(varname)
self.vim_varname = utf_varname.encode('ascii')
vim.command('unlet! g:' + utf_varname)
vim.command('let g:' + utf_varname + ' = []')
def emit(self, record):
message = u(record.message)
if record.exc_text:
message += '\n' + u(record.exc_text)
vim.eval(b'add(g:' + self.vim_varname + b', ' + python_to_vim(message) + b')')
class VimPowerline(Powerline):
def init(self, pyeval='PowerlinePyeval', **kwargs):
super(VimPowerline, self).init('vim', **kwargs)
self.last_window_id = 1
self.pyeval = pyeval
self.construct_window_statusline = self.create_window_statusline_constructor()
if all((hasattr(vim.current.window, attr) for attr in ('options', 'vars', 'number'))):
self.win_idx = self.new_win_idx
else:
self.win_idx = self.old_win_idx
self._vim_getwinvar = vim_get_func('getwinvar', 'bytes')
self._vim_setwinvar = vim_get_func('setwinvar')
if sys.version_info < (3,):
def create_window_statusline_constructor(self):
window_statusline = b'%!' + str(self.pyeval) + b'(\'powerline.statusline({0})\')'
return window_statusline.format
else:
def create_window_statusline_constructor(self):
startstr = b'%!' + self.pyeval.encode('ascii') + b'(\'powerline.statusline('
endstr = b')\')'
return lambda idx: (
startstr + str(idx).encode('ascii') + endstr
)
create_window_statusline_constructor.__doc__ = (
'''Create function which returns &l:stl value being given window index
Created function must return :py:class:`bytes` instance because this is
what ``window.options['statusline']`` returns (``window`` is
:py:class:`vim.Window` instance).
:return:
Function with type ``int bytes``.
'''
)
default_log_stream = sys.stdout
def add_local_theme(self, key, config):
'''Add local themes at runtime (during vim session).
:param str key:
Matcher name (in format ``{matcher_module}.{module_attribute}`` or
``{module_attribute}`` if ``{matcher_module}`` is
``powerline.matchers.vim``). Function pointed by
``{module_attribute}`` should be hashable and accept a dictionary
with information about current buffer and return boolean value
indicating whether current window matched conditions. See also
:ref:`local_themes key description <config-ext-local_themes>`.
:param dict config:
:ref:`Theme <config-themes>` dictionary.
:return:
``True`` if theme was added successfully and ``False`` if theme with
the same matcher already exists.
'''
self.update_renderer()
matcher = self.get_matcher(key)
theme_config = {}
for cfg_path in self.theme_levels:
try:
lvl_config = self.load_config(cfg_path, 'theme')
except IOError:
pass
else:
mergedicts(theme_config, lvl_config)
mergedicts(theme_config, config)
try:
self.renderer.add_local_theme(matcher, {'config': theme_config})
except KeyError:
return False
else:
# Hack for local themes support: when reloading modules it is not
# guaranteed that .add_local_theme will be called once again, so
# this function arguments will be saved here for calling from
# .do_setup().
self.setup_kwargs.setdefault('_local_themes', []).append((key, config))
return True
get_encoding = staticmethod(get_vim_encoding)
def load_main_config(self):
main_config = _override_from(super(VimPowerline, self).load_main_config(), 'powerline_config_overrides')
try:
use_var_handler = bool(int(vim_getvar('powerline_use_var_handler')))
except KeyError:
use_var_handler = False
if use_var_handler:
main_config.setdefault('common', {})
main_config['common'] = finish_common_config(self.get_encoding(), main_config['common'])
main_config['common']['log_file'].append(['powerline.vim.VimVarHandler', [['powerline_log_messages']]])
return main_config
def load_theme_config(self, name):
return _override_from(
super(VimPowerline, self).load_theme_config(name),
'powerline_theme_overrides',
name
)
def get_local_themes(self, local_themes):
if not local_themes:
return {}
return dict((
(matcher, {'config': self.load_theme_config(val)})
for matcher, key, val in (
(
(None if k == '__tabline__' else self.get_matcher(k)),
k,
v
)
for k, v in local_themes.items()
) if (
matcher or
key == '__tabline__'
)
))
def get_matcher(self, match_name):
match_module, separator, match_function = match_name.rpartition('.')
if not separator:
match_module = 'powerline.matchers.{0}'.format(self.ext)
match_function = match_name
return self.get_module_attr(match_module, match_function, prefix='matcher_generator')
def get_config_paths(self):
try:
return vim_getvar('powerline_config_paths')
except KeyError:
return super(VimPowerline, self).get_config_paths()
def do_setup(self, pyeval=None, pycmd=None, can_replace_pyeval=True, _local_themes=()):
import __main__
if not pyeval:
pyeval = 'pyeval' if sys.version_info < (3,) else 'py3eval'
can_replace_pyeval = True
if not pycmd:
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) or pyeval == 'PowerlinePyeval':
vim.command(('''
function! PowerlinePyeval(e)
{pycmd} powerline.do_pyeval()
endfunction
''').format(pycmd=pycmd))
pyeval = 'PowerlinePyeval'
self.pyeval = pyeval
self.construct_window_statusline = self.create_window_statusline_constructor()
self.update_renderer()
__main__.powerline = self
try:
if (
bool(int(vim.eval('has(\'gui_running\') && argc() == 0')))
and not vim.current.buffer.name
and len(vim.windows) == 1
):
# Hack to show startup screen. Problems in GUI:
# - Defining local value of &statusline option while computing
# global value purges startup screen.
# - Defining highlight group while computing statusline purges
# startup screen.
# This hack removes the “while computing statusline” part: both
# things are defined, but they are defined right now.
#
# The above condition disables this hack if no GUI is running,
# Vim did not open any files and there is only one window.
# Without GUI everything works, in other cases startup screen is
# not shown.
self.new_window()
except UnicodeDecodeError:
# vim.current.buffer.name may raise UnicodeDecodeError when using
# Python-3*. Fortunately, this means that current buffer is not
# empty buffer, so the above condition should be False.
pass
# Cannot have this in one line due to weird newline handling (in :execute
# context newline is considered part of the command in just the same cases
# when bar is considered part of the command (unless defining function
# inside :execute)). vim.command is :execute equivalent regarding this case.
vim.command('augroup Powerline')
vim.command(' autocmd! ColorScheme * :{pycmd} powerline.reset_highlight()'.format(pycmd=pycmd))
vim.command(' autocmd! VimLeavePre * :{pycmd} powerline.shutdown()'.format(pycmd=pycmd))
vim.command('augroup END')
# Hack for local themes support after reloading.
for args in _local_themes:
self.add_local_theme(*args)
def reset_highlight(self):
try:
self.renderer.reset_highlight()
except AttributeError:
# Renderer object appears only after first `.render()` call. Thus if
# ColorScheme event happens before statusline is drawn for the first
# time AttributeError will be thrown for the self.renderer. It is
# fine to ignore it: no renderer == no colors to reset == no need to
# do anything.
pass
def new_win_idx(self, window_id):
r = None
for window in vim.windows:
try:
curwindow_id = window.vars['powerline_window_id']
if r is not None and curwindow_id == window_id:
raise KeyError
except KeyError:
curwindow_id = self.last_window_id
self.last_window_id += 1
window.vars['powerline_window_id'] = curwindow_id
statusline = self.construct_window_statusline(curwindow_id)
if window.options['statusline'] != statusline:
window.options['statusline'] = statusline
if curwindow_id == window_id if window_id else window is vim.current.window:
r = (window, curwindow_id, window.number)
return r
def old_win_idx(self, window_id):
r = None
for winnr, window in zip(count(1), vim.windows):
curwindow_id = self._vim_getwinvar(winnr, 'powerline_window_id')
if curwindow_id and not (r is not None and curwindow_id == window_id):
curwindow_id = int(curwindow_id)
else:
curwindow_id = self.last_window_id
self.last_window_id += 1
self._vim_setwinvar(winnr, 'powerline_window_id', curwindow_id)
statusline = self.construct_window_statusline(curwindow_id)
if self._vim_getwinvar(winnr, '&statusline') != statusline:
self._vim_setwinvar(winnr, '&statusline', statusline)
if curwindow_id == window_id if window_id else window is vim.current.window:
r = (window, curwindow_id, winnr)
return r
def statusline(self, window_id):
window, window_id, winnr = self.win_idx(window_id) or (None, None, None)
if not window:
return FailedUnicode('No window {0}'.format(window_id))
return self.render(window, window_id, winnr)
def tabline(self):
return self.render(*self.win_idx(None), is_tabline=True)
def new_window(self):
return self.render(*self.win_idx(None))
@staticmethod
def do_pyeval():
'''Evaluate python string passed to PowerlinePyeval
Is here to reduce the number of requirements to __main__ globals to just
one powerline object (previously it required as well vim and json).
'''
import __main__
vim.command('return ' + json.dumps(eval(vim.eval('a:e'), __main__.__dict__)))
def setup_components(self, components):
if components is None:
components = ('statusline', 'tabline')
if 'statusline' in components:
# Is immediately changed after new_window function is run. Good for
# global value.
vim.command('set statusline=%!{pyeval}(\'powerline.new_window()\')'.format(
pyeval=self.pyeval))
if 'tabline' in components:
vim.command('set tabline=%!{pyeval}(\'powerline.tabline()\')'.format(
pyeval=self.pyeval))
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(*args, **kwargs):
powerline = VimPowerline()
return powerline.setup(*args, **kwargs)