Added support for multi-word snippets.

This commit is contained in:
rygwdn@gmail.com 2010-08-08 21:32:36 -03:00
parent 1167fd6729
commit 4851a8277e
2 changed files with 225 additions and 42 deletions

View File

@ -101,20 +101,41 @@ class _SnippetsFileParser(object):
cdescr = "" cdescr = ""
coptions = "" coptions = ""
cs = ""
cs = line.split()[1] snip = line.split()[0]
left = line.find('"') if snip != "snippet":
if left != -1: self._error("Expecting 'snippet' not: %s" % snip)
right = line.rfind('"')
cdescr = line[left+1:right] remain = line[len(snip):].lstrip()
coptions = line[right:].strip() words = remain.split()
if len(words) > 2:
# second to last word ends with a quote
if '"' not in words[-1] and words[-2][-1] == '"':
coptions = words[-1]
remain = remain[:-len(coptions) - 1].rstrip()
remain = remain.strip()
if len(remain.split()) > 1 and remain[-1] == '"':
left = remain[:-1].rfind('"')
if left != -1 and left != 0:
cdescr, remain = remain[left:], remain[:left]
cs = remain.strip()
if len(cs.split()) > 1 or "r" in coptions:
if cs[0] != cs[-1]:
self._error("Invalid multiword trigger: '%s'" % cs)
cs = ""
else:
cs = cs[1:-1]
cv = "" cv = ""
while self._goto_next_line(): while self._goto_next_line():
line = self._line() line = self._line()
if line.rstrip() == "endsnippet": if line.rstrip() == "endsnippet":
cv = cv[:-1] # Chop the last newline cv = cv[:-1] # Chop the last newline
self._sm.add_snippet(cs, cv, cdescr, coptions, self._ft) if cs:
self._sm.add_snippet(cs, cv, cdescr, coptions, self._ft)
break break
cv += line cv += line
else: else:
@ -153,11 +174,23 @@ class Snippet(object):
def __repr__(self): def __repr__(self):
return "Snippet(%s,%s,%s)" % (self._t,self._d,self._opts) return "Snippet(%s,%s,%s)" % (self._t,self._d,self._opts)
def _word_for_line(self, before): def _words_for_line(self, before, num_words=None):
word = '' words = ''
if len(before) and before.split(): if not len(before):
word = before.split()[-1] return ''
return word
if num_words is None:
num_words = len(self._t.split())
word_list = before.split()
if len(word_list) <= num_words:
return before.strip()
else:
before_words = before
for i in xrange(-1, -(num_words + 1), -1):
left = before_words.rfind(word_list[i])
before_words = before_words[:left]
return before[len(before_words):].strip()
def _re_match(self, trigger): def _re_match(self, trigger):
match = re.search(self._t, trigger) match = re.search(self._t, trigger)
@ -177,68 +210,70 @@ class Snippet(object):
if trigger and trigger[-1] in string.whitespace: if trigger and trigger[-1] in string.whitespace:
return False return False
word = self._word_for_line(trigger) words = self._words_for_line(trigger)
if "b" in self._opts:
text_before = trigger.rstrip()[:-len(word)]
if text_before.strip(" \t") != '':
return False
if "r" in self._opts: if "r" in self._opts:
match = self._re_match(trigger) match = self._re_match(trigger)
elif "w" in self._opts: elif "w" in self._opts:
word_len = len(self._t) words_len = len(self._t)
word_prefix = word[:-word_len] words_prefix = words[:-words_len]
word_suffix = word[-word_len:] words_suffix = words[-words_len:]
match = (word_suffix == self._t) match = (words_suffix == self._t)
if match and word_prefix: if match and words_prefix:
# Require a word boundary between prefix and suffix. # Require a word boundary between prefix and suffix.
boundaryChars = word_prefix[-1:] + word_suffix[:1] boundaryChars = words_prefix[-1:] + words_suffix[:1]
match = re.match(r'.\b.', boundaryChars) match = re.match(r'.\b.', boundaryChars)
elif "i" in self._opts: elif "i" in self._opts:
match = word.endswith(self._t) match = words.endswith(self._t)
else: else:
match = (word == self._t) match = (words == self._t)
if match and not self._matched: if match and not self._matched:
self._matched = self._t self._matched = self._t
if "b" in self._opts and match:
text_before = trigger.rstrip()[:-len(self._matched)]
if text_before.strip(" \t") != '':
self._matched = ""
return False
return match return match
def could_match(self, trigger): def could_match(self, trigger):
word = self._word_for_line(trigger) self._matched = ""
if trigger and trigger[-1] in string.whitespace: if trigger and trigger[-1] in string.whitespace:
return False return False
if "b" in self._opts: words = self._words_for_line(trigger)
text_before = trigger.rstrip()[:-len(word)]
if text_before.strip(" \t") != '':
return False
if "r" in self._opts: if "r" in self._opts:
# Test for full match only # Test for full match only
match = self._re_match(trigger) match = self._re_match(trigger)
elif "w" in self._opts: elif "w" in self._opts:
# Trim non-empty prefix up to word boundary, if present. # Trim non-empty prefix up to word boundary, if present.
word_suffix = re.sub(r'^.+\b(.+)$', r'\1', word) words_suffix = re.sub(r'^.+\b(.+)$', r'\1', words)
match = self._t.startswith(word_suffix) match = self._t.startswith(words_suffix)
self._matched = word_suffix self._matched = words_suffix
# TODO: list_snippets() function cannot handle partial-trigger # TODO: list_snippets() function cannot handle partial-trigger
# matches yet, so for now fail if we trimmed the prefix. # matches yet, so for now fail if we trimmed the prefix.
if word_suffix != word: if words_suffix != words:
match = False match = False
elif "i" in self._opts: elif "i" in self._opts:
# TODO: It is hard to define when a inword snippet could match, # TODO: It is hard to define when a inword snippet could match,
# therefore we check only for full-word trigger. # therefore we check only for full-word trigger.
match = self._t.startswith(word) match = self._t.startswith(words)
else: else:
match = self._t.startswith(word) match = self._t.startswith(words)
if match and not self._matched: if match and not self._matched:
self._matched = word self._matched = words
if "b" in self._opts and match:
text_before = trigger.rstrip()[:-len(self._matched)]
if text_before.strip(" \t") != '':
self._matched = ""
return False
return match return match

