You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
6.1 KiB
Python
210 lines
6.1 KiB
Python
#!/usr/bin/env python
|
|
# encoding: utf-8
|
|
|
|
"""Implements `!p ` interpolation."""
|
|
|
|
import os
|
|
from collections import namedtuple
|
|
|
|
from UltiSnips.compatibility import as_unicode
|
|
from UltiSnips.indent_util import IndentUtil
|
|
from UltiSnips.text_objects._base import NoneditableTextObject
|
|
import UltiSnips._vim as _vim
|
|
|
|
|
|
class _Tabs(object):
|
|
"""Allows access to tabstop content via t[] inside of python code."""
|
|
def __init__(self, to):
|
|
self._to = to
|
|
|
|
def __getitem__(self, no):
|
|
ts = self._to._get_tabstop(self._to, int(no)) # pylint:disable=protected-access
|
|
if ts is None:
|
|
return ""
|
|
return ts.current_text
|
|
|
|
_VisualContent = namedtuple('_VisualContent', ['mode', 'text'])
|
|
|
|
class SnippetUtil(object):
|
|
"""Provides easy access to indentation, etc. This is the 'snip' object in
|
|
python code."""
|
|
|
|
def __init__(self, initial_indent, vmode, vtext):
|
|
self._ind = IndentUtil()
|
|
self._visual = _VisualContent(vmode, vtext)
|
|
self._initial_indent = self._ind.indent_to_spaces(initial_indent)
|
|
self._reset("")
|
|
|
|
def _reset(self, cur):
|
|
"""Gets the snippet ready for another update.
|
|
:cur: the new value for c.
|
|
"""
|
|
self._ind.reset()
|
|
self._cur = cur
|
|
self._rv = ""
|
|
self._changed = False
|
|
self.reset_indent()
|
|
|
|
def shift(self, amount=1):
|
|
"""Shifts the indentation level.
|
|
Note that this uses the shiftwidth because thats what code
|
|
formatters use.
|
|
|
|
:amount: the amount by which to shift.
|
|
"""
|
|
self.indent += " " * self._ind.shiftwidth * amount
|
|
|
|
def unshift(self, amount=1):
|
|
"""Unshift the indentation level.
|
|
Note that this uses the shiftwidth because thats what code
|
|
formatters use.
|
|
|
|
:amount: the amount by which to unshift.
|
|
"""
|
|
by = -self._ind.shiftwidth * amount
|
|
try:
|
|
self.indent = self.indent[:by]
|
|
except IndexError:
|
|
self.indent = ""
|
|
|
|
def mkline(self, line="", indent=None):
|
|
"""Creates a properly set up line.
|
|
|
|
:line: the text to add
|
|
:indent: the indentation to have at the beginning
|
|
if None, it uses the default amount
|
|
"""
|
|
if indent is None:
|
|
indent = self.indent
|
|
# this deals with the fact that the first line is
|
|
# already properly indented
|
|
if '\n' not in self._rv:
|
|
try:
|
|
indent = indent[len(self._initial_indent):]
|
|
except IndexError:
|
|
indent = ""
|
|
indent = self._ind.spaces_to_indent(indent)
|
|
|
|
return indent + line
|
|
|
|
def reset_indent(self):
|
|
"""Clears the indentation."""
|
|
self.indent = self._initial_indent
|
|
|
|
# Utility methods
|
|
@property
|
|
def fn(self): # pylint:disable=no-self-use,invalid-name
|
|
"""The filename."""
|
|
return _vim.eval('expand("%:t")') or ""
|
|
|
|
@property
|
|
def basename(self): # pylint:disable=no-self-use
|
|
"""The filename without extension."""
|
|
return _vim.eval('expand("%:t:r")') or ""
|
|
|
|
@property
|
|
def ft(self): # pylint:disable=invalid-name
|
|
"""The filetype."""
|
|
return self.opt("&filetype", "")
|
|
|
|
@property
|
|
def rv(self): # pylint:disable=invalid-name
|
|
"""The return value. The text to insert at the location of the
|
|
placeholder."""
|
|
return self._rv
|
|
|
|
@rv.setter
|
|
def rv(self, value): # pylint:disable=invalid-name
|
|
"""See getter."""
|
|
self._changed = True
|
|
self._rv = value
|
|
|
|
@property
|
|
def _rv_changed(self):
|
|
"""True if rv has changed."""
|
|
return self._changed
|
|
|
|
@property
|
|
def c(self): # pylint:disable=invalid-name
|
|
"""The current text of the placeholder."""
|
|
return self._cur
|
|
|
|
@property
|
|
def v(self): # pylint:disable=invalid-name
|
|
"""Content of visual expansions"""
|
|
return self._visual
|
|
|
|
def opt(self, option, default=None): # pylint:disable=no-self-use
|
|
"""Gets a Vim variable."""
|
|
if _vim.eval("exists('%s')" % option) == "1":
|
|
try:
|
|
return _vim.eval(option)
|
|
except _vim.error:
|
|
pass
|
|
return default
|
|
|
|
def __add__(self, value):
|
|
"""Appends the given line to rv using mkline."""
|
|
self.rv += '\n' # pylint:disable=invalid-name
|
|
self.rv += self.mkline(value)
|
|
return self
|
|
|
|
def __lshift__(self, other):
|
|
"""Same as unshift."""
|
|
self.unshift(other)
|
|
|
|
def __rshift__(self, other):
|
|
"""Same as shift."""
|
|
self.shift(other)
|
|
|
|
|
|
class PythonCode(NoneditableTextObject):
|
|
"""See module docstring."""
|
|
|
|
def __init__(self, parent, token):
|
|
|
|
# Find our containing snippet for snippet local data
|
|
snippet = parent
|
|
while snippet:
|
|
try:
|
|
self._locals = snippet.locals
|
|
text = snippet.visual_content.text
|
|
mode = snippet.visual_content.mode
|
|
break
|
|
except AttributeError:
|
|
snippet = snippet._parent # pylint:disable=protected-access
|
|
self._snip = SnippetUtil(token.indent, mode, text)
|
|
|
|
self._codes = ((
|
|
"import re, os, vim, string, random",
|
|
"\n".join(snippet.globals.get("!p", [])).replace("\r\n", "\n"),
|
|
token.code.replace("\\`", "`")
|
|
))
|
|
NoneditableTextObject.__init__(self, parent, token)
|
|
|
|
def _update(self, done):
|
|
path = _vim.eval('expand("%")') or ""
|
|
ct = self.current_text
|
|
self._locals.update({
|
|
't': _Tabs(self._parent),
|
|
'fn': os.path.basename(path),
|
|
'path': path,
|
|
'cur': ct,
|
|
'res': ct,
|
|
'snip': self._snip,
|
|
})
|
|
self._snip._reset(ct) # pylint:disable=protected-access
|
|
|
|
for code in self._codes:
|
|
exec(code, self._locals) # pylint:disable=exec-used
|
|
|
|
rv = as_unicode(
|
|
self._snip.rv if self._snip._rv_changed # pylint:disable=protected-access
|
|
else as_unicode(self._locals['res'])
|
|
)
|
|
|
|
if ct != rv:
|
|
self.overwrite(rv)
|
|
return False
|
|
return True
|