Added support for multi-word snippets.
This commit is contained in:
parent
1167fd6729
commit
4851a8277e
@ -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
152
test.py
@ -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 #
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
Loading…
Reference in New Issue
Block a user