diff --git a/PySnipEmu.py b/PySnipEmu.py index 78aa7e7..5986f8c 100644 --- a/PySnipEmu.py +++ b/PySnipEmu.py @@ -120,10 +120,10 @@ class TextObject(object): This base class represents any object in the text that has a span in any ways """ - _TABSTOP = re.compile(r'''(?xms) -(?:\${(\d+):(.*?)})| # A simple tabstop with default value -(?:\$(\d+)) # A mirror or a tabstop without default value. -''') + # A simple tabstop with default value + _TABSTOP = re.compile(r'''\${(\d+):(.*?)}''') + # A mirror or a tabstop without default value. + _MIRROR_OR_TS = re.compile(r'\$(\d+)') def __init__(self, parent, start, end, initial_text): self._start = start @@ -137,13 +137,23 @@ class TextObject(object): if parent is not None: parent.add_child(self) - self._current_text = TextBuffer(self._parse(initial_text)) + self._has_parsed = False + self._current_text = initial_text + + debug("New text_object: %s" % self) + if len(self._children): + debug(" Children:") + for c in self._children: + debug(" %s" % c) def _do_update(self): pass def update(self, change_buffer, indend = ""): + if not self._has_parsed: + self._current_text = TextBuffer(self._parse(self._current_text)) + debug("%sUpdating %s" % (indend, self)) for c in self._children: debug("%s Updating Child%s" % (indend, c)) @@ -180,7 +190,6 @@ class TextObject(object): if m == obj: continue - debug("Considering %s for moving!" % m) delta_lines = 0 delta_cols_begin = 0 @@ -196,8 +205,6 @@ class TextObject(object): if m.start.line == m.end.line: delta_cols_end = cols - debug("delta_lines: %i, delta_cols_begin: %i, delta_cols_end: %i" % - (delta_lines, delta_cols_begin, delta_cols_end)) m.start.line += delta_lines m.end.line += delta_lines m.start.col += delta_cols_begin @@ -207,7 +214,7 @@ class TextObject(object): def _get_pos(s, pos): line_idx = s[:pos].count('\n') line_start = s[:pos].rfind('\n') + 1 - start_in_line = start_pos - line_start + start_in_line = pos - line_start return Position(line_idx, start_in_line) return _get_pos(val, start_pos), _get_pos(val, end_pos) @@ -220,14 +227,10 @@ class TextObject(object): start_pos, end_pos = m.span() start, end = self._get_start_end(val,start_pos,end_pos) - val = val[:start_pos] + val[end_pos:] - - ts = TabStop(self, start, def_text) + ts = TabStop(self, start, end, def_text) self.add_tabstop(no,ts) - return val - def _get_tabstop(self,no): if no in self._tabstops: return self._tabstops[no] @@ -235,35 +238,41 @@ class TextObject(object): return self._parent._get_tabstop(no) def _handle_ts_or_mirror(self, m, val): - no = int(m.group(3)) + no = int(m.group(1)) start_pos, end_pos = m.span() start, end = self._get_start_end(val,start_pos,end_pos) ts = self._get_tabstop(no) if ts is not None: - m = Mirror(self, ts, start) + m = Mirror(self, ts, start, end) else: - ts = TabStop(self, start) + ts = TabStop(self, start, end) self.add_tabstop(no,ts) - val = val[:start_pos] + val[end_pos:] - - return val def add_tabstop(self,no, ts): self._tabstops[no] = ts def _parse(self, val): - while 1: - m = self._TABSTOP.search(val) + self._has_parsed = True + + if not len(val): + return val + + for m in self._TABSTOP.finditer(val): + self._handle_tabstop(m,val) + # Replace the whole definition with spaces + s, e = m.span() + val = val[:s] + (e-s)*" " + val[e:] + debug("Handled a tabstop: %s" % repr(val)) + + for m in self._MIRROR_OR_TS.finditer(val): + self._handle_ts_or_mirror(m,val) + # Replace the whole definition with spaces + s, e = m.span() + val = val[:s] + (e-s)*" " + val[e:] + debug("Handled a mirror or ts: %s" % repr(val)) - if m is not None: - if m.group(1) is not None: # ${1:hallo} - val = self._handle_tabstop(m,val) - elif m.group(3) is not None: # $1 - val = self._handle_ts_or_mirror(m,val) - else: - break debug("End of parse: %s" % repr(val)) return val @@ -312,8 +321,7 @@ class Mirror(ChangeableText): """ A Mirror object mirrors a TabStop that is, text is repeated here """ - def __init__(self, parent, ts, start): - end = start + (ts.end - ts.start) + def __init__(self, parent, ts, start, end): ChangeableText.__init__(self, parent, start, end) self._ts = ts @@ -331,16 +339,15 @@ class TabStop(ChangeableText): 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, parent, start, default_text = ""): - end = Position(start.line,start.col) + def __init__(self, parent, start, end, default_text = ""): ChangeableText.__init__(self, parent, start, end, default_text) def __repr__(self): return "TabStop(%s -> %s, %s)" % (self._start, self._end, repr(self._current_text)) - def select(self): - lineno, col = self._parent.start.line, self._parent.start.col + def select(self, start): + lineno, col = start.line, start.col newline = lineno + self._start.line newcol = self._start.col @@ -350,18 +357,23 @@ class TabStop(ChangeableText): vim.current.window.cursor = newline + 1, newcol - # Select the word - # Depending on the current mode and position, we - # might need to move escape out of the mode and this - # will move our cursor one left if len(self.current_text) > 0: + # Select the word + # Depending on the current mode and position, we + # might need to move escape out of the mode and this + # will move our cursor one left if newcol != 0 and vim.eval("mode()") == 'i': move_one_right = "l" else: move_one_right = "" - vim.command(r'call feedkeys("\%sv%il\")' - % (move_one_right, len(self.current_text)-1)) + if len(self.current_text) == 1: + do_select = "" + else: + do_select = "%il" % (len(self.current_text)-1) + + vim.command(r'call feedkeys("\%sv%s\")' % + (move_one_right, do_select)) class SnippetInstance(TextObject): @@ -380,6 +392,8 @@ class SnippetInstance(TextObject): self._vb = VimBuffer(text_before, text_after) + self._current_text = TextBuffer(self._parse(initial_text)) + debug("Before update!"); self.update(self._vb) debug("After update!"); @@ -417,7 +431,7 @@ class SnippetInstance(TextObject): ts = self._tabstops[self._cts] - ts.select() + ts.select(self._start) self._tab_selected = True return True diff --git a/test.py b/test.py index 300b8fe..d38ae3f 100755 --- a/test.py +++ b/test.py @@ -167,8 +167,17 @@ class TabStopNoReplace_ExceptCorrectResult(_VimTest): self.type("echo\t") def runTest(self): self.check_output() +# TODO: multiline tabstops, maybe? + +class TabStopWithOneChar_ExceptCorrectResult(_VimTest): + snippets = ("hallo", "nothing ${1:i} hups") + wanted = "nothing ship hups" + def cmd(self): + self.type("hallo\tship") + def runTest(self): self.check_output() + class TabStopTestJumping_ExceptCorrectResult(_VimTest): - snippets = ("hallo", "hallo ${0:End} mitte ${1:Beginning}") + snippets = ("hallo", "hallo ${2:End} mitte ${1:Beginning}") wanted = "hallo TestHi mitte Beginning" def cmd(self): self.type("hallo\t\tTest\tHi") @@ -254,6 +263,19 @@ class TextTabStopAllSurrounded_ExceptCorrectResult(_VimTest): self.type("test\thallo welt") def runTest(self): self.check_output() +class MirrorBeforeTabstopLeave_ExceptCorrectResult(_VimTest): + snippets = ("test", "$1 ${1:this is it} $1") + wanted = "this is it this is it this is it" + def cmd(self): + self.type("test\t") + def runTest(self): self.check_output() +class MirrorBeforeTabstopOverwrite_ExceptCorrectResult(_VimTest): + snippets = ("test", "$1 ${1:this is it} $1") + wanted = "a a a" + def cmd(self): + self.type("test\ta") + def runTest(self): self.check_output() + class TextTabStopSimpleMirrorMultiline_ExceptCorrectResult(_VimTest): snippets = ("test", "$1\n$1") wanted = "hallo\nhallo" @@ -286,7 +308,6 @@ class MultilineTabStopSimpleMirrorDeleteInLine_ExceptCorrectResult(_VimTest): self.type("test\thallo Du\nHi\b\bAch Blah") def runTest(self): self.check_output() - class SimpleMirrorDelete_ExceptCorrectResult(_VimTest): snippets = ("test", "$1\n$1") wanted = "hal\nhal" @@ -379,21 +400,36 @@ class TabstopWithMirrorInDefaultOverwrite_ExceptCorrectResult(_VimTest): self.type("test\tstdin\toverwritten") def runTest(self): self.check_output() +class MirrorRealLifeExample_ExceptCorrectResult(_VimTest): + snippets = ( + ("for", "for(size_t ${2:i} = 0; $2 < ${1:count}; ${3:++$2})" \ + "\n{\n\t${0:/* code */}\n}"), + ) + wanted = """for(size_t a_variable = 0; a_variable < 100; a_variable *= 2) +{ +\t// do nothing +}""" + def cmd(self): + self.type("for\t100\tavar\b\b\b\ba_variable\ta_variable *= 2" + "\t// do nothing") + def runTest(self): self.check_output() -# class MirrorMoreInvolved_ExceptCorrectResult(_VimTest): -# snippets = ( -# ("for", "for(size_t ${2:i} = 0; $2 < ${1:count}; ${3:++$2})\n{\n\t${0:/* code */}\n}"), -# ) -# -# def cmd(self): -# self.type("for\t") -# -# def runTest(self): -# self.assertEqual(self.output,"hallo Du Nase na") # TODO: recursive expansion -# TODO: mirrors in default expansion -# TODO: $1 ${1:This is the tabstop} + +################### +# TRANSFORMATIONS # +################### +class Transformation_SimpleCase_ExceptCorrectResult(_VimTest): + snippets = ("test", "$1 ${1/foo/batzl/}") + wanted = "hallo foo boy hallo batzl boy" + def cmd(self): + self.type("test\t","hallo foo boy") +class Transformation_SimpleCaseNoTransform_ExceptCorrectResult(_VimTest): + snippets = ("test", "$1 ${1/foo/batzl/}") + wanted = "hallo hallo" + def cmd(self): + self.type("test\t","hallo") if __name__ == '__main__': import sys