2009-06-23 14:45:04 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# encoding: utf-8
|
|
|
|
|
2009-07-06 23:09:53 +02:00
|
|
|
import glob
|
2009-07-04 12:14:13 +02:00
|
|
|
import os
|
2009-07-06 23:09:53 +02:00
|
|
|
import re
|
|
|
|
import string
|
|
|
|
import vim
|
2009-07-04 12:14:13 +02:00
|
|
|
|
2009-07-10 12:47:54 +02:00
|
|
|
from UltiSnips.Geometry import Position
|
|
|
|
from UltiSnips.TextObjects import *
|
|
|
|
from UltiSnips.Buffer import VimBuffer
|
2009-07-01 15:20:40 +02:00
|
|
|
|
2009-09-17 14:11:43 +02:00
|
|
|
# The following lines silence DeprecationWarnings. They are raised
|
|
|
|
# by python2.6 for vim.error (which is a string that is used as an exception,
|
|
|
|
# which is deprecated since 2.5 and will no longer work in 2.7. Let's hope
|
|
|
|
# vim gets this fixed before)
|
2009-09-17 14:26:57 +02:00
|
|
|
import sys
|
|
|
|
if sys.version_info[:2] >= (2,6):
|
|
|
|
import warnings
|
|
|
|
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
2009-09-17 14:11:43 +02:00
|
|
|
|
2009-10-13 09:56:12 +02:00
|
|
|
def _vim_quote(s):
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
"""Quote string s as Vim literal string."""
|
|
|
|
return "'" + s.replace("'", "''") + "'"
|
|
|
|
|
2009-08-16 16:42:32 +02:00
|
|
|
class _SnippetDictionary(object):
|
2009-08-02 11:57:43 +02:00
|
|
|
def __init__(self, *args, **kwargs):
|
2009-08-16 16:42:32 +02:00
|
|
|
self._snippets = []
|
2009-08-02 11:57:43 +02:00
|
|
|
self._extends = []
|
|
|
|
|
2009-08-16 16:42:32 +02:00
|
|
|
def add_snippet(self, s):
|
|
|
|
self._snippets.append(s)
|
|
|
|
|
2009-08-16 20:55:08 +02:00
|
|
|
def get_matching_snippets(self, trigger, potentially):
|
2009-08-16 16:34:54 +02:00
|
|
|
"""Returns all snippets matching the given trigger."""
|
2009-08-16 20:55:08 +02:00
|
|
|
if not potentially:
|
|
|
|
return [ s for s in self._snippets if s.matches(trigger) ]
|
|
|
|
else:
|
|
|
|
return [ s for s in self._snippets if s.could_match(trigger) ]
|
2009-08-16 16:34:54 +02:00
|
|
|
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
def clear_snippets(self, triggers=[]):
|
|
|
|
"""Remove all snippets that match each trigger in triggers.
|
|
|
|
When triggers is empty, removes all snippets.
|
|
|
|
"""
|
|
|
|
if triggers:
|
|
|
|
for t in triggers:
|
|
|
|
for s in self.get_matching_snippets(t, potentially=False):
|
|
|
|
self._snippets.remove(s)
|
|
|
|
else:
|
|
|
|
self._snippets = []
|
|
|
|
|
2009-08-02 11:57:43 +02:00
|
|
|
def extends():
|
|
|
|
def fget(self):
|
|
|
|
return self._extends
|
|
|
|
def fset(self, value):
|
|
|
|
self._extends = value
|
|
|
|
return locals()
|
|
|
|
extends = property(**extends())
|
|
|
|
|
2009-08-02 11:29:42 +02:00
|
|
|
class _SnippetsFileParser(object):
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
def __init__(self, ft, fn, snip_manager, file_data=None):
|
2009-08-02 11:29:42 +02:00
|
|
|
self._sm = snip_manager
|
|
|
|
self._ft = ft
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
self._fn = fn
|
|
|
|
if file_data is None:
|
|
|
|
self._lines = open(fn).readlines()
|
|
|
|
else:
|
|
|
|
self._lines = file_data.splitlines(True)
|
2009-08-02 11:29:42 +02:00
|
|
|
|
|
|
|
self._idx = 0
|
|
|
|
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
def _error(self, msg):
|
2009-10-13 09:56:12 +02:00
|
|
|
fn = vim.eval("""fnamemodify(%s, ":~:.")""" % _vim_quote(self._fn))
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
self._sm._error("%s in %s(%d)" % (msg, fn, self._idx + 1))
|
|
|
|
|
|
|
|
def _line(self):
|
|
|
|
if self._idx < len(self._lines):
|
|
|
|
line = self._lines[self._idx]
|
|
|
|
else:
|
|
|
|
line = ""
|
|
|
|
return line
|
|
|
|
|
|
|
|
def _line_head_tail(self):
|
|
|
|
parts = re.split(r"\s+", self._line().rstrip(), maxsplit=1)
|
|
|
|
parts.append('')
|
|
|
|
return parts[:2]
|
|
|
|
|
|
|
|
def _line_head(self):
|
|
|
|
return self._line_head_tail()[0]
|
|
|
|
|
|
|
|
def _line_tail(self):
|
|
|
|
return self._line_head_tail()[1]
|
|
|
|
|
|
|
|
def _goto_next_line(self):
|
|
|
|
self._idx += 1
|
|
|
|
return self._line()
|
|
|
|
|
2009-08-02 11:29:42 +02:00
|
|
|
def _parse_snippet(self):
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
line = self._line()
|
2009-08-02 11:29:42 +02:00
|
|
|
|
|
|
|
cdescr = ""
|
|
|
|
coptions = ""
|
|
|
|
|
|
|
|
cs = line.split()[1]
|
|
|
|
left = line.find('"')
|
|
|
|
if left != -1:
|
|
|
|
right = line.rfind('"')
|
|
|
|
cdescr = line[left+1:right]
|
|
|
|
coptions = line[right:].strip()
|
|
|
|
|
|
|
|
cv = ""
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
while self._goto_next_line():
|
|
|
|
line = self._line()
|
|
|
|
if line.rstrip() == "endsnippet":
|
2009-08-02 11:29:42 +02:00
|
|
|
cv = cv[:-1] # Chop the last newline
|
|
|
|
self._sm.add_snippet(cs, cv, cdescr, coptions, self._ft)
|
|
|
|
break
|
|
|
|
cv += line
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
else:
|
|
|
|
self._error("Missing 'endsnippet' for %r" % cs)
|
2009-08-02 11:29:42 +02:00
|
|
|
|
|
|
|
def parse(self):
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
while self._line():
|
|
|
|
head, tail = self._line_head_tail()
|
|
|
|
if head == "extends":
|
|
|
|
if tail:
|
|
|
|
self._sm.add_extending_info(self._ft,
|
|
|
|
[ p.strip() for p in tail.split(',') ])
|
|
|
|
else:
|
|
|
|
self._error("'extends' without file types")
|
|
|
|
elif head == "snippet":
|
|
|
|
self._parse_snippet()
|
|
|
|
elif head == "clearsnippets":
|
|
|
|
self._sm.clear_snippets(tail.split(), self._ft)
|
|
|
|
elif head and not head.startswith('#'):
|
|
|
|
self._error("Invalid line %r" % self._line().rstrip())
|
|
|
|
break
|
|
|
|
self._goto_next_line()
|
2009-08-02 11:29:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2009-07-03 10:59:55 +02:00
|
|
|
class Snippet(object):
|
2009-07-04 17:59:50 +02:00
|
|
|
_INDENT = re.compile(r"^[ \t]*")
|
|
|
|
|
2009-07-17 23:33:48 +02:00
|
|
|
def __init__(self, trigger, value, descr, options):
|
2009-06-23 14:45:04 +02:00
|
|
|
self._t = trigger
|
|
|
|
self._v = value
|
2009-07-04 21:58:13 +02:00
|
|
|
self._d = descr
|
2009-07-18 00:51:19 +02:00
|
|
|
self._opts = options
|
2009-07-17 23:33:48 +02:00
|
|
|
|
2009-07-19 12:56:10 +02:00
|
|
|
def __repr__(self):
|
|
|
|
return "Snippet(%s,%s,%s)" % (self._t,self._d,self._opts)
|
|
|
|
|
2009-08-16 16:34:54 +02:00
|
|
|
def matches(self, trigger):
|
2009-08-23 18:44:19 -04:00
|
|
|
# If user supplies both "w" and "i", it should perhaps be an
|
|
|
|
# error, but if permitted it seems that "w" should take precedence
|
|
|
|
# (since matching at word boundary and within a word == matching at word
|
|
|
|
# boundary).
|
|
|
|
if "w" in self._opts:
|
|
|
|
trigger_len = len(self._t)
|
|
|
|
trigger_prefix = trigger[:-trigger_len]
|
|
|
|
trigger_suffix = trigger[-trigger_len:]
|
|
|
|
match = (trigger_suffix == self._t)
|
|
|
|
if match and trigger_prefix:
|
|
|
|
# Require a word boundary between prefix and suffix.
|
|
|
|
boundaryChars = trigger_prefix[-1:] + trigger_suffix[:1]
|
|
|
|
match = re.match(r'.\b.', boundaryChars)
|
|
|
|
elif "i" in self._opts:
|
|
|
|
match = trigger.endswith(self._t)
|
|
|
|
else:
|
|
|
|
match = (trigger == self._t)
|
|
|
|
return match
|
2009-08-16 16:34:54 +02:00
|
|
|
|
2009-08-16 20:55:08 +02:00
|
|
|
def could_match(self, trigger):
|
2009-08-23 18:44:19 -04:00
|
|
|
if "w" in self._opts:
|
|
|
|
# Trim non-empty prefix up to word boundary, if present.
|
|
|
|
trigger_suffix = re.sub(r'^.+\b(.+)$', r'\1', trigger)
|
|
|
|
match = self._t.startswith(trigger_suffix)
|
|
|
|
|
|
|
|
# TODO: list_snippets() function cannot handle partial-trigger
|
|
|
|
# matches yet, so for now fail if we trimmed the prefix.
|
|
|
|
if trigger_suffix != trigger:
|
|
|
|
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(trigger)
|
|
|
|
else:
|
|
|
|
match = self._t.startswith(trigger)
|
|
|
|
return match
|
2009-08-16 20:55:08 +02:00
|
|
|
|
2009-07-17 23:33:48 +02:00
|
|
|
def overwrites_previous(self):
|
2009-07-18 00:51:19 +02:00
|
|
|
return "!" in self._opts
|
2009-07-17 23:33:48 +02:00
|
|
|
overwrites_previous = property(overwrites_previous)
|
2009-07-04 21:58:13 +02:00
|
|
|
|
2009-07-18 00:51:19 +02:00
|
|
|
def needs_ws_in_front(self):
|
|
|
|
return "b" in self._opts
|
|
|
|
needs_ws_in_front = property(needs_ws_in_front)
|
|
|
|
|
2009-07-04 21:58:13 +02:00
|
|
|
def description(self):
|
2009-08-16 18:50:14 +02:00
|
|
|
return ("(%s) %s" % (self._t, self._d)).strip()
|
2009-07-04 21:58:13 +02:00
|
|
|
description = property(description)
|
2009-06-28 14:51:27 +02:00
|
|
|
|
2009-06-23 14:45:04 +02:00
|
|
|
def trigger(self):
|
|
|
|
return self._t
|
2009-07-02 19:19:47 +02:00
|
|
|
trigger = property(trigger)
|
2009-06-28 14:51:27 +02:00
|
|
|
|
2009-07-18 00:14:20 +02:00
|
|
|
def launch(self, text_before, parent, start, end = None):
|
2009-07-06 10:40:07 +02:00
|
|
|
indent = self._INDENT.match(text_before).group(0)
|
2009-07-04 17:59:50 +02:00
|
|
|
v = self._v
|
2009-07-06 10:40:07 +02:00
|
|
|
if len(indent):
|
2009-07-04 21:58:13 +02:00
|
|
|
lines = self._v.splitlines()
|
2009-07-06 10:40:07 +02:00
|
|
|
v = lines[0]
|
|
|
|
if len(lines) > 1:
|
|
|
|
v += os.linesep + \
|
|
|
|
os.linesep.join([indent + l for l in lines[1:]])
|
2009-07-04 21:58:13 +02:00
|
|
|
|
2009-07-29 09:49:44 +02:00
|
|
|
if vim.eval("&expandtab") == '1':
|
|
|
|
ts = int(vim.eval("&ts"))
|
2009-07-30 08:08:36 +02:00
|
|
|
# expandtabs will not work for us, we have to replace all tabstops
|
|
|
|
# so that indent is right at least. tabs in the middle of the line
|
|
|
|
# will not be expanded correctly
|
|
|
|
v = v.replace('\t', ts*" ")
|
2009-07-29 09:49:44 +02:00
|
|
|
|
2009-07-18 00:14:20 +02:00
|
|
|
if parent is None:
|
2009-07-28 08:04:53 +02:00
|
|
|
return SnippetInstance(StartMarker(start), indent, v)
|
2009-07-18 00:14:20 +02:00
|
|
|
else:
|
2009-07-28 08:04:53 +02:00
|
|
|
return SnippetInstance(parent, indent, v, start, end)
|
2009-06-28 14:51:27 +02:00
|
|
|
|
2009-07-06 08:22:46 +02:00
|
|
|
class VimState(object):
|
2009-07-04 12:45:35 +02:00
|
|
|
def __init__(self):
|
|
|
|
self._abs_pos = None
|
|
|
|
self._moved = Position(0,0)
|
|
|
|
|
2009-07-05 22:25:01 +02:00
|
|
|
self._lines = None
|
|
|
|
self._dlines = None
|
|
|
|
self._cols = None
|
|
|
|
self._dcols = None
|
2009-07-16 10:16:30 +02:00
|
|
|
self._cline = None
|
2009-07-06 10:31:12 +02:00
|
|
|
self._lline = None
|
2009-07-05 22:25:01 +02:00
|
|
|
self._text_changed = None
|
|
|
|
|
2009-07-06 08:22:46 +02:00
|
|
|
def update(self):
|
2009-07-04 12:45:35 +02:00
|
|
|
line, col = vim.current.window.cursor
|
|
|
|
line -= 1
|
|
|
|
abs_pos = Position(line,col)
|
|
|
|
if self._abs_pos:
|
|
|
|
self._moved = abs_pos - self._abs_pos
|
|
|
|
self._abs_pos = abs_pos
|
|
|
|
|
2009-07-06 08:22:46 +02:00
|
|
|
# Update buffer infos
|
2009-07-05 22:25:01 +02:00
|
|
|
cols = len(vim.current.buffer[line])
|
|
|
|
if self._cols:
|
|
|
|
self._dcols = cols - self._cols
|
|
|
|
self._cols = cols
|
|
|
|
|
|
|
|
lines = len(vim.current.buffer)
|
|
|
|
if self._lines:
|
|
|
|
self._dlines = lines - self._lines
|
|
|
|
self._lines = lines
|
|
|
|
|
2009-07-06 08:22:46 +02:00
|
|
|
# Check if the buffer has changed in any ways
|
|
|
|
self._text_changed = False
|
2009-07-06 10:31:12 +02:00
|
|
|
# does it have more lines?
|
|
|
|
if self._dlines:
|
|
|
|
self._text_changed = True
|
|
|
|
# did we stay in the same line and it has more columns now?
|
|
|
|
elif not self.moved.line and self._dcols:
|
|
|
|
self._text_changed = True
|
|
|
|
# If the length didn't change but we moved a column, check if
|
|
|
|
# the char under the cursor has changed (might be one char tab).
|
|
|
|
elif self.moved.col == 1:
|
2009-07-16 10:16:30 +02:00
|
|
|
self._text_changed = self._cline != vim.current.buffer[line]
|
|
|
|
self._lline = self._cline
|
|
|
|
self._cline = vim.current.buffer[line]
|
2009-07-06 08:22:46 +02:00
|
|
|
|
2009-07-09 14:24:43 +02:00
|
|
|
def select_span(self, r):
|
2009-07-09 07:50:48 +02:00
|
|
|
delta = r.end - r.start
|
|
|
|
lineno, col = r.start.line, r.start.col
|
|
|
|
|
|
|
|
vim.current.window.cursor = lineno + 1, col
|
|
|
|
|
2009-07-10 12:06:58 +02:00
|
|
|
if delta.line == delta.col == 0:
|
2009-07-10 09:35:21 +02:00
|
|
|
if col == 0 or vim.eval("mode()") != 'i':
|
2009-07-09 07:50:48 +02:00
|
|
|
vim.command(r'call feedkeys("\<Esc>i")')
|
|
|
|
else:
|
|
|
|
vim.command(r'call feedkeys("\<Esc>a")')
|
|
|
|
else:
|
2009-07-10 12:06:58 +02:00
|
|
|
if delta.line:
|
|
|
|
move_lines = "%ij" % delta.line
|
|
|
|
else:
|
|
|
|
move_lines = ""
|
2009-07-09 07:50:48 +02:00
|
|
|
# Depending on the current mode and position, we
|
|
|
|
# might need to move escape out of the mode and this
|
|
|
|
# will move our cursor one left
|
|
|
|
if col != 0 and vim.eval("mode()") == 'i':
|
|
|
|
move_one_right = "l"
|
|
|
|
else:
|
|
|
|
move_one_right = ""
|
|
|
|
|
2009-07-10 12:06:58 +02:00
|
|
|
if 0 <= delta.col <= 1:
|
2009-07-09 07:50:48 +02:00
|
|
|
do_select = ""
|
2009-07-10 12:06:58 +02:00
|
|
|
elif delta.col > 0:
|
2009-07-09 07:50:48 +02:00
|
|
|
do_select = "%il" % (delta.col-1)
|
2009-07-10 12:06:58 +02:00
|
|
|
else:
|
|
|
|
do_select = "%ih" % (-delta.col+1)
|
|
|
|
|
2009-07-09 07:50:48 +02:00
|
|
|
|
2009-07-10 12:06:58 +02:00
|
|
|
vim.command(r'call feedkeys("\<Esc>%sv%s%s\<c-g>")' %
|
|
|
|
(move_one_right, move_lines, do_select))
|
2009-07-09 07:50:48 +02:00
|
|
|
|
|
|
|
|
2009-07-05 22:25:01 +02:00
|
|
|
def buf_changed(self):
|
|
|
|
return self._text_changed
|
|
|
|
buf_changed = property(buf_changed)
|
|
|
|
|
2009-07-04 12:45:35 +02:00
|
|
|
def pos(self):
|
|
|
|
return self._abs_pos
|
|
|
|
pos = property(pos)
|
|
|
|
|
2009-07-04 15:01:19 +02:00
|
|
|
def ppos(self):
|
|
|
|
if not self.has_moved:
|
|
|
|
return self.pos
|
|
|
|
return self.pos - self.moved
|
|
|
|
ppos = property(ppos)
|
|
|
|
|
2009-07-04 12:45:35 +02:00
|
|
|
def moved(self):
|
|
|
|
return self._moved
|
|
|
|
moved = property(moved)
|
|
|
|
|
|
|
|
def has_moved(self):
|
|
|
|
return bool(self._moved.line or self._moved.col)
|
|
|
|
has_moved = property(has_moved)
|
|
|
|
|
2009-07-16 10:16:30 +02:00
|
|
|
def last_line(self):
|
|
|
|
return self._lline
|
|
|
|
last_line = property(last_line)
|
|
|
|
|
2009-06-23 14:45:04 +02:00
|
|
|
class SnippetManager(object):
|
|
|
|
def __init__(self):
|
2009-07-06 08:22:46 +02:00
|
|
|
self._vstate = VimState()
|
2009-07-21 10:21:05 +02:00
|
|
|
self._supertab_keys = None
|
2009-07-04 12:45:35 +02:00
|
|
|
|
2009-07-16 17:34:36 +02:00
|
|
|
self.reset()
|
|
|
|
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
def reset(self, test_error=False):
|
|
|
|
self._test_error = test_error
|
2009-07-01 10:39:46 +02:00
|
|
|
self._snippets = {}
|
2009-07-16 14:10:59 +02:00
|
|
|
self._csnippets = []
|
2009-07-16 17:34:36 +02:00
|
|
|
self._reinit()
|
|
|
|
|
2009-07-21 10:21:05 +02:00
|
|
|
def jump_forwards(self):
|
|
|
|
if not self._jump():
|
|
|
|
return self._handle_failure(self.forward_trigger)
|
|
|
|
|
|
|
|
def jump_backwards(self):
|
|
|
|
if not self._jump(True):
|
2009-07-26 23:03:59 +02:00
|
|
|
return self._handle_failure(self.backward_trigger)
|
2009-07-21 10:21:05 +02:00
|
|
|
|
|
|
|
def expand(self):
|
|
|
|
if not self._try_expand():
|
|
|
|
self._handle_failure(self.expand_trigger)
|
2009-07-22 12:08:21 +02:00
|
|
|
|
2009-08-16 18:50:14 +02:00
|
|
|
def list_snippets(self):
|
2009-08-16 20:55:08 +02:00
|
|
|
filetypes = self._ensure_snippets_loaded()
|
|
|
|
|
|
|
|
# TODO: this code is duplicated below
|
|
|
|
filetypes = vim.eval("&filetype").split(".") + [ "all" ]
|
|
|
|
lineno,col = vim.current.window.cursor
|
|
|
|
|
|
|
|
line = vim.current.line
|
|
|
|
before,after = line[:col], line[col:]
|
|
|
|
|
|
|
|
word = ''
|
|
|
|
if len(before):
|
|
|
|
word = before.split()[-1]
|
|
|
|
|
|
|
|
found_snippets = []
|
|
|
|
for ft in filetypes[::-1]:
|
|
|
|
found_snippets += self._find_snippets(ft, word, True)
|
|
|
|
|
|
|
|
if len(found_snippets) == 0:
|
|
|
|
return True
|
|
|
|
|
|
|
|
display = [ "%i %s" % (idx+1,s.description)
|
|
|
|
for idx,s in enumerate(found_snippets) ]
|
|
|
|
|
|
|
|
# TODO: this code is also mirrored below
|
|
|
|
try:
|
|
|
|
rv = vim.eval("inputlist(%s)" % display)
|
|
|
|
if rv is None or rv == '0':
|
|
|
|
return True
|
|
|
|
rv = int(rv)
|
|
|
|
if rv > len(found_snippets):
|
|
|
|
rv = len(found_snippets)
|
|
|
|
snippet = found_snippets[rv-1]
|
2009-09-17 14:11:43 +02:00
|
|
|
except: # vim.error, e:
|
2009-08-16 20:55:08 +02:00
|
|
|
if str(e) == 'invalid expression':
|
|
|
|
return True
|
|
|
|
raise
|
|
|
|
|
|
|
|
# TODO: even more code duplicated below
|
|
|
|
# Adjust before, maybe the trigger is not the complete word
|
|
|
|
text_before = before.rstrip()[:-len(word)]
|
|
|
|
text_before += word[:-len(snippet.trigger)]
|
|
|
|
|
|
|
|
self._expect_move_wo_change = True
|
|
|
|
if self._cs:
|
|
|
|
# Determine position
|
|
|
|
pos = self._vstate.pos
|
|
|
|
p_start = self._ctab.abs_start
|
|
|
|
|
|
|
|
if pos.line == p_start.line:
|
|
|
|
end = Position(0, pos.col - p_start.col)
|
|
|
|
else:
|
|
|
|
end = Position(pos.line - p_start.line, pos.col)
|
|
|
|
start = Position(end.line, end.col - len(snippet.trigger))
|
|
|
|
|
|
|
|
si = snippet.launch(text_before, self._ctab, start, end)
|
|
|
|
|
|
|
|
self._update_vim_buffer()
|
|
|
|
|
|
|
|
if si.has_tabs:
|
|
|
|
self._csnippets.append(si)
|
|
|
|
self._jump()
|
|
|
|
else:
|
|
|
|
self._vb = VimBuffer(text_before, after)
|
|
|
|
|
|
|
|
start = Position(lineno-1, len(text_before))
|
|
|
|
self._csnippets.append(snippet.launch(text_before, None, start))
|
|
|
|
|
|
|
|
self._vb.replace_lines(lineno-1, lineno-1,
|
|
|
|
self._cs._current_text)
|
|
|
|
|
|
|
|
self._jump()
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
2009-08-16 18:50:14 +02:00
|
|
|
|
2009-07-21 10:21:05 +02:00
|
|
|
def expand_or_jump(self):
|
|
|
|
"""
|
|
|
|
This function is used for people who wants to have the same trigger for
|
|
|
|
expansion and forward jumping. It first tries to expand a snippet, if
|
|
|
|
this fails, it tries to jump forward.
|
|
|
|
"""
|
|
|
|
rv = self._try_expand()
|
|
|
|
if not rv:
|
|
|
|
rv = self._jump()
|
|
|
|
if not rv:
|
|
|
|
self._handle_failure(self.expand_trigger)
|
2009-07-16 14:10:59 +02:00
|
|
|
|
2009-08-02 11:29:42 +02:00
|
|
|
def add_snippet(self, trigger, value, descr, options, ft = "all"):
|
|
|
|
if ft not in self._snippets:
|
2009-08-02 11:57:43 +02:00
|
|
|
self._snippets[ft] = _SnippetDictionary()
|
2009-08-16 16:42:32 +02:00
|
|
|
l = self._snippets[ft].add_snippet(
|
|
|
|
Snippet(trigger, value, descr, options)
|
|
|
|
)
|
2009-07-04 21:58:13 +02:00
|
|
|
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
def clear_snippets(self, triggers = [], ft = "all"):
|
|
|
|
if ft in self._snippets:
|
|
|
|
self._snippets[ft].clear_snippets(triggers)
|
|
|
|
|
2009-08-02 11:57:43 +02:00
|
|
|
def add_extending_info(self, ft, parents):
|
|
|
|
if ft not in self._snippets:
|
|
|
|
self._snippets[ft] = _SnippetDictionary()
|
|
|
|
sd = self._snippets[ft]
|
|
|
|
for p in parents:
|
|
|
|
if p in sd.extends:
|
|
|
|
continue
|
|
|
|
|
|
|
|
sd.extends.append(p)
|
|
|
|
|
2009-07-21 10:21:05 +02:00
|
|
|
|
|
|
|
def backspace_while_selected(self):
|
|
|
|
"""
|
|
|
|
This is called when backspace was used while a placeholder was selected.
|
|
|
|
"""
|
|
|
|
# BS was called in select mode
|
|
|
|
|
|
|
|
if self._cs and (self._span_selected is not None):
|
|
|
|
# This only happens when a default value is delted using backspace
|
|
|
|
vim.command(r'call feedkeys("i")')
|
|
|
|
self._chars_entered('')
|
|
|
|
else:
|
|
|
|
vim.command(r'call feedkeys("\<BS>")')
|
|
|
|
|
|
|
|
def cursor_moved(self):
|
|
|
|
self._vstate.update()
|
|
|
|
|
|
|
|
if not self._vstate.buf_changed and not self._expect_move_wo_change:
|
|
|
|
self._check_if_still_inside_snippet()
|
|
|
|
|
|
|
|
if not self._ctab:
|
|
|
|
return
|
|
|
|
|
|
|
|
if self._vstate.buf_changed and self._ctab:
|
|
|
|
# Detect a carriage return
|
|
|
|
if self._vstate.moved.col <= 0 and self._vstate.moved.line == 1:
|
|
|
|
# Multiple things might have happened: either the user entered
|
|
|
|
# a newline character or pasted some text which means we have
|
|
|
|
# to copy everything he entered on the last line and keep the
|
|
|
|
# indent vim chose for this line.
|
|
|
|
lline = vim.current.buffer[self._vstate.ppos.line]
|
|
|
|
|
|
|
|
# Another thing that might have happened is that a word
|
|
|
|
# wrapped, in this case the last line is shortened and we must
|
2009-07-29 09:49:44 +02:00
|
|
|
# delete what Vim deleted there
|
2009-07-21 10:21:05 +02:00
|
|
|
line_was_shortened = len(self._vstate.last_line) > len(lline)
|
2009-07-29 09:49:44 +02:00
|
|
|
|
|
|
|
# Another thing that might have happened is that vim has
|
|
|
|
# adjusted the indent of the last line and therefore the line
|
|
|
|
# effectivly got longer. This means a newline was entered and
|
|
|
|
# we quite definitivly do not want the indent that vim added
|
|
|
|
line_was_lengthened = len(lline) > len(self._vstate.last_line)
|
|
|
|
|
2009-07-21 10:21:05 +02:00
|
|
|
user_didnt_enter_newline = len(lline) != self._vstate.ppos.col
|
2009-07-29 09:49:44 +02:00
|
|
|
cline = vim.current.buffer[self._vstate.pos.line]
|
|
|
|
if line_was_lengthened:
|
|
|
|
this_entered = vim.current.line[:self._vstate.pos.col]
|
|
|
|
self._chars_entered('\n' + cline + this_entered, 1)
|
2009-07-21 10:21:05 +02:00
|
|
|
if line_was_shortened and user_didnt_enter_newline:
|
|
|
|
self._backspace(len(self._vstate.last_line)-len(lline))
|
|
|
|
self._chars_entered('\n' + cline, 1)
|
|
|
|
else:
|
|
|
|
pentered = lline[self._vstate.ppos.col:]
|
|
|
|
this_entered = vim.current.line[:self._vstate.pos.col]
|
|
|
|
|
|
|
|
self._chars_entered(pentered + '\n' + this_entered)
|
|
|
|
elif self._vstate.moved.line == 0 and self._vstate.moved.col<0:
|
|
|
|
# Some deleting was going on
|
|
|
|
self._backspace(-self._vstate.moved.col)
|
|
|
|
elif self._vstate.moved.line < 0:
|
|
|
|
# Backspace over line end
|
|
|
|
self._backspace(1)
|
|
|
|
else:
|
|
|
|
line = vim.current.line
|
|
|
|
|
|
|
|
chars = line[self._vstate.pos.col - self._vstate.moved.col:
|
|
|
|
self._vstate.pos.col]
|
|
|
|
self._chars_entered(chars)
|
|
|
|
|
|
|
|
self._expect_move_wo_change = False
|
|
|
|
|
|
|
|
def entered_insert_mode(self):
|
|
|
|
self._vstate.update()
|
|
|
|
if self._cs and self._vstate.has_moved:
|
|
|
|
self._reinit()
|
|
|
|
self._csnippets = []
|
|
|
|
|
|
|
|
###################################
|
|
|
|
# Private/Protect Functions Below #
|
|
|
|
###################################
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
def _error(self, msg):
|
2009-10-13 09:56:12 +02:00
|
|
|
msg = _vim_quote("UltiSnips: " + msg)
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
if self._test_error:
|
|
|
|
msg = msg.replace('"', r'\"')
|
|
|
|
msg = msg.replace('|', r'\|')
|
|
|
|
vim.command("let saved_pos=getpos('.')")
|
|
|
|
vim.command("$:put =%s" % msg)
|
|
|
|
vim.command("call setpos('.', saved_pos)")
|
|
|
|
elif False:
|
|
|
|
vim.command("echohl WarningMsg")
|
|
|
|
vim.command("echomsg %s" % msg)
|
|
|
|
vim.command("echohl None")
|
|
|
|
else:
|
|
|
|
vim.command("echoerr %s" % msg)
|
|
|
|
|
2009-07-21 10:21:05 +02:00
|
|
|
def _reinit(self):
|
|
|
|
self._ctab = None
|
|
|
|
self._span_selected = None
|
|
|
|
self._expect_move_wo_change = False
|
|
|
|
|
|
|
|
def _check_if_still_inside_snippet(self):
|
|
|
|
# Cursor moved without input.
|
|
|
|
self._ctab = None
|
|
|
|
|
|
|
|
# Did we leave the snippet with this movement?
|
|
|
|
if self._cs and not (self._vstate.pos in self._cs.abs_span):
|
|
|
|
self._csnippets.pop()
|
|
|
|
|
|
|
|
self._reinit()
|
|
|
|
|
|
|
|
self._check_if_still_inside_snippet()
|
2009-07-22 12:08:21 +02:00
|
|
|
|
2009-07-21 10:21:05 +02:00
|
|
|
def _jump(self, backwards = False):
|
2009-08-25 19:18:06 -04:00
|
|
|
jumped = False
|
2009-07-16 14:10:59 +02:00
|
|
|
if self._cs:
|
2009-07-05 22:25:01 +02:00
|
|
|
self._expect_move_wo_change = True
|
2009-07-16 14:10:59 +02:00
|
|
|
self._ctab = self._cs.select_next_tab(backwards)
|
2009-07-09 14:17:35 +02:00
|
|
|
if self._ctab:
|
2009-07-09 14:24:43 +02:00
|
|
|
self._vstate.select_span(self._ctab.abs_span)
|
2009-07-10 12:06:58 +02:00
|
|
|
self._span_selected = self._ctab.abs_span
|
2009-08-25 19:18:06 -04:00
|
|
|
jumped = True
|
|
|
|
if self._ctab.no == 0:
|
|
|
|
self._ctab = None
|
|
|
|
self._csnippets.pop()
|
|
|
|
self._vstate.update()
|
2009-07-09 14:17:35 +02:00
|
|
|
else:
|
2009-08-25 19:18:06 -04:00
|
|
|
# This really shouldn't happen, because a snippet should
|
|
|
|
# have been popped when its final tabstop was used.
|
|
|
|
# Cleanup by removing current snippet and recursing.
|
2009-07-16 14:10:59 +02:00
|
|
|
self._csnippets.pop()
|
2009-08-25 19:18:06 -04:00
|
|
|
jumped = self._jump(backwards)
|
|
|
|
return jumped
|
2009-07-21 10:21:05 +02:00
|
|
|
|
|
|
|
def _handle_failure(self, trigger):
|
|
|
|
"""
|
|
|
|
Mainly make sure that we play well with SuperTab
|
|
|
|
"""
|
2009-08-24 06:28:54 -04:00
|
|
|
if trigger.lower() == "<tab>":
|
|
|
|
feedkey = "\\" + trigger
|
|
|
|
else:
|
|
|
|
feedkey = None
|
|
|
|
mode = "n"
|
2009-07-21 10:21:05 +02:00
|
|
|
if not self._supertab_keys:
|
|
|
|
if vim.eval("exists('g:SuperTabMappingForward')") != "0":
|
|
|
|
self._supertab_keys = (
|
|
|
|
vim.eval("g:SuperTabMappingForward"),
|
|
|
|
vim.eval("g:SuperTabMappingBackward"),
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
self._supertab_keys = [ '', '' ]
|
|
|
|
|
|
|
|
for idx, sttrig in enumerate(self._supertab_keys):
|
|
|
|
if trigger.lower() == sttrig.lower():
|
|
|
|
if idx == 0:
|
|
|
|
feedkey= r"\<c-n>"
|
|
|
|
elif idx == 1:
|
|
|
|
feedkey = r"\<c-p>"
|
2009-08-24 06:28:54 -04:00
|
|
|
# Use remap mode so SuperTab mappings will be invoked.
|
|
|
|
mode = "m"
|
2009-07-21 10:21:05 +02:00
|
|
|
break
|
|
|
|
|
|
|
|
if feedkey:
|
2009-08-24 06:28:54 -04:00
|
|
|
vim.command(r'call feedkeys("%s", "%s")' % (feedkey, mode))
|
2009-07-21 10:21:05 +02:00
|
|
|
|
2009-08-16 20:55:08 +02:00
|
|
|
def _ensure_snippets_loaded(self):
|
2009-07-17 23:00:59 +02:00
|
|
|
filetypes = vim.eval("&filetype").split(".") + [ "all" ]
|
|
|
|
for ft in filetypes[::-1]:
|
|
|
|
if len(ft) and ft not in self._snippets:
|
|
|
|
self._load_snippets_for(ft)
|
2009-07-09 15:30:23 +02:00
|
|
|
|
2009-08-16 20:55:08 +02:00
|
|
|
return filetypes
|
|
|
|
|
|
|
|
def _try_expand(self):
|
|
|
|
filetypes = self._ensure_snippets_loaded()
|
|
|
|
|
2009-07-09 15:30:23 +02:00
|
|
|
self._expect_move_wo_change = False
|
2009-06-28 22:22:19 +02:00
|
|
|
|
2009-07-09 13:49:15 +02:00
|
|
|
lineno,col = vim.current.window.cursor
|
2009-07-04 12:14:13 +02:00
|
|
|
if col == 0:
|
2009-07-04 16:08:14 +02:00
|
|
|
return False
|
2009-07-04 12:14:13 +02:00
|
|
|
|
|
|
|
line = vim.current.line
|
2009-06-23 14:45:04 +02:00
|
|
|
|
|
|
|
if col > 0 and line[col-1] in string.whitespace:
|
2009-07-04 16:08:14 +02:00
|
|
|
return False
|
2009-06-23 14:45:04 +02:00
|
|
|
|
|
|
|
# Get the word to the left of the current edit position
|
|
|
|
before,after = line[:col], line[col:]
|
|
|
|
|
|
|
|
word = before.split()[-1]
|
2009-07-19 12:56:10 +02:00
|
|
|
found_snippets = []
|
|
|
|
for ft in filetypes[::-1]:
|
|
|
|
found_snippets += self._find_snippets(ft, word)
|
2009-07-04 12:14:13 +02:00
|
|
|
|
2009-07-17 23:33:48 +02:00
|
|
|
# Search if any of the snippets overwrites the previous
|
2009-07-19 12:56:10 +02:00
|
|
|
snippets = []
|
|
|
|
for s in found_snippets:
|
|
|
|
if s.overwrites_previous:
|
|
|
|
snippets = []
|
|
|
|
snippets.append(s)
|
2009-07-17 23:33:48 +02:00
|
|
|
|
2009-07-18 00:51:19 +02:00
|
|
|
# Check if there are any only whitespace in front snippets
|
|
|
|
text_before = before.rstrip()[:-len(word)]
|
|
|
|
if text_before.strip(" \t") != '':
|
|
|
|
snippets = [ s for s in snippets if not s.needs_ws_in_front ]
|
|
|
|
|
2009-07-04 21:58:13 +02:00
|
|
|
if not len(snippets):
|
2009-07-04 12:14:13 +02:00
|
|
|
# No snippet found
|
2009-07-04 16:08:14 +02:00
|
|
|
return False
|
2009-07-04 21:58:13 +02:00
|
|
|
elif len(snippets) == 1:
|
|
|
|
snippet, = snippets
|
|
|
|
else:
|
|
|
|
display = repr(
|
|
|
|
[ "%i: %s" % (i+1,s.description) for i,s in
|
|
|
|
enumerate(snippets)
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2009-08-09 15:00:20 +02:00
|
|
|
try:
|
|
|
|
rv = vim.eval("inputlist(%s)" % display)
|
|
|
|
if rv is None or rv == '0':
|
|
|
|
return True
|
|
|
|
rv = int(rv)
|
|
|
|
if rv > len(snippets):
|
|
|
|
rv = len(snippets)
|
|
|
|
snippet = snippets[rv-1]
|
|
|
|
except vim.error, e:
|
|
|
|
if str(e) == 'invalid expression':
|
|
|
|
return True
|
|
|
|
raise
|
2009-07-04 12:14:13 +02:00
|
|
|
|
2009-08-16 16:34:54 +02:00
|
|
|
# Adjust before, maybe the trigger is not the complete word
|
|
|
|
text_before += word[:-len(snippet.trigger)]
|
|
|
|
|
2009-07-05 22:25:01 +02:00
|
|
|
self._expect_move_wo_change = True
|
2009-07-16 14:10:59 +02:00
|
|
|
if self._cs:
|
|
|
|
# Determine position
|
|
|
|
pos = self._vstate.pos
|
|
|
|
p_start = self._ctab.abs_start
|
|
|
|
|
|
|
|
if pos.line == p_start.line:
|
|
|
|
end = Position(0, pos.col - p_start.col)
|
|
|
|
else:
|
|
|
|
end = Position(pos.line - p_start.line, pos.col)
|
|
|
|
start = Position(end.line, end.col - len(snippet.trigger))
|
2009-07-18 00:14:20 +02:00
|
|
|
|
|
|
|
si = snippet.launch(text_before, self._ctab, start, end)
|
2009-07-16 17:00:25 +02:00
|
|
|
|
2009-07-16 14:10:59 +02:00
|
|
|
self._update_vim_buffer()
|
|
|
|
|
2009-07-16 17:00:25 +02:00
|
|
|
if si.has_tabs:
|
|
|
|
self._csnippets.append(si)
|
2009-07-21 10:21:05 +02:00
|
|
|
self._jump()
|
2009-07-16 14:10:59 +02:00
|
|
|
else:
|
|
|
|
self._vb = VimBuffer(text_before, after)
|
2009-07-09 13:49:15 +02:00
|
|
|
|
2009-07-16 14:10:59 +02:00
|
|
|
start = Position(lineno-1, len(text_before))
|
2009-07-18 00:14:20 +02:00
|
|
|
self._csnippets.append(snippet.launch(text_before, None, start))
|
2009-07-09 13:49:15 +02:00
|
|
|
|
2009-07-16 14:10:59 +02:00
|
|
|
self._vb.replace_lines(lineno-1, lineno-1,
|
|
|
|
self._cs._current_text)
|
2009-07-09 08:23:39 +02:00
|
|
|
|
2009-07-21 10:21:05 +02:00
|
|
|
self._jump()
|
2009-07-01 10:39:46 +02:00
|
|
|
|
2009-07-04 16:08:14 +02:00
|
|
|
return True
|
|
|
|
|
2009-07-16 10:16:30 +02:00
|
|
|
|
2009-07-09 15:30:23 +02:00
|
|
|
# Input Handling
|
2009-07-16 10:16:30 +02:00
|
|
|
def _chars_entered(self, chars, del_more_lines = 0):
|
2009-07-10 12:06:58 +02:00
|
|
|
if (self._span_selected is not None):
|
2009-07-09 14:06:37 +02:00
|
|
|
self._ctab.current_text = chars
|
2009-07-10 12:06:58 +02:00
|
|
|
|
|
|
|
moved = self._span_selected.start.line - \
|
|
|
|
self._span_selected.end.line
|
|
|
|
self._span_selected = None
|
|
|
|
|
2009-07-16 10:16:30 +02:00
|
|
|
self._update_vim_buffer(moved + del_more_lines)
|
2009-07-09 14:06:37 +02:00
|
|
|
else:
|
|
|
|
self._ctab.current_text += chars
|
2009-07-16 10:16:30 +02:00
|
|
|
self._update_vim_buffer(del_more_lines)
|
2009-07-09 15:30:23 +02:00
|
|
|
|
2009-07-09 14:13:13 +02:00
|
|
|
|
|
|
|
def _backspace(self, count):
|
|
|
|
self._ctab.current_text = self._ctab.current_text[:-count]
|
|
|
|
self._update_vim_buffer()
|
|
|
|
|
2009-07-10 12:06:58 +02:00
|
|
|
def _update_vim_buffer(self, del_more_lines = 0):
|
2009-07-16 14:10:59 +02:00
|
|
|
if not len(self._csnippets):
|
|
|
|
return
|
|
|
|
|
|
|
|
s = self._csnippets[0]
|
|
|
|
sline = s.abs_start.line
|
|
|
|
dlines = s.end.line - s.start.line
|
2009-07-09 14:13:13 +02:00
|
|
|
|
2009-07-16 14:10:59 +02:00
|
|
|
s.update()
|
2009-07-09 14:13:13 +02:00
|
|
|
|
2009-07-09 14:06:37 +02:00
|
|
|
# Replace
|
2009-07-10 12:06:58 +02:00
|
|
|
dlines += self._vstate.moved.line + del_more_lines
|
2009-07-09 14:06:37 +02:00
|
|
|
self._vb.replace_lines(sline, sline + dlines,
|
2009-07-16 14:10:59 +02:00
|
|
|
s._current_text)
|
2009-07-09 14:06:37 +02:00
|
|
|
ct_end = self._ctab.abs_end
|
|
|
|
vim.current.window.cursor = ct_end.line +1, ct_end.col
|
|
|
|
|
|
|
|
self._vstate.update()
|
2009-07-09 13:53:49 +02:00
|
|
|
|
2009-07-16 14:10:59 +02:00
|
|
|
def _cs(self):
|
|
|
|
if not len(self._csnippets):
|
|
|
|
return None
|
|
|
|
return self._csnippets[-1]
|
|
|
|
_cs = property(_cs)
|
|
|
|
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
def _parse_snippets(self, ft, fn, file_data=None):
|
|
|
|
_SnippetsFileParser(ft, fn, self, file_data).parse()
|
|
|
|
|
2009-07-09 15:30:23 +02:00
|
|
|
# Loading
|
|
|
|
def _load_snippets_for(self, ft):
|
2009-08-02 11:57:43 +02:00
|
|
|
self._snippets[ft] = _SnippetDictionary()
|
2009-07-09 15:30:23 +02:00
|
|
|
for p in vim.eval("&runtimepath").split(',')[::-1]:
|
2009-07-10 12:47:54 +02:00
|
|
|
pattern = p + os.path.sep + "UltiSnips" + os.path.sep + \
|
2009-07-09 15:30:23 +02:00
|
|
|
"*%s.snippets" % ft
|
|
|
|
|
|
|
|
for fn in glob.glob(pattern):
|
A "clearsnippets" feature
=========================
It's difficult for the user to control which of the default
bundled snippets are active in his environment. The
'runtimepath' variable must be set to the root of the ultisnips
installation, which brings in all of the bundled snippets.
Though the user may individually override the definition of the
bundled snippets using the "!" flag, the method has a couple of
problems:
- There's no way to remove a snippet, only to override it (and
each snippet must be overridden individually).
- The "!" flag currently doesn't remove the overridden snippets
from the "list snippets" command.
It might be considered a feature that "!" doesn't actually
remove the snippets from the "list snippets" command, though
perhaps that's an unintended effect. In any case, it would be
more convenient to allow the user to selectively remove the
bundled snippets from his environment.
A patch is provided in the following branch to address these problems:
http://code.launchpad.net/~drmikehenry/ultisnips/clearsnippets
The branch's primary purpose is the addition of a
"clearsnippets" command that may be placed in a user's
~/.vim/UltiSnips/ft.snippets file. The user may clear all
lower-priority snippet for that file type with the line:
clearsnippets
Alternatively, he may clear individual snippets by listing their
triggers:
clearsnippets trigger1 trigger2
A few changes were made to the testing system as part of the
incorporation of this new feature. These changes include:
- The "extends" directive is now supported on multiple lines
throughout file.
- A completely empty .snippets file is now possible.
- The test.py scripts now handles most of the vim setup,
simplifying the running of the tests. The invocation of Vim
now reduces to:
vim -u NONE
Instructions for running the tests are included at top of
test.py, where they should be more visible to interested
users; UltiSnips.vim now just points to test.py's
instructions.
- A new function vim_quote() encodes an arbitrary string into a
singly-quoted Vim string, with embedded quotes escaped.
- SnippetsFileParser() now allows file_data to be passed
directly for unit testing, avoiding the need to create files
in the filesystem for test purposes.
- A new _error() function reports errors to the user. At
runtime, this function uses :echo_err in general, but also can
append error text to current buffer to check for expected
errors during unit tests.
- Added error checks to snippets file parsing, along with unit
tests for the parsing.
- Increased retries from 2 to 4 (on my system, occasionally the
timing still causes tests to fail).
2009-09-08 20:15:10 -04:00
|
|
|
self._parse_snippets(ft, fn)
|
2009-07-09 13:49:15 +02:00
|
|
|
|
2009-08-02 11:57:43 +02:00
|
|
|
# Now load for the parents
|
|
|
|
for p in self._snippets[ft].extends:
|
|
|
|
if p not in self._snippets:
|
|
|
|
self._load_snippets_for(p)
|
2009-07-09 15:30:23 +02:00
|
|
|
|
2009-08-16 20:55:08 +02:00
|
|
|
def _find_snippets(self, ft, trigger, potentially = False):
|
|
|
|
"""
|
|
|
|
Find snippets matching trigger
|
|
|
|
|
|
|
|
ft - file type to search
|
|
|
|
trigger - trigger to match against
|
|
|
|
potentially - also returns snippets that could potentially match; that
|
|
|
|
is which triggers start with the current trigger
|
|
|
|
"""
|
|
|
|
|
2009-07-09 15:30:23 +02:00
|
|
|
snips = self._snippets.get(ft,None)
|
|
|
|
if not snips:
|
|
|
|
return []
|
|
|
|
|
2009-08-02 11:57:43 +02:00
|
|
|
parent_results = reduce( lambda a,b: a+b,
|
2009-08-16 20:55:08 +02:00
|
|
|
[ self._find_snippets(p, trigger, potentially)
|
|
|
|
for p in snips.extends ], [])
|
2009-08-02 11:57:43 +02:00
|
|
|
|
2009-08-16 20:55:08 +02:00
|
|
|
return parent_results + snips.get_matching_snippets(
|
|
|
|
trigger, potentially)
|
2009-07-09 15:30:23 +02:00
|
|
|
|
2009-06-23 14:45:04 +02:00
|
|
|
|
2009-07-10 12:47:54 +02:00
|
|
|
UltiSnips_Manager = SnippetManager()
|
2009-07-01 20:14:54 +02:00
|
|
|
|