Added support for multi-word snippets.
This commit is contained in:
parent
1167fd6729
commit
4851a8277e
@ -101,20 +101,41 @@ class _SnippetsFileParser(object):
|
||||
|
||||
cdescr = ""
|
||||
coptions = ""
|
||||
cs = ""
|
||||
|
||||
cs = line.split()[1]
|
||||
left = line.find('"')
|
||||
if left != -1:
|
||||
right = line.rfind('"')
|
||||
cdescr = line[left+1:right]
|
||||
coptions = line[right:].strip()
|
||||
snip = line.split()[0]
|
||||
if snip != "snippet":
|
||||
self._error("Expecting 'snippet' not: %s" % snip)
|
||||
|
||||
remain = line[len(snip):].lstrip()
|
||||
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 = ""
|
||||
while self._goto_next_line():
|
||||
line = self._line()
|
||||
if line.rstrip() == "endsnippet":
|
||||
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
|
||||
cv += line
|
||||
else:
|
||||
@ -153,11 +174,23 @@ class Snippet(object):
|
||||
def __repr__(self):
|
||||
return "Snippet(%s,%s,%s)" % (self._t,self._d,self._opts)
|
||||
|
||||
def _word_for_line(self, before):
|
||||
word = ''
|
||||
if len(before) and before.split():
|
||||
word = before.split()[-1]
|
||||
return word
|
||||
def _words_for_line(self, before, num_words=None):
|
||||
words = ''
|
||||
if not len(before):
|
||||
return ''
|
||||
|
||||
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):
|
||||
match = re.search(self._t, trigger)
|
||||
@ -177,68 +210,70 @@ class Snippet(object):
|
||||
if trigger and trigger[-1] in string.whitespace:
|
||||
return False
|
||||
|
||||
word = self._word_for_line(trigger)
|
||||
|
||||
if "b" in self._opts:
|
||||
text_before = trigger.rstrip()[:-len(word)]
|
||||
if text_before.strip(" \t") != '':
|
||||
return False
|
||||
|
||||
words = self._words_for_line(trigger)
|
||||
|
||||
if "r" in self._opts:
|
||||
match = self._re_match(trigger)
|
||||
elif "w" in self._opts:
|
||||
word_len = len(self._t)
|
||||
word_prefix = word[:-word_len]
|
||||
word_suffix = word[-word_len:]
|
||||
match = (word_suffix == self._t)
|
||||
if match and word_prefix:
|
||||
words_len = len(self._t)
|
||||
words_prefix = words[:-words_len]
|
||||
words_suffix = words[-words_len:]
|
||||
match = (words_suffix == self._t)
|
||||
if match and words_prefix:
|
||||
# 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)
|
||||
elif "i" in self._opts:
|
||||
match = word.endswith(self._t)
|
||||
match = words.endswith(self._t)
|
||||
else:
|
||||
match = (word == self._t)
|
||||
match = (words == self._t)
|
||||
|
||||
if match and not self._matched:
|
||||
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
|
||||
|
||||
def could_match(self, trigger):
|
||||
word = self._word_for_line(trigger)
|
||||
|
||||
self._matched = ""
|
||||
if trigger and trigger[-1] in string.whitespace:
|
||||
return False
|
||||
|
||||
if "b" in self._opts:
|
||||
text_before = trigger.rstrip()[:-len(word)]
|
||||
if text_before.strip(" \t") != '':
|
||||
return False
|
||||
words = self._words_for_line(trigger)
|
||||
|
||||
if "r" in self._opts:
|
||||
# Test for full match only
|
||||
match = self._re_match(trigger)
|
||||
elif "w" in self._opts:
|
||||
# Trim non-empty prefix up to word boundary, if present.
|
||||
word_suffix = re.sub(r'^.+\b(.+)$', r'\1', word)
|
||||
match = self._t.startswith(word_suffix)
|
||||
self._matched = word_suffix
|
||||
words_suffix = re.sub(r'^.+\b(.+)$', r'\1', words)
|
||||
match = self._t.startswith(words_suffix)
|
||||
self._matched = words_suffix
|
||||
|
||||
# TODO: list_snippets() function cannot handle partial-trigger
|
||||
# matches yet, so for now fail if we trimmed the prefix.
|
||||
if word_suffix != word:
|
||||
if words_suffix != words:
|
||||
match = False
|
||||
elif "i" in self._opts:
|
||||
# TODO: It is hard to define when a inword snippet could match,
|
||||
# therefore we check only for full-word trigger.
|
||||
match = self._t.startswith(word)
|
||||
match = self._t.startswith(words)
|
||||
else:
|
||||
match = self._t.startswith(word)
|
||||
match = self._t.startswith(words)
|
||||
|
||||
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
|
||||
|
||||
|
152
test.py
152
test.py
@ -647,8 +647,6 @@ i0
|
||||
i1
|
||||
End"""
|
||||
|
||||
# TODO
|
||||
# Different mixes of ts, et, sts, sw
|
||||
class PythonCode_IndentEtSw(_VimTest):
|
||||
def _options_on(self):
|
||||
self.send(":set sw=3\n")
|
||||
@ -1469,6 +1467,74 @@ class SnippetOptions_Regex_Self_TextBefore(_Regex_Self):
|
||||
keys = "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 #
|
||||
@ -1634,6 +1700,88 @@ class ParseSnippets_ClearTwo(_VimTest):
|
||||
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 #
|
||||
###########################################################################
|
||||
|
Loading…
Reference in New Issue
Block a user