From 66bc2e8f6ea8958688e506dbfaf92b6f9a693f36 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Thu, 16 Apr 2015 11:31:12 +0600 Subject: [PATCH] documentation, pull-requests fixes --- doc/UltiSnips.txt | 96 +++++++++++++++++++ pythonx/UltiSnips/snippet/definition/_base.py | 15 +-- .../UltiSnips/snippet/definition/snipmate.py | 3 +- .../snippet/source/file/ultisnips.py | 15 +-- 4 files changed, 114 insertions(+), 15 deletions(-) diff --git a/doc/UltiSnips.txt b/doc/UltiSnips.txt index dbbe8fa..3d54571 100644 --- a/doc/UltiSnips.txt +++ b/doc/UltiSnips.txt @@ -653,6 +653,11 @@ The options currently supported are: > Without this option empty lines in snippets definition will have 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. > endsnippet @@ -818,6 +823,7 @@ The variables automatically defined in python code are: > t - The values of the placeholders, t[1] is the text of ${1}, and so on snip - UltiSnips.TextObjects.SnippetUtil object instance. Has methods that simplify indentation handling. + context - Result of context condition. See |UltiSnips-context-snippets|. The 'snip' object provides the following methods: > @@ -1293,6 +1299,96 @@ clearsnippets trigger1 trigger2 ------------------- 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 + +'expression' can be any python expression. If 'expression' evaluates to +'True', then this snippet will be chosen for expansion. 'expression' must be +wrapped with double-quotes. + +Following python modules are automatically imported: 're', 'os', 'vim', +'string', 'random'. + +Also, variables are declared in local scope for use in expression: > + 'window' - alias for 'vim.current.window' + 'buffer' - alias for 'vim.current.window.buffer' + '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 current line you need to use 'line-1' expression. + +------------------- 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 previous line is starting +from 'if err' prefix. + +Note: context snippets prioritized over non-context ones. So, if there are +two snippets with the same trigger, and one of them is context snippet and +it's expression evaluates to 'True', then context snippet will be used for +expansion and first will be ignored. + +------------------- 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 default 'if' snippet will be expanded. + +It's good idea to move context conditions to separate library, so it can be +used with other UltiSnips user. In that case, library 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 cursor is located in the return statement, +and then it will expand either to 'err' or to 'nil' depending in which 'if' +statement it's located. 'is_return_argument' and 'is_in_err_condition' are +part of custom python library 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 considired 'True', then snippet +will be expanded. Moreover, result of condition will be accessed in the +'context' variable: + +------------------- 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* diff --git a/pythonx/UltiSnips/snippet/definition/_base.py b/pythonx/UltiSnips/snippet/definition/_base.py index c720a94..9b9c5de 100644 --- a/pythonx/UltiSnips/snippet/definition/_base.py +++ b/pythonx/UltiSnips/snippet/definition/_base.py @@ -4,6 +4,7 @@ """Snippet representation after parsing.""" import re + import vim from UltiSnips import _vim @@ -44,7 +45,7 @@ class SnippetDefinition(object): _TABS = re.compile(r"^\t*") def __init__(self, priority, trigger, value, description, - options, globals, location, context=None): + options, globals, location, context): self._priority = int(priority) self._trigger = as_unicode(trigger) self._value = as_unicode(value) @@ -90,17 +91,17 @@ class SnippetDefinition(object): code = "\n".join([ 'import re, os, vim, string, random', '\n'.join(self._globals.get('!p', [])).replace('\r\n', '\n'), - 'ctx["match"] = ' + self._context_code, + 'context["match"] = ' + self._context_code, '' ]) context = {'match': False} locals = { - 'ctx': context, - 'w': current.window, - 'b': current.buffer, - 'l': current.window.cursor[0], - 'c': current.window.cursor[1], + 'context': context, + 'window': current.window, + 'buffer': current.buffer, + 'line': current.window.cursor[0], + 'column': current.window.cursor[1], } exec(code, locals) diff --git a/pythonx/UltiSnips/snippet/definition/snipmate.py b/pythonx/UltiSnips/snippet/definition/snipmate.py index 7f28212..a77046b 100644 --- a/pythonx/UltiSnips/snippet/definition/snipmate.py +++ b/pythonx/UltiSnips/snippet/definition/snipmate.py @@ -15,7 +15,8 @@ class SnipMateSnippetDefinition(SnippetDefinition): def __init__(self, trigger, value, description, location): SnippetDefinition.__init__(self, self.SNIPMATE_SNIPPET_PRIORITY, - trigger, value, description, '', {}, location) + trigger, value, description, '', {}, location, + None) def instantiate(self, snippet_instance, initial_text, indent): parse_and_instantiate(snippet_instance, initial_text, indent) diff --git a/pythonx/UltiSnips/snippet/source/file/ultisnips.py b/pythonx/UltiSnips/snippet/source/file/ultisnips.py index 99de8b6..0383997 100644 --- a/pythonx/UltiSnips/snippet/source/file/ultisnips.py +++ b/pythonx/UltiSnips/snippet/source/file/ultisnips.py @@ -72,12 +72,11 @@ def _handle_snippet_or_global(filename, line, lines, python_globals, priority): opts = words[-1] remain = remain[:-len(opts) - 1].rstrip() - if 'x' in opts: + context = None + if 'e' in opts: left = remain[:-1].rfind('"') if left != -1 and left != 0: context, remain = remain[left:].strip('"'), remain[:left] - else: - context = None # Get and strip description if it exists remain = remain.strip() @@ -111,10 +110,12 @@ def _handle_snippet_or_global(filename, line, lines, python_globals, priority): if snip == 'global': python_globals[trig].append(content) elif snip == 'snippet': - return 'snippet', (UltiSnipsSnippetDefinition(priority, trig, content, - descr, opts, python_globals, - '%s:%i' % (filename, start_line_index), - context),) + definition = UltiSnipsSnippetDefinition( + priority, trig, content, + descr, opts, python_globals, + '%s:%i' % (filename, start_line_index), + context) + return 'snippet', (definition,) else: return 'error', ("Invalid snippet type: '%s'" % snip, lines.line_index)