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.

132 lines
4.5 KiB
Python

#!/usr/bin/env python
# encoding: utf-8
"""A Snippet instance is an instance of a Snippet Definition. That is, when the
user expands a snippet, a SnippetInstance is created to keep track of the
corresponding TextObjects. The Snippet itself is also a TextObject. """
from UltiSnips.position import Position
import UltiSnips._vim as _vim
from UltiSnips.text_objects._base import EditableTextObject, \
NoneditableTextObject
from UltiSnips.text_objects._parser import parse_text_object
class SnippetInstance(EditableTextObject):
"""See module docstring."""
# pylint:disable=protected-access
def __init__(self, snippet, parent, indent, initial_text,
start, end, visual_content, last_re, globals):
if start is None:
start = Position(0, 0)
if end is None:
end = Position(0, 0)
self.snippet = snippet
self._cts = 0
self.locals = {"match" : last_re}
self.globals = globals
self.visual_content = visual_content
EditableTextObject.__init__(self, parent, start, end, initial_text)
parse_text_object(self, initial_text, indent)
self.update_textobjects()
def replace_initial_text(self):
"""Puts the initial text of all text elements into Vim."""
def _place_initial_text(obj):
"""recurses on the children to do the work."""
obj.overwrite()
if isinstance(obj, EditableTextObject):
for child in obj._children:
_place_initial_text(child)
_place_initial_text(self)
def replay_user_edits(self, cmds):
"""Replay the edits the user has done to keep endings of our
Text objects in sync with reality"""
for cmd in cmds:
self._do_edit(cmd)
def update_textobjects(self):
"""Update the text objects that should change automagically after
the users edits have been replayed. This might also move the Cursor
"""
vc = _VimCursor(self)
done = set()
not_done = set()
def _find_recursive(obj):
"""Finds all text objects and puts them into 'not_done'."""
if isinstance(obj, EditableTextObject):
for child in obj._children:
_find_recursive(child)
not_done.add(obj)
_find_recursive(self)
counter = 10
while (done != not_done) and counter:
# Order matters for python locals!
for obj in sorted(not_done - done):
if obj._update(done):
done.add(obj)
counter -= 1
if not counter:
raise RuntimeError(
"The snippets content did not converge: Check for Cyclic "
"dependencies or random strings in your snippet. You can use "
"'if not snip.c' to make sure to only expand random output "
"once.")
vc.to_vim()
self._del_child(vc)
def select_next_tab(self, backwards=False):
"""Selects the next tabstop or the previous if 'backwards' is True."""
if self._cts is None:
return
if backwards:
cts_bf = self._cts
res = self._get_prev_tab(self._cts)
if res is None:
self._cts = cts_bf
return self._tabstops.get(self._cts, None)
self._cts, ts = res
return ts
else:
res = self._get_next_tab(self._cts)
if res is None:
self._cts = None
return self._tabstops.get(0, None)
else:
self._cts, ts = res
return ts
return self._tabstops[self._cts]
def _get_tabstop(self, requester, no):
# SnippetInstances are completely self contained, therefore, we do not
# need to ask our parent for Tabstops
cached_parent = self._parent
self._parent = None
rv = EditableTextObject._get_tabstop(self, requester, no)
self._parent = cached_parent
return rv
class _VimCursor(NoneditableTextObject):
"""Helper class to keep track of the Vim Cursor when text objects expand
and move."""
def __init__(self, parent):
NoneditableTextObject.__init__(
self, parent, _vim.buf.cursor, _vim.buf.cursor,
tiebreaker=Position(-1, -1))
def to_vim(self):
"""Moves the cursor in the Vim to our position."""
assert self._start == self._end
_vim.buf.cursor = self._start