152
test.py
View File

@ -647,8 +647,6 @@ i0
i1 i1
End""" End"""
# TODO
# Different mixes of ts, et, sts, sw
class PythonCode_IndentEtSw(_VimTest): class PythonCode_IndentEtSw(_VimTest):
def _options_on(self): def _options_on(self):
self.send(":set sw=3\n") self.send(":set sw=3\n")
@ -1469,6 +1467,74 @@ class SnippetOptions_Regex_Self_TextBefore(_Regex_Self):
keys = "a." + EX keys = "a." + EX
wanted = "a." + EX wanted = "a." + EX
#######################
# MULTI-WORD SNIPPETS #
#######################
class MultiWordSnippet_Simple(_VimTest):
snippets = ("test me", "Expand me!")
keys = "test me" + EX
wanted = "Expand me!"
class MultiWord_SnippetOptions_OverwriteExisting_ECR(_VimTest):
snippets = (
("test me", "${1:Hallo}", "Types Hallo"),
("test me", "${1:World}", "Types World"),
("test me", "We overwrite", "Overwrite the two", "!"),
)
keys = "test me" + EX
wanted = "We overwrite"
class MultiWord_SnippetOptions_OnlyExpandWhenWSInFront_Expand(_VimTest):
snippets = ("test it", "Expand me!", "", "b")
keys = "test it" + EX
wanted = "Expand me!"
class MultiWord_SnippetOptions_OnlyExpandWhenWSInFront_Expand2(_VimTest):
snippets = ("test it", "Expand me!", "", "b")
keys = " test it" + EX
wanted = " Expand me!"
class MultiWord_SnippetOptions_OnlyExpandWhenWSInFront_DontExpand(_VimTest):
snippets = ("test it", "Expand me!", "", "b")
keys = "a test it" + EX
wanted = "a test it" + EX
class MultiWord_SnippetOptions_OnlyExpandWhenWSInFront_OneWithOneWO(_VimTest):
snippets = (
("test it", "Expand me!", "", "b"),
("test it", "not at beginning", "", ""),
)
keys = "a test it" + EX
wanted = "a not at beginning"
class MultiWord_SnippetOptions_OnlyExpandWhenWSInFront_OneWithOneWOChoose(_VimTest):
snippets = (
("test it", "Expand me!", "", "b"),
("test it", "not at beginning", "", ""),
)
keys = " test it" + EX + "1\n"
wanted = " Expand me!"
class MultiWord_SnippetOptions_ExpandInwordSnippets_SimpleExpand(_VimTest):
snippets = (("test it", "Expand me!", "", "i"), )
keys = "atest it" + EX
wanted = "aExpand me!"
class MultiWord_SnippetOptions_ExpandInwordSnippets_ExpandSingle(_VimTest):
snippets = (("test it", "Expand me!", "", "i"), )
keys = "test it" + EX
wanted = "Expand me!"
class _MultiWord_SnippetOptions_ExpandWordSnippets(_VimTest):
snippets = (("test it", "Expand me!", "", "w"), )
class MultiWord_SnippetOptions_ExpandWordSnippets_NormalExpand(
_MultiWord_SnippetOptions_ExpandWordSnippets):
keys = "test it" + EX
wanted = "Expand me!"
class MultiWord_SnippetOptions_ExpandWordSnippets_NoExpand(
_MultiWord_SnippetOptions_ExpandWordSnippets):
keys = "atest it" + EX
wanted = "atest it" + EX
class MultiWord_SnippetOptions_ExpandWordSnippets_ExpandSuffix(
_MultiWord_SnippetOptions_ExpandWordSnippets):
keys = "a-test it" + EX
wanted = "a-Expand me!"
###################### ######################
# SELECTING MULTIPLE # # SELECTING MULTIPLE #
@ -1634,6 +1700,88 @@ class ParseSnippets_ClearTwo(_VimTest):
wanted = "toclear" + EX + "\n" + "testsnip" + EX wanted = "toclear" + EX + "\n" + "testsnip" + EX
class _ParseSnippets_MultiWord(_VimTest):
snippets_test_file = ("all", "test_file", r"""
snippet /test snip/
This is a test.
endsnippet
snippet !snip test! "Another snippet"
This is another test.
endsnippet
snippet "snippet test" "Another snippet" b
This is yet another test.
endsnippet
""")
class ParseSnippets_MultiWord_Simple(_ParseSnippets_MultiWord):
keys = "test snip" + EX
wanted = "This is a test."
class ParseSnippets_MultiWord_Description(_ParseSnippets_MultiWord):
keys = "snip test" + EX
wanted = "This is another test."
class ParseSnippets_MultiWord_Description_Option(_ParseSnippets_MultiWord):
keys = "snippet test" + EX
wanted = "This is yet another test."
class _ParseSnippets_MultiWord_RE(_VimTest):
snippets_test_file = ("all", "test_file", r"""
snippet /[d-f]+/ "" r
az test
endsnippet
snippet !^(foo|bar)$! "" r
foo-bar test
endsnippet
snippet "(test ?)+" "" r
re-test
endsnippet
""")
class ParseSnippets_MultiWord_RE1(_ParseSnippets_MultiWord_RE):
keys = "abc def" + EX
wanted = "abc az test"
class ParseSnippets_MultiWord_RE2(_ParseSnippets_MultiWord_RE):
keys = "foo" + EX + " bar" + EX + "\nbar" + EX
wanted = "foo-bar test bar\t\nfoo-bar test"
class ParseSnippets_MultiWord_RE3(_ParseSnippets_MultiWord_RE):
keys = "test test test" + EX
wanted = "re-test"
class ParseSnippets_MultiWord_Quotes(_VimTest):
snippets_test_file = ("all", "test_file", r"""
snippet "test snip"
This is a test.
endsnippet
""")
keys = "test snip" + EX
wanted = "This is a test."
class ParseSnippets_MultiWord_NoContainer(_VimTest):
snippets_test_file = ("all", "test_file", r"""
snippet test snip
This is a test.
endsnippet
""")
keys = "test snip" + EX
wanted = keys
expected_error = dedent("""
UltiSnips: Invalid multiword trigger: 'test snip' in test_file(2)
""").strip()
class ParseSnippets_MultiWord_UnmatchedContainer(_VimTest):
snippets_test_file = ("all", "test_file", r"""
snippet !inv snip/
This is a test.
endsnippet
""")
keys = "inv snip" + EX
wanted = keys
expected_error = dedent("""
UltiSnips: Invalid multiword trigger: '!inv snip/' in test_file(2)
""").strip()
########################################################################### ###########################################################################
# END OF TEST # # END OF TEST #
########################################################################### ###########################################################################