Merge pull request #446 from seletskiy/context-snippets
New feature: Context-aware snippets
This commit is contained in:
commit
d6098193ef
@ -36,6 +36,7 @@ UltiSnips *snippet* *snippets* *UltiSnips*
|
|||||||
4.7.1 Replacement String |UltiSnips-replacement-string|
|
4.7.1 Replacement String |UltiSnips-replacement-string|
|
||||||
4.7.2 Demos |UltiSnips-demos|
|
4.7.2 Demos |UltiSnips-demos|
|
||||||
4.8 Clearing snippets |UltiSnips-clearing-snippets|
|
4.8 Clearing snippets |UltiSnips-clearing-snippets|
|
||||||
|
4.9 Context snippets |UltiSnips-context-snippets|
|
||||||
5. UltiSnips and Other Plugins |UltiSnips-other-plugins|
|
5. UltiSnips and Other Plugins |UltiSnips-other-plugins|
|
||||||
5.1 Existing Integrations |UltiSnips-integrations|
|
5.1 Existing Integrations |UltiSnips-integrations|
|
||||||
5.2 Extending UltiSnips |UltiSnips-extending|
|
5.2 Extending UltiSnips |UltiSnips-extending|
|
||||||
@ -653,6 +654,11 @@ The options currently supported are: >
|
|||||||
Without this option empty lines in snippets definition will have
|
Without this option empty lines in snippets definition will have
|
||||||
indentation too.
|
indentation too.
|
||||||
|
|
||||||
|
e Context snippets - With this option expansion of snippet can be
|
||||||
|
controlled not only by previous characters in line, but by any given
|
||||||
|
python expression. This option can be specified along with other
|
||||||
|
options, like 'b'. See |UltiSnips-context-snippets| for more info.
|
||||||
|
|
||||||
The end line is the 'endsnippet' keyword on a line by itself. >
|
The end line is the 'endsnippet' keyword on a line by itself. >
|
||||||
|
|
||||||
endsnippet
|
endsnippet
|
||||||
@ -813,11 +819,12 @@ output is ignored.
|
|||||||
|
|
||||||
The variables automatically defined in python code are: >
|
The variables automatically defined in python code are: >
|
||||||
|
|
||||||
fn - The current filename
|
fn - The current filename
|
||||||
path - The complete path to the current file
|
path - The complete path to the current file
|
||||||
t - The values of the placeholders, t[1] is the text of ${1}, and so on
|
t - The values of the placeholders, t[1] is the text of ${1}, etc.
|
||||||
snip - UltiSnips.TextObjects.SnippetUtil object instance. Has methods that
|
snip - UltiSnips.TextObjects.SnippetUtil object instance. Has methods
|
||||||
simplify indentation handling.
|
that simplify indentation handling.
|
||||||
|
context - Result of context condition. See |UltiSnips-context-snippets|.
|
||||||
|
|
||||||
The 'snip' object provides the following methods: >
|
The 'snip' object provides the following methods: >
|
||||||
|
|
||||||
@ -1294,6 +1301,95 @@ clearsnippets trigger1 trigger2
|
|||||||
------------------- SNAP -------------------
|
------------------- SNAP -------------------
|
||||||
|
|
||||||
|
|
||||||
|
4.9 Context snippets *UltiSnips-context-snippets*
|
||||||
|
|
||||||
|
Context snippets can be enabled by using 'e' option in snippet definition.
|
||||||
|
|
||||||
|
In that case snippet should be defined using this syntax: >
|
||||||
|
|
||||||
|
snippet tab_trigger "description" "expression" options
|
||||||
|
|
||||||
|
The 'expression' can be any python expression. If 'expression' evaluates to
|
||||||
|
'True', then this snippet will be chosen for expansion. The 'expression' must
|
||||||
|
be wrapped with double-quotes.
|
||||||
|
|
||||||
|
The following python modules are automatically imported into the scope before
|
||||||
|
'expression' is evaluated: 're', 'os', 'vim', 'string', 'random'.
|
||||||
|
|
||||||
|
Also, the following variables are defined:
|
||||||
|
'window' - alias for 'vim.current.window'
|
||||||
|
'buffer' - alias for 'vim.current.window.buffer'
|
||||||
|
'cursor' - alias for 'vim.current.cursor'
|
||||||
|
'line' and 'column' - aliases for cursor position
|
||||||
|
|
||||||
|
Keep in mind, that lines in vim numbered from 1, and lists in python starts
|
||||||
|
from 0, so to access the current line you need to use 'line-1'.
|
||||||
|
|
||||||
|
------------------- SNIP -------------------
|
||||||
|
snippet r "return" "re.match('^\s+if err ', buffer[line-2])" be
|
||||||
|
return err
|
||||||
|
endsnippet
|
||||||
|
------------------- SNAP -------------------
|
||||||
|
|
||||||
|
That snippet will expand to 'return err' only if the previous line is starting
|
||||||
|
from 'if err' prefix.
|
||||||
|
|
||||||
|
Note: context snippets prioritized over non-context ones. It makes possible to
|
||||||
|
use non-context snippets as fallback, if no context matched:
|
||||||
|
|
||||||
|
------------------- SNIP -------------------
|
||||||
|
snippet i "if ..." b
|
||||||
|
if $1 {
|
||||||
|
$2
|
||||||
|
}
|
||||||
|
endsnippet
|
||||||
|
|
||||||
|
snippet i "if err != nil" "re.match('^\s+[^=]*err\s*:?=', buffer[line-2])" be
|
||||||
|
if err != nil {
|
||||||
|
$1
|
||||||
|
}
|
||||||
|
endsnippet
|
||||||
|
------------------- SNAP -------------------
|
||||||
|
|
||||||
|
That snippet will expand into 'if err != nil' if previous line will
|
||||||
|
match 'err :=' prefix, otherwise the default 'if' snippet will be expanded.
|
||||||
|
|
||||||
|
It's a good idea to move context conditions to a separate module, so it can be
|
||||||
|
used by other UltiSnips users. In that case, module should be imported
|
||||||
|
using 'global' keyword, like this:
|
||||||
|
|
||||||
|
------------------- SNIP -------------------
|
||||||
|
global !p
|
||||||
|
import my_utils
|
||||||
|
endglobal
|
||||||
|
|
||||||
|
snippet , "return ..., nil/err" "my_utils.is_return_argument(buffer, line, column)" ie
|
||||||
|
, `!p if my_utils.is_in_err_condition():
|
||||||
|
snip.rv = "err"
|
||||||
|
else:
|
||||||
|
snip.rv = "nil"`
|
||||||
|
endsnippet
|
||||||
|
------------------- SNAP -------------------
|
||||||
|
|
||||||
|
That snippet will expand only if the cursor is located in the return statement,
|
||||||
|
and then it will expand either to 'err' or to 'nil' depending on which 'if'
|
||||||
|
statement it's located. 'is_return_argument' and 'is_in_err_condition' are
|
||||||
|
part of custom python module which is called 'my_utils' in this example.
|
||||||
|
|
||||||
|
Context condition can return any value which python can use as condition in
|
||||||
|
it's 'if' statement, and if it's considered 'True', then snippet will be
|
||||||
|
expanded. The evaluated value of 'condition' is available in the 'context'
|
||||||
|
variable inside the snippet:
|
||||||
|
|
||||||
|
------------------- SNIP -------------------
|
||||||
|
snippet + "var +=" "re.match('\s*(.*?)\s*:?=', buffer[line-2])" ie
|
||||||
|
`!p snip.rv = context.group(1)` += $1
|
||||||
|
endsnippet
|
||||||
|
------------------- SNAP -------------------
|
||||||
|
|
||||||
|
That snippet will expand to 'var1 +=' after line, which begins from 'var1 :='.
|
||||||
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
5. UltiSnips and Other Plugins *UltiSnips-other-plugins*
|
5. UltiSnips and Other Plugins *UltiSnips-other-plugins*
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
import vim
|
||||||
|
|
||||||
from UltiSnips import _vim
|
from UltiSnips import _vim
|
||||||
from UltiSnips.compatibility import as_unicode
|
from UltiSnips.compatibility import as_unicode
|
||||||
from UltiSnips.indent_util import IndentUtil
|
from UltiSnips.indent_util import IndentUtil
|
||||||
@ -43,7 +45,7 @@ class SnippetDefinition(object):
|
|||||||
_TABS = re.compile(r"^\t*")
|
_TABS = re.compile(r"^\t*")
|
||||||
|
|
||||||
def __init__(self, priority, trigger, value, description,
|
def __init__(self, priority, trigger, value, description,
|
||||||
options, globals, location):
|
options, globals, location, context):
|
||||||
self._priority = int(priority)
|
self._priority = int(priority)
|
||||||
self._trigger = as_unicode(trigger)
|
self._trigger = as_unicode(trigger)
|
||||||
self._value = as_unicode(value)
|
self._value = as_unicode(value)
|
||||||
@ -53,6 +55,8 @@ class SnippetDefinition(object):
|
|||||||
self._last_re = None
|
self._last_re = None
|
||||||
self._globals = globals
|
self._globals = globals
|
||||||
self._location = location
|
self._location = location
|
||||||
|
self._context_code = context
|
||||||
|
self._context = None
|
||||||
|
|
||||||
# Make sure that we actually match our trigger in case we are
|
# Make sure that we actually match our trigger in case we are
|
||||||
# immediately expanded.
|
# immediately expanded.
|
||||||
@ -78,6 +82,31 @@ class SnippetDefinition(object):
|
|||||||
return match
|
return match
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _context_match(self):
|
||||||
|
current = vim.current
|
||||||
|
# skip on empty buffer
|
||||||
|
if len(current.buffer) == 1 and current.buffer[0] == "":
|
||||||
|
return
|
||||||
|
|
||||||
|
code = "\n".join([
|
||||||
|
'import re, os, vim, string, random',
|
||||||
|
'\n'.join(self._globals.get('!p', [])).replace('\r\n', '\n'),
|
||||||
|
'context["match"] = ' + self._context_code,
|
||||||
|
''
|
||||||
|
])
|
||||||
|
|
||||||
|
context = {'match': False}
|
||||||
|
locals = {
|
||||||
|
'context': context,
|
||||||
|
'window': current.window,
|
||||||
|
'buffer': current.buffer,
|
||||||
|
'line': current.window.cursor[0],
|
||||||
|
'column': current.window.cursor[1],
|
||||||
|
'cursor': current.window.cursor,
|
||||||
|
}
|
||||||
|
exec(code, locals)
|
||||||
|
return context["match"]
|
||||||
|
|
||||||
def has_option(self, opt):
|
def has_option(self, opt):
|
||||||
"""Check if the named option is set."""
|
"""Check if the named option is set."""
|
||||||
return opt in self._opts
|
return opt in self._opts
|
||||||
@ -109,6 +138,11 @@ class SnippetDefinition(object):
|
|||||||
"""Where this snippet was defined."""
|
"""Where this snippet was defined."""
|
||||||
return self._location
|
return self._location
|
||||||
|
|
||||||
|
@property
|
||||||
|
def context(self):
|
||||||
|
"""The matched context."""
|
||||||
|
return self._context
|
||||||
|
|
||||||
def matches(self, trigger):
|
def matches(self, trigger):
|
||||||
"""Returns True if this snippet matches 'trigger'."""
|
"""Returns True if this snippet matches 'trigger'."""
|
||||||
# If user supplies both "w" and "i", it should perhaps be an
|
# If user supplies both "w" and "i", it should perhaps be an
|
||||||
@ -152,6 +186,12 @@ class SnippetDefinition(object):
|
|||||||
if text_before.strip(' \t') != '':
|
if text_before.strip(' \t') != '':
|
||||||
self._matched = ''
|
self._matched = ''
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if match and self._context_code:
|
||||||
|
self._context = self._context_match()
|
||||||
|
if not self.context:
|
||||||
|
match = False
|
||||||
|
|
||||||
return match
|
return match
|
||||||
|
|
||||||
def could_match(self, trigger):
|
def could_match(self, trigger):
|
||||||
@ -236,7 +276,8 @@ class SnippetDefinition(object):
|
|||||||
|
|
||||||
snippet_instance = SnippetInstance(
|
snippet_instance = SnippetInstance(
|
||||||
self, parent, initial_text, start, end, visual_content,
|
self, parent, initial_text, start, end, visual_content,
|
||||||
last_re=self._last_re, globals=self._globals)
|
last_re=self._last_re, globals=self._globals,
|
||||||
|
context=self._context)
|
||||||
self.instantiate(snippet_instance, initial_text, indent)
|
self.instantiate(snippet_instance, initial_text, indent)
|
||||||
|
|
||||||
snippet_instance.update_textobjects()
|
snippet_instance.update_textobjects()
|
||||||
|
@ -15,7 +15,8 @@ class SnipMateSnippetDefinition(SnippetDefinition):
|
|||||||
|
|
||||||
def __init__(self, trigger, value, description, location):
|
def __init__(self, trigger, value, description, location):
|
||||||
SnippetDefinition.__init__(self, self.SNIPMATE_SNIPPET_PRIORITY,
|
SnippetDefinition.__init__(self, self.SNIPMATE_SNIPPET_PRIORITY,
|
||||||
trigger, value, description, '', {}, location)
|
trigger, value, description, '', {}, location,
|
||||||
|
None)
|
||||||
|
|
||||||
def instantiate(self, snippet_instance, initial_text, indent):
|
def instantiate(self, snippet_instance, initial_text, indent):
|
||||||
parse_and_instantiate(snippet_instance, initial_text, indent)
|
parse_and_instantiate(snippet_instance, initial_text, indent)
|
||||||
|
@ -69,8 +69,12 @@ class SnippetFileSource(SnippetSource):
|
|||||||
if ft in self._snippets:
|
if ft in self._snippets:
|
||||||
del self._snippets[ft]
|
del self._snippets[ft]
|
||||||
del self._extends[ft]
|
del self._extends[ft]
|
||||||
for fn in self._files_for_ft[ft]:
|
try:
|
||||||
self._parse_snippets(ft, fn)
|
for fn in self._files_for_ft[ft]:
|
||||||
|
self._parse_snippets(ft, fn)
|
||||||
|
except:
|
||||||
|
del self._files_for_ft[ft]
|
||||||
|
raise
|
||||||
# Now load for the parents
|
# Now load for the parents
|
||||||
for parent_ft in self.get_deep_extends([ft]):
|
for parent_ft in self.get_deep_extends([ft]):
|
||||||
if parent_ft != ft and self._needs_update(parent_ft):
|
if parent_ft != ft and self._needs_update(parent_ft):
|
||||||
|
@ -65,12 +65,19 @@ def _handle_snippet_or_global(filename, line, lines, python_globals, priority):
|
|||||||
# Get and strip options if they exist
|
# Get and strip options if they exist
|
||||||
remain = line[len(snip):].strip()
|
remain = line[len(snip):].strip()
|
||||||
words = remain.split()
|
words = remain.split()
|
||||||
|
|
||||||
if len(words) > 2:
|
if len(words) > 2:
|
||||||
# second to last word ends with a quote
|
# second to last word ends with a quote
|
||||||
if '"' not in words[-1] and words[-2][-1] == '"':
|
if '"' not in words[-1] and words[-2][-1] == '"':
|
||||||
opts = words[-1]
|
opts = words[-1]
|
||||||
remain = remain[:-len(opts) - 1].rstrip()
|
remain = remain[:-len(opts) - 1].rstrip()
|
||||||
|
|
||||||
|
context = None
|
||||||
|
if 'e' in opts:
|
||||||
|
left = remain[:-1].rfind('"')
|
||||||
|
if left != -1 and left != 0:
|
||||||
|
context, remain = remain[left:].strip('"'), remain[:left]
|
||||||
|
|
||||||
# Get and strip description if it exists
|
# Get and strip description if it exists
|
||||||
remain = remain.strip()
|
remain = remain.strip()
|
||||||
if len(remain.split()) > 1 and remain[-1] == '"':
|
if len(remain.split()) > 1 and remain[-1] == '"':
|
||||||
@ -103,9 +110,12 @@ def _handle_snippet_or_global(filename, line, lines, python_globals, priority):
|
|||||||
if snip == 'global':
|
if snip == 'global':
|
||||||
python_globals[trig].append(content)
|
python_globals[trig].append(content)
|
||||||
elif snip == 'snippet':
|
elif snip == 'snippet':
|
||||||
return 'snippet', (UltiSnipsSnippetDefinition(priority, trig, content,
|
definition = UltiSnipsSnippetDefinition(
|
||||||
descr, opts, python_globals,
|
priority, trig, content,
|
||||||
'%s:%i' % (filename, start_line_index)),)
|
descr, opts, python_globals,
|
||||||
|
'%s:%i' % (filename, start_line_index),
|
||||||
|
context)
|
||||||
|
return 'snippet', (definition,)
|
||||||
else:
|
else:
|
||||||
return 'error', ("Invalid snippet type: '%s'" % snip, lines.line_index)
|
return 'error', ("Invalid snippet type: '%s'" % snip, lines.line_index)
|
||||||
|
|
||||||
|
@ -205,18 +205,19 @@ class SnippetManager(object):
|
|||||||
|
|
||||||
@err_to_scratch_buffer
|
@err_to_scratch_buffer
|
||||||
def add_snippet(self, trigger, value, description,
|
def add_snippet(self, trigger, value, description,
|
||||||
options, ft='all', priority=0):
|
options, ft='all', priority=0, context=None):
|
||||||
"""Add a snippet to the list of known snippets of the given 'ft'."""
|
"""Add a snippet to the list of known snippets of the given 'ft'."""
|
||||||
self._added_snippets_source.add_snippet(ft,
|
self._added_snippets_source.add_snippet(ft,
|
||||||
UltiSnipsSnippetDefinition(priority, trigger, value,
|
UltiSnipsSnippetDefinition(priority, trigger, value,
|
||||||
description, options, {}, 'added'))
|
description, options, {}, 'added',
|
||||||
|
context))
|
||||||
|
|
||||||
@err_to_scratch_buffer
|
@err_to_scratch_buffer
|
||||||
def expand_anon(self, value, trigger='', description='', options=''):
|
def expand_anon(self, value, trigger='', description='', options='', context=None):
|
||||||
"""Expand an anonymous snippet right here."""
|
"""Expand an anonymous snippet right here."""
|
||||||
before = _vim.buf.line_till_cursor
|
before = _vim.buf.line_till_cursor
|
||||||
snip = UltiSnipsSnippetDefinition(0, trigger, value, description,
|
snip = UltiSnipsSnippetDefinition(0, trigger, value, description,
|
||||||
options, {}, '')
|
options, {}, '', context)
|
||||||
|
|
||||||
if not trigger or snip.matches(before):
|
if not trigger or snip.matches(before):
|
||||||
self._do_snippet(snip, before)
|
self._do_snippet(snip, before)
|
||||||
@ -574,6 +575,11 @@ class SnippetManager(object):
|
|||||||
if not before:
|
if not before:
|
||||||
return False
|
return False
|
||||||
snippets = self._snips(before, False)
|
snippets = self._snips(before, False)
|
||||||
|
if snippets:
|
||||||
|
# prefer snippets with context if any
|
||||||
|
snippets_with_context = [s for s in snippets if s.context]
|
||||||
|
if snippets_with_context:
|
||||||
|
snippets = snippets_with_context
|
||||||
if not snippets:
|
if not snippets:
|
||||||
# No snippet found
|
# No snippet found
|
||||||
return False
|
return False
|
||||||
|
@ -21,7 +21,7 @@ class SnippetInstance(EditableTextObject):
|
|||||||
# pylint:disable=protected-access
|
# pylint:disable=protected-access
|
||||||
|
|
||||||
def __init__(self, snippet, parent, initial_text,
|
def __init__(self, snippet, parent, initial_text,
|
||||||
start, end, visual_content, last_re, globals):
|
start, end, visual_content, last_re, globals, context):
|
||||||
if start is None:
|
if start is None:
|
||||||
start = Position(0, 0)
|
start = Position(0, 0)
|
||||||
if end is None:
|
if end is None:
|
||||||
@ -29,7 +29,7 @@ class SnippetInstance(EditableTextObject):
|
|||||||
self.snippet = snippet
|
self.snippet = snippet
|
||||||
self._cts = 0
|
self._cts = 0
|
||||||
|
|
||||||
self.locals = {'match': last_re}
|
self.locals = {'match': last_re, 'context': context}
|
||||||
self.globals = globals
|
self.globals = globals
|
||||||
self.visual_content = visual_content
|
self.visual_content = visual_content
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ syn match snipSnippetTrigger ,".\{-}"\ze\%(\s\+"\%(\s*\S\)\@=[^"]*\%("\s\+[^"[:s
|
|||||||
syn match snipSnippetTriggerInvalid ,\S\@=.\{-}\S\ze\%(\s\+"[^"]*\%("\s\+[^"[:space:]]\+\s*\|"\s*\)\=\|\s*\)$, contained nextgroup=snipSnippetDocString skipwhite
|
syn match snipSnippetTriggerInvalid ,\S\@=.\{-}\S\ze\%(\s\+"[^"]*\%("\s\+[^"[:space:]]\+\s*\|"\s*\)\=\|\s*\)$, contained nextgroup=snipSnippetDocString skipwhite
|
||||||
syn match snipSnippetDocString ,"[^"]*\%("\ze\s*\%(\s[^"[:space:]]\+\s*\)\=\)\=$, contained nextgroup=snipSnippetOptions skipwhite
|
syn match snipSnippetDocString ,"[^"]*\%("\ze\s*\%(\s[^"[:space:]]\+\s*\)\=\)\=$, contained nextgroup=snipSnippetOptions skipwhite
|
||||||
syn match snipSnippetOptions ,\S\+, contained contains=snipSnippetOptionFlag
|
syn match snipSnippetOptions ,\S\+, contained contains=snipSnippetOptionFlag
|
||||||
syn match snipSnippetOptionFlag ,[biwrts], contained
|
syn match snipSnippetOptionFlag ,[biwrtsmx], contained
|
||||||
|
|
||||||
" Command substitution {{{4
|
" Command substitution {{{4
|
||||||
|
|
||||||
|
127
test/test_ContextSnippets.py
Normal file
127
test/test_ContextSnippets.py
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
from test.vim_test_case import VimTestCase as _VimTest
|
||||||
|
from test.constant import *
|
||||||
|
|
||||||
|
|
||||||
|
class ContextSnippets_SimpleSnippet(_VimTest):
|
||||||
|
files = { 'us/all.snippets': r"""
|
||||||
|
snippet a "desc" "True" e
|
||||||
|
abc
|
||||||
|
endsnippet
|
||||||
|
"""}
|
||||||
|
keys = 'a' + EX
|
||||||
|
wanted = 'abc'
|
||||||
|
|
||||||
|
|
||||||
|
class ContextSnippets_ExpandOnTrue(_VimTest):
|
||||||
|
files = { 'us/all.snippets': r"""
|
||||||
|
global !p
|
||||||
|
def check_context():
|
||||||
|
return True
|
||||||
|
endglobal
|
||||||
|
|
||||||
|
snippet a "desc" "check_context()" e
|
||||||
|
abc
|
||||||
|
endsnippet
|
||||||
|
"""}
|
||||||
|
keys = 'a' + EX
|
||||||
|
wanted = 'abc'
|
||||||
|
|
||||||
|
|
||||||
|
class ContextSnippets_DoNotExpandOnFalse(_VimTest):
|
||||||
|
files = { 'us/all.snippets': r"""
|
||||||
|
global !p
|
||||||
|
def check_context():
|
||||||
|
return False
|
||||||
|
endglobal
|
||||||
|
|
||||||
|
snippet a "desc" "check_context()" e
|
||||||
|
abc
|
||||||
|
endsnippet
|
||||||
|
"""}
|
||||||
|
keys = 'a' + EX
|
||||||
|
wanted = keys
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ContextSnippets_UseContext(_VimTest):
|
||||||
|
files = { 'us/all.snippets': r"""
|
||||||
|
global !p
|
||||||
|
def wrap(ins):
|
||||||
|
return "< " + ins + " >"
|
||||||
|
endglobal
|
||||||
|
|
||||||
|
snippet a "desc" "wrap(buffer[line-1])" e
|
||||||
|
{ `!p snip.rv = context` }
|
||||||
|
endsnippet
|
||||||
|
"""}
|
||||||
|
keys = 'a' + EX
|
||||||
|
wanted = '{ < a > }'
|
||||||
|
|
||||||
|
|
||||||
|
class ContextSnippets_SnippetPriority(_VimTest):
|
||||||
|
files = { 'us/all.snippets': r"""
|
||||||
|
snippet i "desc" "re.search('err :=', buffer[line-2])" e
|
||||||
|
if err != nil {
|
||||||
|
${1:// pass}
|
||||||
|
}
|
||||||
|
endsnippet
|
||||||
|
|
||||||
|
snippet i
|
||||||
|
if ${1:true} {
|
||||||
|
${2:// pass}
|
||||||
|
}
|
||||||
|
endsnippet
|
||||||
|
"""}
|
||||||
|
|
||||||
|
keys = r"""
|
||||||
|
err := some_call()
|
||||||
|
i""" + EX + JF + """
|
||||||
|
i""" + EX
|
||||||
|
wanted = r"""
|
||||||
|
err := some_call()
|
||||||
|
if err != nil {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
if true {
|
||||||
|
// pass
|
||||||
|
}"""
|
||||||
|
|
||||||
|
|
||||||
|
class ContextSnippets_PriorityKeyword(_VimTest):
|
||||||
|
files = { 'us/all.snippets': r"""
|
||||||
|
snippet i "desc" "True" e
|
||||||
|
a
|
||||||
|
endsnippet
|
||||||
|
|
||||||
|
priority 100
|
||||||
|
snippet i
|
||||||
|
b
|
||||||
|
endsnippet
|
||||||
|
"""}
|
||||||
|
|
||||||
|
keys = "i" + EX
|
||||||
|
wanted = "b"
|
||||||
|
|
||||||
|
|
||||||
|
class ContextSnippets_ReportError(_VimTest):
|
||||||
|
files = { 'us/all.snippets': r"""
|
||||||
|
snippet e "desc" "Tru" e
|
||||||
|
error
|
||||||
|
endsnippet
|
||||||
|
"""}
|
||||||
|
|
||||||
|
keys = "e" + EX
|
||||||
|
wanted = "e" + EX
|
||||||
|
expected_error = r"NameError: name 'Tru' is not defined"
|
||||||
|
|
||||||
|
|
||||||
|
class ContextSnippets_ReportErrorOnIndexOutOfRange(_VimTest):
|
||||||
|
files = { 'us/all.snippets': r"""
|
||||||
|
snippet e "desc" "buffer[123]" e
|
||||||
|
error
|
||||||
|
endsnippet
|
||||||
|
"""}
|
||||||
|
|
||||||
|
keys = "e" + EX
|
||||||
|
wanted = "e" + EX
|
||||||
|
expected_error = r"IndexError: line number out of range"
|
@ -159,7 +159,8 @@ class MySnippetSource(SnippetSource):
|
|||||||
if before.endswith('blumba'):
|
if before.endswith('blumba'):
|
||||||
return [
|
return [
|
||||||
UltiSnipsSnippetDefinition(
|
UltiSnipsSnippetDefinition(
|
||||||
-100, "blumba", "this is a dynamic snippet", "", "", {}, "blub")
|
-100, "blumba", "this is a dynamic snippet", "", "", {}, "blub",
|
||||||
|
None)
|
||||||
]
|
]
|
||||||
return []
|
return []
|
||||||
""")
|
""")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user