From 9608346e776d69dd06b8be3a92edac147646e4e3 Mon Sep 17 00:00:00 2001 From: Holger Rapp Date: Sun, 24 Jul 2011 18:29:31 +0200 Subject: [PATCH] TextObjects now accept a single token as initializer --- plugin/UltiSnips/Lexer.py | 33 ++++++------ plugin/UltiSnips/TextObjects.py | 93 +++++++++++++++++---------------- 2 files changed, 64 insertions(+), 62 deletions(-) diff --git a/plugin/UltiSnips/Lexer.py b/plugin/UltiSnips/Lexer.py index aa44a2b..081de6b 100644 --- a/plugin/UltiSnips/Lexer.py +++ b/plugin/UltiSnips/Lexer.py @@ -103,6 +103,7 @@ def _parse_till_unescaped_char(stream, char): # Tokens {{{ class Token(object): def __init__(self, gen, indent): + self.initial_text = "" self.start = gen.pos self._parse(gen, indent) self.end = gen.pos @@ -123,11 +124,11 @@ class TabStopToken(Token): if stream.peek() is ":": stream.next() - self.default_text = _parse_till_closing_brace(stream) + self.initial_text = _parse_till_closing_brace(stream) def __repr__(self): return "TabStopToken(%r,%r,%r,%r)" % ( - self.start, self.end, self.no, self.default_text + self.start, self.end, self.no, self.initial_text ) class TransformationToken(Token): @@ -164,6 +165,7 @@ class MirrorToken(Token): return klass.CHECK.match(stream.peek(10)) != None def _parse(self, stream, indent): + # TODO: why not parse number? self.no = "" stream.next() # $ while not stream.exhausted and stream.peek() in string.digits: @@ -184,13 +186,11 @@ class EscapeCharToken(Token): def _parse(self, stream, indent): stream.next() # \ - self.char = stream.next() + self.initial_text = stream.next() - - # TODO: get rid of those __repr__ maybe def __repr__(self): return "EscapeCharToken(%r,%r,%r)" % ( - self.start, self.end, self.char + self.start, self.end, self.initial_text ) class ShellCodeToken(Token): @@ -200,12 +200,12 @@ class ShellCodeToken(Token): def _parse(self, stream, indent): stream.next() # ` - self.content = _parse_till_unescaped_char(stream, '`') + self.code = _parse_till_unescaped_char(stream, '`') # TODO: get rid of those __repr__ maybe def __repr__(self): return "ShellCodeToken(%r,%r,%r)" % ( - self.start, self.end, self.content + self.start, self.end, self.code ) # TODO: identical to VimLCodeToken @@ -222,25 +222,25 @@ class PythonCodeToken(Token): if stream.peek() in '\t ': stream.next() - content = _parse_till_unescaped_char(stream, '`') + code = _parse_till_unescaped_char(stream, '`') # TODO: stupid to pass the indent down even if only python # needs it. Stupid to indent beforehand. # Strip the indent if any if len(indent): - lines = content.splitlines() - self.content = lines[0] + '\n' - self.content += '\n'.join([l[len(indent):] + lines = code.splitlines() + self.code = lines[0] + '\n' + self.code += '\n'.join([l[len(indent):] for l in lines[1:]]) else: - self.content = content + self.code = code self.indent = indent # TODO: get rid of those __repr__ maybe def __repr__(self): return "PythonCodeToken(%r,%r,%r)" % ( - self.start, self.end, self.content + self.start, self.end, self.code ) @@ -254,12 +254,11 @@ class VimLCodeToken(Token): def _parse(self, stream, indent): for i in range(4): stream.next() # `!v - self.content = _parse_till_unescaped_char(stream, '`') + self.code = _parse_till_unescaped_char(stream, '`') - # TODO: get rid of those __repr__ maybe def __repr__(self): return "VimLCodeToken(%r,%r,%r)" % ( - self.start, self.end, self.content + self.start, self.end, self.code ) # End: Tokens }}} diff --git a/plugin/UltiSnips/TextObjects.py b/plugin/UltiSnips/TextObjects.py index 559af5d..054b50f 100644 --- a/plugin/UltiSnips/TextObjects.py +++ b/plugin/UltiSnips/TextObjects.py @@ -121,26 +121,29 @@ class TextObject(object): This base class represents any object in the text that has a span in any ways """ - def __init__(self, parent, start, end, initial_text): - self._start = start - self._end = end - + def __init__(self, parent, token, end = None, initial_text = ""): self._parent = parent + if end is not None: # Took 4 arguments + self._start = token + self._end = end + self._current_text = TextBuffer(initial_text) + else: # Initialize from token + self._start = token.start + self._end = token.end + self._current_text = TextBuffer(token.initial_text) + self._childs = [] self._tabstops = {} if parent is not None: parent._add_child(self) - self._current_text = TextBuffer(initial_text) - self._cts = 0 def __cmp__(self, other): return cmp(self._start, other._start) - ############## # PROPERTIES # ############## @@ -268,7 +271,6 @@ class TextObject(object): return max(posible_sol) - ############################### # Private/Protected functions # ############################### @@ -328,7 +330,6 @@ class EscapedChar(TextObject): """ pass - class StartMarker(TextObject): """ This class only remembers it's starting position. It is used to @@ -337,15 +338,15 @@ class StartMarker(TextObject): """ def __init__(self, start): end = Position(start.line, start.col) - TextObject.__init__(self, None, start, end, "") + TextObject.__init__(self, None, start, end) class Mirror(TextObject): """ A Mirror object mirrors a TabStop that is, text is repeated here """ - def __init__(self, parent, ts, start, end): - TextObject.__init__(self, parent, start, end, "") + def __init__(self, parent, ts, token): + TextObject.__init__(self, parent, token) self._ts = ts @@ -357,19 +358,19 @@ class Mirror(TextObject): class Transformation(Mirror): - def __init__(self, parent, ts, start, end, s, r, options): - Mirror.__init__(self, parent, ts, start, end) + def __init__(self, parent, ts, token): + Mirror.__init__(self, parent, ts, token) flags = 0 self._match_this_many = 1 - if options: - if "g" in options: + if token.options: + if "g" in token.options: self._match_this_many = 0 - if "i" in options: + if "i" in token.options: flags |= re.IGNORECASE - self._find = re.compile(s, flags | re.DOTALL) - self._replace = _CleverReplace(r) + self._find = re.compile(token.search, flags | re.DOTALL) + self._replace = _CleverReplace(token.replace) def _do_update(self): t = self._ts.current_text @@ -380,9 +381,8 @@ class Transformation(Mirror): return "Transformation(%s -> %s)" % (self._start, self._end) class ShellCode(TextObject): - def __init__(self, parent, start, end, code): - - code = code.replace("\\`", "`") + def __init__(self, parent, token): + code = token.code.replace("\\`", "`") # Write the code to a temporary file handle, path = tempfile.mkstemp(text=True) @@ -401,16 +401,17 @@ class ShellCode(TextObject): os.unlink(path) - TextObject.__init__(self, parent, start, end, output) + token.initial_text = output + TextObject.__init__(self, parent, token) def __repr__(self): return "ShellCode(%s -> %s)" % (self._start, self._end) class VimLCode(TextObject): - def __init__(self, parent, start, end, code): - self._code = code.replace("\\`", "`").strip() + def __init__(self, parent, token): + self._code = token.code.replace("\\`", "`").strip() - TextObject.__init__(self, parent, start, end, "") + TextObject.__init__(self, parent, token) def _do_update(self): self.current_text = str(vim.eval(self._code)) @@ -567,9 +568,9 @@ class SnippetUtil(object): class PythonCode(TextObject): - def __init__(self, parent, start, end, code, indent=""): + def __init__(self, parent, token): - code = code.replace("\\`", "`") + code = token.code.replace("\\`", "`") # Find our containing snippet for snippet local data snippet = parent @@ -578,7 +579,7 @@ class PythonCode(TextObject): snippet = snippet._parent except AttributeError: snippet = None - self._snip = SnippetUtil(indent) + self._snip = SnippetUtil(token.indent) self._locals = snippet.locals self._globals = {} @@ -588,7 +589,7 @@ class PythonCode(TextObject): # Add Some convenience to the code self._code = "import re, os, vim, string, random\n" + code - TextObject.__init__(self, parent, start, end, "") + TextObject.__init__(self, parent, token) def _do_update(self): @@ -626,9 +627,13 @@ class TabStop(TextObject): This is the most important TextObject. A TabStop is were the cursor comes to rest when the user taps through the Snippet. """ - def __init__(self, no, parent, start, end, default_text = ""): - TextObject.__init__(self, parent, start, end, default_text) - self._no = no + def __init__(self, parent, token, start = None, end = None): + if start is not None: + self._no = token + TextObject.__init__(self, parent, start, end) + else: + TextObject.__init__(self, parent, token) + self._no = token.no def no(self): return self._no @@ -675,7 +680,7 @@ class SnippetInstance(TextObject): col -= self.start.col start = Position(delta.line, col) end = Position(delta.line, col) - ts = TabStop(0, self, start, end, "") + ts = TabStop(self, 0, start, end) self._add_tabstop(0,ts) self.update() @@ -734,6 +739,7 @@ class _TOParser(object): self._indent = indent self.current_to = parent self.text = text + debug("text: %s" % (text)) def parse(self): seen_ts = {} @@ -750,18 +756,18 @@ class _TOParser(object): # TODO: maybe we can get rid of _get_tabstop and _add_tabstop if token.no not in seen_ts: debug("token.start: %s, token.end: %s" % (token.start, token.end)) - ts = TabStop(token.no, parent, token.start, token.end) + ts = TabStop(parent, token) seen_ts[token.no] = ts parent._add_tabstop(token.no,ts) else: - Mirror(parent, seen_ts[token.no], token.start, token.end) + Mirror(parent, seen_ts[token.no], token) # TODO: third phase: associate tabstops with Transformations for parent, token in tokens: if isinstance(token, TransformationToken): if token.no not in seen_ts: raise RuntimeError("Tabstop %i is not known" % t._ts) - Transformation(parent, seen_ts[token.no], token.start, token.end, token.search, token.replace, token.options) + Transformation(parent, seen_ts[token.no], token) # TODO: check if all associations have been done properly. Also add a testcase for this! def _parse(self, all_tokens, seen_ts): @@ -772,21 +778,18 @@ class _TOParser(object): all_tokens.append((self.current_to, token)) if isinstance(token, TabStopToken): - # TODO: could also take the token directly - debug("token.start: %s, token.end: %s" % (token.start, token.end)) - ts = TabStop(token.no, self.current_to, - token.start, token.end, token.default_text) + ts = TabStop(self.current_to, token) seen_ts[token.no] = ts self.current_to._add_tabstop(token.no,ts) k = _TOParser(ts, ts.current_text, self._indent) k._parse(all_tokens, seen_ts) elif isinstance(token, EscapeCharToken): - EscapedChar(self.current_to, token.start, token.end, token.char) + EscapedChar(self.current_to, token) elif isinstance(token, ShellCodeToken): - ShellCode(self.current_to, token.start, token.end, token.content) + ShellCode(self.current_to, token) elif isinstance(token, PythonCodeToken): - PythonCode(self.current_to, token.start, token.end, token.content, token.indent) + PythonCode(self.current_to, token) elif isinstance(token, VimLCodeToken): - VimLCode(self.current_to, token.start, token.end, token.content) + VimLCode(self.current_to, token)