# vim:fileencoding=utf-8:noet _log = [] vars = {} vvars = {'version': 703} _tabpage = 0 _mode = 'n' _buf_purge_events = set() options = { 'paste': 0, 'ambiwidth': 'single', 'columns': 80, 'encoding': 'utf-8', } _last_bufnr = 0 _highlights = {} from collections import defaultdict as _defaultdict _environ = _defaultdict(lambda: '') del _defaultdict _thread_id = None def _set_thread_id(): global _thread_id from threading import current_thread _thread_id = current_thread().ident # Assuming import is done from the main thread _set_thread_id() def _print_log(): for item in _log: print (item) _log[:] = () def _vim(func): from functools import wraps from threading import current_thread @wraps(func) def f(*args, **kwargs): global _thread_id if _thread_id != current_thread().ident: raise RuntimeError('Accessing vim from separate threads is not allowed') _log.append((func.__name__, args)) return func(*args, **kwargs) return f def _unicode(func): from functools import wraps import sys if sys.version_info < (3,): return func @wraps(func) def f(*args, **kwargs): from powerline.lib.unicode import u ret = func(*args, **kwargs) if isinstance(ret, bytes): ret = u(ret) return ret return f class _Buffers(object): @_vim def __init__(self): self.d = {} @_vim def __len__(self): return len(self.d) @_vim def __getitem__(self, item): return self.d[item] @_vim def __setitem__(self, item, value): self.d[item] = value @_vim def __iter__(self): return iter(self.d.values()) @_vim def __contains__(self, item): return item in self.d @_vim def _keys(self): return self.d.keys() @_vim def _pop(self, *args, **kwargs): return self.d.pop(*args, **kwargs) buffers = _Buffers() class _ObjList(object): @_vim def __init__(self, objtype): self.l = [] self.objtype = objtype @_vim def __getitem__(self, item): return self.l[item - int(item > 0)] @_vim def __len__(self): return len(self.l) @_vim def __iter__(self): return iter(self.l) @_vim def _pop(self, idx): obj = self.l.pop(idx - 1) for moved_obj in self.l[idx - 1:]: moved_obj.number -= 1 return obj @_vim def _append(self, *args, **kwargs): return self.l.append(*args, **kwargs) @_vim def _new(self, *args, **kwargs): number = len(self) + 1 new_obj = self.objtype(number, *args, **kwargs) self._append(new_obj) return new_obj def _construct_result(r): import sys if sys.version_info < (3,): return r else: if isinstance(r, str): return r.encode('utf-8') elif isinstance(r, list): return [_construct_result(i) for i in r] elif isinstance(r, dict): return dict(( (_construct_result(k), _construct_result(v)) for k, v in r.items() )) return r def _str_func(func): from functools import wraps @wraps(func) def f(*args, **kwargs): return _construct_result(func(*args, **kwargs)) return f def _log_print(): import sys for entry in _log: 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() vars[varname] = value elif cmd.startswith('hi '): sp = cmd.split() _highlights[sp[1]] = sp[2:] 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]) elif cmd.startswith('set '): if cmd.startswith('set statusline='): options['statusline'] = cmd[len('set statusline='):] elif cmd.startswith('set tabline='): options['tabline'] = cmd[len('set tabline='):] else: raise NotImplementedError(cmd) else: raise NotImplementedError(cmd) @_vim @_unicode def eval(expr): if expr.startswith('g:'): return vars[expr[2:]] elif expr.startswith('v:'): return vvars[expr[2:]] elif expr.startswith('&'): return options[expr[1:]] elif expr.startswith('$'): return _environ[expr[1:]] elif expr.startswith('PowerlineRegisterCachePurgerEvent'): _buf_purge_events.add(expr[expr.find('"') + 1:expr.rfind('"') - 1]) return '0' elif expr.startswith('exists('): return '0' elif expr.startswith('getwinvar('): import re match = re.match(r'^getwinvar\((\d+), "(\w+)"\)$', expr) if not match: raise NotImplementedError(expr) winnr = int(match.group(1)) varname = match.group(2) return _emul_getwinvar(winnr, varname) elif expr.startswith('has_key('): import re match = re.match(r'^has_key\(getwinvar\((\d+), ""\), "(\w+)"\)$', expr) if match: winnr = int(match.group(1)) varname = match.group(2) return 0 + (varname in current.tabpage.windows[winnr].vars) else: match = re.match(r'^has_key\(gettabwinvar\((\d+), (\d+), ""\), "(\w+)"\)$', expr) if not match: raise NotImplementedError(expr) tabnr = int(match.group(1)) winnr = int(match.group(2)) varname = match.group(3) return 0 + (varname in tabpages[tabnr].windows[winnr].vars) elif expr == 'getbufvar("%", "NERDTreeRoot").path.str()': import os assert os.path.basename(current.buffer.name).startswith('NERD_tree_') return '/usr/include' elif expr.startswith('getbufvar('): import re match = re.match(r'^getbufvar\((\d+), ["\'](.+)["\']\)$', expr) if not match: raise NotImplementedError(expr) bufnr = int(match.group(1)) varname = match.group(2) return _emul_getbufvar(bufnr, varname) elif expr == 'tabpagenr()': return current.tabpage.number elif expr == 'tabpagenr("$")': return len(tabpages) elif expr.startswith('tabpagewinnr('): tabnr = int(expr[len('tabpagewinnr('):-1]) return tabpages[tabnr].window.number elif expr.startswith('tabpagebuflist('): import re match = re.match(r'tabpagebuflist\((\d+)\)\[(\d+)\]', expr) tabnr = int(match.group(1)) winnr = int(match.group(2)) + 1 return tabpages[tabnr].windows[winnr].buffer.number elif expr.startswith('gettabwinvar('): import re match = re.match(r'gettabwinvar\((\d+), (\d+), "(\w+)"\)', expr) tabnr = int(match.group(1)) winnr = int(match.group(2)) varname = match.group(3) return tabpages[tabnr].windows[winnr].vars[varname] elif expr.startswith('type(function('): import re match = re.match(r'^type\(function\("([^"]+)"\)\) == 2$', expr) if not match: raise NotImplementedError(expr) return 0 raise NotImplementedError(expr) @_vim def bindeval(expr): if expr == 'g:': return vars elif expr == '{}': return {} elif expr == '[]': return [] import re match = re.compile(r'^function\("([^"\\]+)"\)$').match(expr) if match: return globals()['_emul_' + match.group(1)] else: raise NotImplementedError @_vim @_str_func def _emul_mode(*args): if args and args[0]: return _mode else: return _mode[0] @_vim @_str_func def _emul_getbufvar(bufnr, varname): import re if varname[0] == '&': if bufnr == '%': bufnr = current.buffer.number if bufnr not in buffers: return '' try: return buffers[bufnr].options[varname[1:]] except KeyError: try: return options[varname[1:]] except KeyError: return '' elif re.match('^[a-zA-Z_]+$', varname): if bufnr == '%': bufnr = current.buffer.number if bufnr not in buffers: return '' return buffers[bufnr].vars[varname] raise NotImplementedError @_vim @_str_func def _emul_getwinvar(winnr, varname): return current.tabpage.windows[winnr].vars.get(varname, '') @_vim def _emul_setwinvar(winnr, varname, value): current.tabpage.windows[winnr].vars[varname] = value @_vim def _emul_virtcol(expr): 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 == '.': 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 @_vim @_str_func def _emul_fnamemodify(path, modstring): import os _modifiers = { '~': lambda path: path.replace(os.environ['HOME'].encode('utf-8'), b'~') if path.startswith(os.environ['HOME'].encode('utf-8')) else path, '.': lambda path: (lambda tpath: path if tpath[:3] == b'..' + os.sep.encode() else tpath)(os.path.relpath(path)), 't': lambda path: os.path.basename(path), 'h': lambda path: os.path.dirname(path), } for mods in modstring.split(':')[1:]: path = _modifiers[mods](path) return path @_vim @_str_func def _emul_expand(expr): global _abuf if expr == '': return _abuf or current.buffer.number raise NotImplementedError @_vim def _emul_bufnr(expr): if expr == '$': return _last_bufnr raise NotImplementedError @_vim def _emul_exists(ident): if ident.startswith('g:'): return ident[2:] in vars elif ident.startswith(':'): return 0 raise NotImplementedError @_vim def _emul_line2byte(line): buflines = current.buffer._buf_lines if line == len(buflines) + 1: return sum((len(s) for s in buflines)) + 1 raise NotImplementedError @_vim def _emul_line(expr): cursorline = current.window.cursor[0] + 1 numlines = len(current.buffer._buf_lines) if expr == 'w0': return max(cursorline - 5, 1) if expr == 'w$': return min(cursorline + 5, numlines) raise NotImplementedError @_vim @_str_func def _emul_strtrans(s): # FIXME Do more replaces return s.replace(b'\xFF', b'') @_vim @_str_func def _emul_bufname(bufnr): try: return buffers[bufnr]._name or b'' except KeyError: return b'' _window_id = 0 class _Window(object): def __init__(self, number, buffer=None, cursor=(1, 0), width=80): global _window_id self.cursor = cursor self.width = width self.number = number if buffer: if type(buffer) is _Buffer: self.buffer = buffer else: self.buffer = _Buffer(**buffer) else: self.buffer = _Buffer() _window_id += 1 self._window_id = _window_id self.options = {} self.vars = { 'powerline_window_id': self._window_id, } def __repr__(self): return '' class _Tabpage(object): def __init__(self, number): self.windows = _ObjList(_Window) self.number = number def _new_window(self, **kwargs): self.window = self.windows._new(**kwargs) return self.window def _close_window(self, winnr, open_window=True): curwinnr = self.window.number win = self.windows._pop(winnr) if self.windows and winnr == curwinnr: self.window = self.windows[-1] elif open_window: current.tabpage._new_window() return win def _close(self): global _tabpage while self.windows: self._close_window(1, False) tabpages._pop(self.number) _tabpage = len(tabpages) tabpages = _ObjList(_Tabpage) _abuf = None class _Buffer(object): def __init__(self, name=None): global _last_bufnr _last_bufnr += 1 bufnr = _last_bufnr self.number = bufnr # FIXME Use unicode() for python-3 self.name = name self.vars = {'changedtick': 1} self.options = { 'modified': 0, 'readonly': 0, 'fileformat': 'unix', 'filetype': '', 'buftype': '', 'fileencoding': 'utf-8', 'textwidth': 80, } self._buf_lines = [''] self._undostate = [self._buf_lines[:]] self._undo_written = len(self._undostate) buffers[bufnr] = self @property def name(self): import sys if sys.version_info < (3,): return self._name else: return str(self._name, 'utf-8') if self._name else None @name.setter def name(self, name): if name is None: self._name = None else: import os if type(name) is not bytes: name = name.encode('utf-8') if b':/' in name: self._name = name else: self._name = os.path.abspath(name) def __getitem__(self, line): return self._buf_lines[line] def __setitem__(self, line, value): self.options['modified'] = 1 self.vars['changedtick'] += 1 self._buf_lines[line] = value from copy import copy self._undostate.append(copy(self._buf_lines)) def __setslice__(self, *args): self.options['modified'] = 1 self.vars['changedtick'] += 1 self._buf_lines.__setslice__(*args) from copy import copy self._undostate.append(copy(self._buf_lines)) def __getslice__(self, *args): return self._buf_lines.__getslice__(*args) def __len__(self): return len(self._buf_lines) def __repr__(self): return '' 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 class _Current(object): @property def buffer(self): return self.window.buffer @property def window(self): return self.tabpage.window @property def tabpage(self): return tabpages[_tabpage - 1] current = _Current() _dict = None @_vim def _init(): global _dict if _dict: return _dict _dict = {} for varname, value in globals().items(): if varname[0] != '_': _dict[varname] = value _tabnew() return _dict @_vim def _get_segment_info(): mode_translations = { chr(ord('V') - 0x40): '^V', chr(ord('S') - 0x40): '^S', } mode = _mode mode = mode_translations.get(mode, mode) window = current.window buffer = current.buffer tabpage = current.tabpage return { 'window': window, 'winnr': window.number, 'buffer': buffer, 'bufnr': buffer.number, 'tabpage': tabpage, 'tabnr': tabpage.number, 'window_id': window._window_id, 'mode': mode, 'encoding': options['encoding'], } @_vim def _launch_event(event): pass @_vim def _start_mode(mode): global _mode if mode == 'i': _launch_event('InsertEnter') elif _mode == 'i': _launch_event('InsertLeave') _mode = mode @_vim def _undo(): if len(current.buffer._undostate) == 1: return buffer = current.buffer buffer._undostate.pop(-1) buffer._buf_lines = buffer._undostate[-1] if buffer._undo_written == len(buffer._undostate): buffer.options['modified'] = 0 @_vim def _edit(name=None): if current.buffer.name is None: buffer = current.buffer buffer.name = name else: buffer = _Buffer(name) current.window.buffer = buffer @_vim def _tabnew(name=None): global windows global _tabpage tabpage = tabpages._new() windows = tabpage.windows _tabpage = len(tabpages) _new(name) return tabpage @_vim def _new(name=None): current.tabpage._new_window(buffer={'name': name}) @_vim def _split(): current.tabpage._new_window(buffer=current.buffer) @_vim def _close(winnr, wipe=True): win = current.tabpage._close_window(winnr) if wipe: for w in current.tabpage.windows: if w.buffer.number == win.buffer.number: break else: _bw(win.buffer.number) @_vim def _bw(bufnr=None): bufnr = bufnr or current.buffer.number winnr = 1 for win in current.tabpage.windows: if win.buffer.number == bufnr: _close(winnr, wipe=False) winnr += 1 buffers._pop(bufnr) if not buffers: _Buffer() _b(max(buffers._keys())) @_vim def _b(bufnr): current.window.buffer = buffers[bufnr] @_vim def _set_cursor(line, col): current.window.cursor = (line, col) if _mode == 'n': _launch_event('CursorMoved') elif _mode == 'i': _launch_event('CursorMovedI') @_vim def _get_buffer(): return current.buffer @_vim def _set_bufoption(option, value, bufnr=None): buffers[bufnr or current.buffer.number].options[option] = value if option == 'filetype': _launch_event('FileType') class _WithNewBuffer(object): def __init__(self, func, *args, **kwargs): self.call = lambda: func(*args, **kwargs) def __enter__(self): self.call() self.bufnr = current.buffer.number return _get_segment_info() def __exit__(self, *args): _bw(self.bufnr) @_vim def _set_dict(d, new, setfunc=None): if not setfunc: def setfunc(k, v): d[k] = v old = {} na = [] for k, v in new.items(): try: old[k] = d[k] except KeyError: na.append(k) setfunc(k, v) return old, na class _WithBufOption(object): def __init__(self, **new): self.new = new def __enter__(self): self.buffer = current.buffer self.old = _set_dict(self.buffer.options, self.new, _set_bufoption)[0] def __exit__(self, *args): self.buffer.options.update(self.old) class _WithMode(object): def __init__(self, new): self.new = new def __enter__(self): self.old = _mode _start_mode(self.new) return _get_segment_info() def __exit__(self, *args): _start_mode(self.old) class _WithDict(object): def __init__(self, d, **new): self.new = new self.d = d def __enter__(self): self.old, self.na = _set_dict(self.d, self.new) def __exit__(self, *args): self.d.update(self.old) for k in self.na: self.d.pop(k) class _WithSplit(object): def __enter__(self): _split() def __exit__(self, *args): _close(2, wipe=False) class _WithBufName(object): def __init__(self, new): self.new = new def __enter__(self): import os buffer = current.buffer self.buffer = buffer self.old = buffer.name buffer.name = self.new def __exit__(self, *args): self.buffer.name = self.old class _WithNewTabPage(object): def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs def __enter__(self): self.tab = _tabnew(*self.args, **self.kwargs) def __exit__(self, *args): 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': return _WithNewBuffer(_edit, *args, **kwargs) elif key == 'bufname': return _WithBufName(*args, **kwargs) elif key == 'mode': return _WithMode(*args, **kwargs) elif key == 'bufoptions': return _WithBufOption(**kwargs) elif key == 'options': return _WithDict(options, **kwargs) elif key == 'globals': return _WithDict(vars, **kwargs) elif key == 'wvars': return _WithDict(current.window.vars, **kwargs) elif key == 'environ': return _WithDict(_environ, **kwargs) elif key == 'split': 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): pass