Merged my fix for bug 427298

This commit is contained in:
Holger Rapp 2010-08-20 10:30:33 +02:00
commit 00656a2bb7
5 changed files with 199 additions and 39 deletions

View File

@ -1,5 +1,7 @@
version 1.2: version 1.2:
- many bugs were fixed - many bugs were fixed
- smode mappings for printable characters are now removed before expanding a
snippet. This is configurable. *UltiSnips-warning-smappings*
- all shipped snippets are now fully compatible with UltiSnips - all shipped snippets are now fully compatible with UltiSnips
- added support for global snippets which enhance python interpolation even - added support for global snippets which enhance python interpolation even
more *UltiSnips-globals* more *UltiSnips-globals*

View File

@ -2,15 +2,16 @@
UltiSnips *snippet* *snippets* *UltiSnips* UltiSnips *snippet* *snippets* *UltiSnips*
1. Description |UltiSnips-description| 1. Description |UltiSnips-description|
2. Installation and Updating |UltiSnips-installnupgrade| 2. Installation and Updating |UltiSnips-installnupgrade|
2.1 Installation |UltiSnips-installation| 2.1 Installation |UltiSnips-installation|
2.2 Deinstallation |UltiSnips-deinstallation| 2.2 Deinstallation |UltiSnips-deinstallation|
2.3 Updating |UltiSnips-updating| 2.3 Updating |UltiSnips-updating|
3. Settings |UltiSnips-settings| 3. Settings |UltiSnips-settings|
4. Syntax |UltiSnips-syntax| 3.1 Warning About Select Mode Mappings |UltiSnips-warning-smappings|
4. Syntax |UltiSnips-syntax|
4.1 Adding Snippets |UltiSnips-adding-snippets| 4.1 Adding Snippets |UltiSnips-adding-snippets|
4.2 Plaintext snippets |UltiSnips-plaintext-snippets| 4.2 Plaintext Snippets |UltiSnips-plaintext-snippets|
4.3 Interpolation |UltiSnips-integration| 4.3 Interpolation |UltiSnips-integration|
4.3.1 Shellcode |UltiSnips-shellcode| 4.3.1 Shellcode |UltiSnips-shellcode|
4.3.2 VimScript |UltiSnips-vimscript| 4.3.2 VimScript |UltiSnips-vimscript|
@ -25,14 +26,14 @@ UltiSnips *snippet* *snippets* *UltiSnips*
5. Helping out |UltiSnips-helping| 5. Helping out |UltiSnips-helping|
6. Contact |UltiSnips-contact| 6. Contact |UltiSnips-contact|
7. Contributors |UltiSnips-contributors| 7. Contributors |UltiSnips-contributors|
8.1 Patches & Coding |UltiSnips-contricoding| 7.1 Patches & Coding |UltiSnips-contricoding|
8.2 Snippets |UltiSnips-contrisnippets| 7.2 Snippets |UltiSnips-contrisnippets|
For Vim version 7.0 or later. For Vim version 7.0 or later.
This plugin only works if 'compatible' is not set. This plugin only works if 'compatible' is not set.
{Vi does not have any of these features} {Vi does not have any of these features}
This plugin needs Python support compiled into Vim. This plugin needs Python support compiled into Vim.
============================================================================= =============================================================================
DESCRIPTION *UltiSnips-description* DESCRIPTION *UltiSnips-description*
@ -45,7 +46,7 @@ signatures and to insert the current date or time into the text while typing.
UltiSnips is an implementation that is developed with in the philosophy of TDD UltiSnips is an implementation that is developed with in the philosophy of TDD
(Test driven development). This guarantees that features do not disappear and (Test driven development). This guarantees that features do not disappear and
bugs do not reappear after they have been fixed once. bugs do not reappear after they have been fixed once.
Acknowledgments: *UltiSnips-acknowledgments* Acknowledgments: *UltiSnips-acknowledgments*
@ -64,9 +65,9 @@ his snippets.
UltiSnips has only been tested on Mac OS X and Linux. Like TextMates Snippets UltiSnips has only been tested on Mac OS X and Linux. Like TextMates Snippets
it also relies heavily on shell integration, therefore Windows users will have it also relies heavily on shell integration, therefore Windows users will have
only a part of the functionality. only a part of the functionality.
To use UltiSnips, you need a python enabled Vim 7. You have python if To use UltiSnips, you need a python enabled Vim 7. You have python if
:echo has("python") :echo has("python")
yields '1'. yields '1'.
@ -105,7 +106,7 @@ remove the line: >
To Update an installation, simply pull the latest revision: > To Update an installation, simply pull the latest revision: >
$ cd ~/.vim/ultisnips_rep $ cd ~/.vim/ultisnips_rep
$ bzr pull $ bzr pull
============================================================================= =============================================================================
3. SETTINGS *UltiSnips-settings* 3. SETTINGS *UltiSnips-settings*
@ -121,15 +122,40 @@ current expand situation. The variables with the default values are: >
It is possible to set the Expand and Forward trigger to the same value. You It is possible to set the Expand and Forward trigger to the same value. You
can get a more TextMate like configuration by adding the next three lines to can get a more TextMate like configuration by adding the next three lines to
you vimrc: > you vimrc: >
let g:UltiSnipsExpandTrigger="<tab>" let g:UltiSnipsExpandTrigger="<tab>"
let g:UltiSnipsJumpForwardTrigger="<tab>" let g:UltiSnipsJumpForwardTrigger="<tab>"
let g:UltiSnipsJumpBackwardTrigger="<s-tab>" let g:UltiSnipsJumpBackwardTrigger="<s-tab>"
3.1 Warning About Select Mode Mappings *UltiSnips-warning-smappings*
--------------------------------------
The help for vim's *mapmode-s* states: >
NOTE: Mapping a printable character in Select mode may confuse the user.
It's better to explicitly use :xmap and :smap for printable characters. Or
use :sunmap after defining the mapping.
But most vim plugins (even the ones shipped with vim) do not adhere to this.
As UltiSnips uses select mode to mark tabstops in snippets for overwriting
these wrong mappings get in the way. UltiSnips therefore defaults to
*:sunmap* all mappings for printable characters in select mode. It will not
touch any other mappings, especially not normal, insert or visual mode
mappings.
If this behaviour is not desired, you can disable it via >
let g:UltiSnipsRemoveSelectModeMappings = 0
If you want to keep certain mappings, you can modify the list of ignores for
this feature. For example >
let g:UltiSnipsMappingsToIgnore = [ "somePlugin", "otherPlugin" ]
will not unmap any mapping that contains the string "somePlugin" or
"otherPlugin" in its complete definition as listed by *:smap* .
============================================================================= =============================================================================
4. SYNTAX *UltiSnips-syntax* 4. SYNTAX *UltiSnips-syntax*
This chapter describes where to add new snippets and how to write them. I'll This chapter describes where to add new snippets and how to write them. I'll
provide many examples to make things clearer. provide many examples to make things clearer.
4.1 Adding Snippets *UltiSnips-adding-snippets* 4.1 Adding Snippets *UltiSnips-adding-snippets*
------------------- -------------------
@ -144,7 +170,7 @@ These files contain the snippet definitions for the various file types. A
special file is > special file is >
all.snippets all.snippets
which contains snippets that are always expanded, no matter what file type is which contains snippets that are always expanded, no matter what file type is
defined. For example, I keep mail signatures and date insertion snippets here. defined. For example, I keep mail signatures and date insertion snippets here.
The dotted file type syntax of vim is supported. For example, for my cpp or The dotted file type syntax of vim is supported. For example, for my cpp or
CUDA files, i keep the file type set to ":set ft=cpp.c" or ":set CUDA files, i keep the file type set to ":set ft=cpp.c" or ":set
@ -176,7 +202,7 @@ if you actually want to include the double-quote characters in the trigger.
The description is only needed when more than one snippet is defined with the The description is only needed when more than one snippet is defined with the
same tab trigger. The user is then given a choice and the description is same tab trigger. The user is then given a choice and the description is
displayed for the user as helper. displayed for the user as helper.
The options is a word of characters, each turning a specific option for this The options is a word of characters, each turning a specific option for this
snippet on. The options currently supported are > snippet on. The options currently supported are >
@ -188,7 +214,7 @@ snippet on. The options currently supported are >
the beginning of a line (that is if there are only whitespaces in front the beginning of a line (that is if there are only whitespaces in front
of the cursor). Default is to expand snippets at every position, even of the cursor). Default is to expand snippets at every position, even
mitten in the line. Most of my snippets have this option set, it keeps mitten in the line. Most of my snippets have this option set, it keeps
UltiSnips out of the way. UltiSnips out of the way.
i Inword expansion - Normally, triggers need whitespace before them to be i Inword expansion - Normally, triggers need whitespace before them to be
expanded. With this option, triggers are also expanded in the middle of expanded. With this option, triggers are also expanded in the middle of
a word. a word.
@ -204,7 +230,7 @@ snippet on. The options currently supported are >
match is also passed to any python code blocks in your snippet match is also passed to any python code blocks in your snippet
definition as the local variable "match". definition as the local variable "match".
4.2 Plaintext snippets *UltiSnips-plaintext-snippets* 4.2 Plaintext Snippets *UltiSnips-plaintext-snippets*
---------------------- ----------------------
Lets start with a simple example. Add this to your all snippets file: > Lets start with a simple example. Add this to your all snippets file: >
@ -277,7 +303,7 @@ similar to the one for Vimscript, but in python code the value of the property
"rv" on the "snip" object is inserted at the position of the code. Also, the "rv" on the "snip" object is inserted at the position of the code. Also, the
code is inside a `!p ` block. Python code gets some special variables defined code is inside a `!p ` block. Python code gets some special variables defined
which can be used: > which can be used: >
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] -> current text of ${1} and so on t - The values of the placeholders, t[1] -> current text of ${1} and so on
@ -289,7 +315,7 @@ The snip object provides the following methods: >
Returns a line ready to be appended to the result. If indent Returns a line ready to be appended to the result. If indent
is None, then mkline prepends spaces and/or tabs appropriate to the is None, then mkline prepends spaces and/or tabs appropriate to the
current tabstop and expandtab variables. current tabstop and expandtab variables.
snip.shift(amount=1): snip.shift(amount=1):
Shifts the default indentation level used by mkline right by the Shifts the default indentation level used by mkline right by the
number of spaces defined by shiftwidth, 'amount' times. number of spaces defined by shiftwidth, 'amount' times.
@ -300,7 +326,7 @@ The snip object provides the following methods: >
snip.reset_indent(): snip.reset_indent():
Resets the indentation level to its initial value. Resets the indentation level to its initial value.
snip.opt(var, default): snip.opt(var, default):
Checks if the vim variable "var" has been set, if so, it returns it, Checks if the vim variable "var" has been set, if so, it returns it,
otherwise it returns "default". otherwise it returns "default".
@ -343,11 +369,11 @@ uppercase and right aligned:
------------------- SNIP ------------------- ------------------- SNIP -------------------
snippet wow snippet wow
${1:Text}`!p snip.rv = (75-2*len(t[1]))*' '+t[1].upper()` ${1:Text}`!p snip.rv = (75-2*len(t[1]))*' '+t[1].upper()`
endsnippet endsnippet
------------------- SNAP ------------------- ------------------- SNAP -------------------
wow<tab>Hello World -> wow<tab>Hello World ->
Hello World HELLO WORLD Hello World HELLO WORLD
4.3.4 Global Snippets: *UltiSnips-globals* 4.3.4 Global Snippets: *UltiSnips-globals*
@ -365,11 +391,11 @@ def upper_right(inp):
endglobal endglobal
snippet wow snippet wow
${1:Text}`!p snip.rv = upper_right(t[1])` ${1:Text}`!p snip.rv = upper_right(t[1])`
endsnippet endsnippet
------------------- SNAP ------------------- ------------------- SNAP -------------------
wow<tab>Hello World -> wow<tab>Hello World ->
Hello World HELLO WORLD Hello World HELLO WORLD
4.4 Tab Stops and Placeholders *UltiSnips-tabstops* *UltiSnips-placeholders* 4.4 Tab Stops and Placeholders *UltiSnips-tabstops* *UltiSnips-placeholders*
@ -391,7 +417,7 @@ more text
You can use <c-j> and <c-k> to jump to the next tab or the previous. <Tab> was You can use <c-j> and <c-k> to jump to the next tab or the previous. <Tab> was
not used for jumping forward because many people (like myself) use <tab> also not used for jumping forward because many people (like myself) use <tab> also
for completion. $0 is a special tab: This is the last tab in the snippet, no for completion. $0 is a special tab: This is the last tab in the snippet, no
matter how many tabs were defined before. matter how many tabs were defined before.
Most of the time it is more useful to have some default text for a tabstop and Most of the time it is more useful to have some default text for a tabstop and
sometimes it is also useful to have a tab stop in a tab stop. Consider the sometimes it is also useful to have a tab stop in a tab stop. Consider the
@ -423,7 +449,7 @@ a<tab>http://www.google.com<c-j><c-j>visited<c-j>hi ->
</a> </a>
Default tab stop text can also contain mirrors, transformations or Default tab stop text can also contain mirrors, transformations or
interpolation. interpolation.
4.6 Mirrors *UltiSnips-mirrors* 4.6 Mirrors *UltiSnips-mirrors*
----------- -----------
@ -440,7 +466,7 @@ endsnippet
------------------- SNAP ------------------- ------------------- SNAP -------------------
env<tab>itemize -> env<tab>itemize ->
\begin{itemize} \begin{itemize}
\end{itemize} \end{itemize}
Another typical example is #ifndef block in C code: Another typical example is #ifndef block in C code:
@ -462,13 +488,13 @@ ifndef<tab>WIN32 ->
------------------- -------------------
A complete description of the features follows, the chapter closes with some A complete description of the features follows, the chapter closes with some
demos, because this feature is rather mighty and hard to understand. demos, because this feature is rather mighty and hard to understand.
Transformations are like mirrors. But instead of just verbatim copying text Transformations are like mirrors. But instead of just verbatim copying text
from the original tab stop, a regular expression is matched to the content of from the original tab stop, a regular expression is matched to the content of
the referenced tab stop and a transformation is then applied to the matched the referenced tab stop and a transformation is then applied to the matched
pattern. UltiSnips follows the syntax and features of TextMate very closely pattern. UltiSnips follows the syntax and features of TextMate very closely
here. here.
A Transformation has the syntax > A Transformation has the syntax >
${<tab stop no/regular expression/replacement/options} ${<tab stop no/regular expression/replacement/options}
@ -485,7 +511,7 @@ The options can be any combination of >
just the first just the first
Regular expressions are not handled here. Python regular expressions are used Regular expressions are not handled here. Python regular expressions are used
internally, so the reference for matching is the re module of python: internally, so the reference for matching is the re module of python:
http://docs.python.org/library/re.html http://docs.python.org/library/re.html
The replacement string is using a own syntax and is handled in the next paragraph. The replacement string is using a own syntax and is handled in the next paragraph.
@ -495,7 +521,7 @@ The replacement string is using a own syntax and is handled in the next paragrap
The replacement string can contain $no variables to reference matched groups The replacement string can contain $no variables to reference matched groups
in the regular expression, $0 is special and yields the whole match. The in the regular expression, $0 is special and yields the whole match. The
replacement string can also contain special escape sequences: > replacement string can also contain special escape sequences: >
\u - Uppercase next letter; \u - Uppercase next letter;
\l - Lowercase next letter \l - Lowercase next letter
\U - Uppercase everything till the next \E \U - Uppercase everything till the next \E
\L - Lowercase everything till the next \E \L - Lowercase everything till the next \E
@ -508,7 +534,7 @@ replacements with the following syntax (?no:text:other text). This reads as
follows: if the group $no has matched, insert "text", otherwise insert "other follows: if the group $no has matched, insert "text", otherwise insert "other
text". "other text" could be skipped and would default to "", that is the text". "other text" could be skipped and would default to "", that is the
empty string. This is a very powerful feature to add optional text into empty string. This is a very powerful feature to add optional text into
snippets. snippets.
4.7.2 Demos: *UltiSnips-demos* 4.7.2 Demos: *UltiSnips-demos*
@ -587,10 +613,10 @@ overwrite individual triggers when redefining them using the '!' option
UltiSnips needs the help of a vibrant vim community to make it more useful UltiSnips needs the help of a vibrant vim community to make it more useful
tomorrow than it is today. Please consider joining this effort by providing tomorrow than it is today. Please consider joining this effort by providing
new snippets, new features or bug reports. new snippets, new features or bug reports.
If you like this plugin, please also vote for it on its vim script page. It is If you like this plugin, please also vote for it on its vim script page. It is
life changing for me, maybe it is for you too. life changing for me, maybe it is for you too.
You can find the page here: You can find the page here:
http://www.vim.org/scripts/script.php?script_id=2715 http://www.vim.org/scripts/script.php?script_id=2715
@ -599,7 +625,7 @@ http://www.vim.org/scripts/script.php?script_id=2715
You can reach me at SirVer -AT- gmx -ADOT- de. You can find the launchpad You can reach me at SirVer -AT- gmx -ADOT- de. You can find the launchpad
project for UltiSnips at https://launchpad.net/ultisnips/, there is also the project for UltiSnips at https://launchpad.net/ultisnips/, there is also the
bug tracker. bug tracker.
This project aims to be the one-for-all solution for Snippets for Vim. If you This project aims to be the one-for-all solution for Snippets for Vim. If you
miss a feature or find a bug, please contact me or file a support ticket. miss a feature or find a bug, please contact me or file a support ticket.

View File

@ -37,6 +37,16 @@ if !exists("g:UltiSnipsJumpBackwardTrigger")
let g:UltiSnipsJumpBackwardTrigger = "<c-k>" let g:UltiSnipsJumpBackwardTrigger = "<c-k>"
endif endif
" Should UltiSnips unmap select mode mappings automagically?
if !exists("g:UltiSnipsRemoveSelectModeMappings")
let g:UltiSnipsRemoveSelectModeMappings = 1
end
" If UltiSnips should remove Mappings, which should be ignored
if !exists("g:UltiSnipsMappingsToIgnore")
let g:UltiSnipsMappingsToIgnore = []
endif
" }}} " }}}
"" FUNCTIONS {{{ "" FUNCTIONS {{{

View File

@ -405,7 +405,6 @@ class LangMapTranslator(object):
return s.translate(self._maps[langmap]) return s.translate(self._maps[langmap])
class VimState(object): class VimState(object):
def __init__(self): def __init__(self):
self._abs_pos = None self._abs_pos = None
@ -454,6 +453,8 @@ class VimState(object):
self._cline = vim.current.buffer[line] self._cline = vim.current.buffer[line]
def select_span(self, r): def select_span(self, r):
self._unmap_select_mode_mapping()
delta = r.end - r.start delta = r.end - r.start
lineno, col = r.start.line, r.start.col lineno, col = r.start.line, r.start.col
@ -534,6 +535,60 @@ class VimState(object):
return self._lline return self._lline
last_line = property(last_line) last_line = property(last_line)
###########################
# Private functions below #
###########################
def _unmap_select_mode_mapping(self):
"""This function unmaps select mode mappings if so wished by the user.
Removes select mode mappings that can actually be typed by the user
(ie, ignores things like <Plug>).
"""
if int(vim.eval("g:UltiSnipsRemoveSelectModeMappings")):
ignores = vim.eval("g:UltiSnipsMappingsToIgnore") + ['UltiSnips']
for option in ("<buffer>", ""):
# Put all smaps into a var, and then read the var
vim.command(r"redir => _tmp_smaps | silent smap %s " % option +
"| redir END")
# Check if any mappings where found
all_maps = filter(len, vim.eval(r"_tmp_smaps").splitlines())
if (len(all_maps) == 1 and
all_maps[0][0] not in " sv"):
# "No maps found". String could be localized. Hopefully
# it doesn't start with any of these letters in any
# language
continue
# Only keep mappings that should not be ignored
maps = [m for m in all_maps if
not any(i in m for i in ignores) and len(m.strip())]
for m in maps:
# Some mappings have their modes listed
trig = m.split()
if m[0] == " ":
trig = trig[0]
else:
trig = trig[1]
# The bar separates commands
if trig[-1] == "|":
trig = trig[:-1] + "<Bar>"
# Special ones
if trig[0] == "<":
add = False
# Only allow these
for valid in ["Tab", "NL", "CR", "C-Tab", "BS"]:
if trig == "<%s>" % valid:
add = True
if not add:
continue
# Actually unmap it
vim.command("sunmap %s %s" % (option,trig))
class SnippetManager(object): class SnippetManager(object):
def __init__(self): def __init__(self):
self._vstate = VimState() self._vstate = VimState()

67
test.py
View File

@ -1933,6 +1933,73 @@ xj
k k
""" """
#######################
# Test for bug 427298 #
#######################
class _SelectModeMappings(_VimTest):
snippets = ("test", "${1:World}")
keys = "test" + EX + "Hello"
wanted = "Hello"
maps = ("", "")
buffer_maps = ("", "")
do_unmapping = True
ignores = []
def _options_on(self):
self.send(":let g:UltiSnipsRemoveSelectModeMappings=%i\n" %
int(self.do_unmapping))
self.send(":let g:UltiSnipsMappingsToIgnore=%s\n" %
repr(self.ignores))
if not isinstance(self.maps[0], tuple):
self.maps = (self.maps,)
if not isinstance(self.buffer_maps[0], tuple):
self.buffer_maps = (self.buffer_maps,)
for key, m in self.maps:
if not len(key): continue
self.send(":smap %s %s\n" % (key,m))
for key, m in self.buffer_maps:
if not len(key): continue
self.send(":smap <buffer> %s %s\n" % (key,m))
def _options_off(self):
for key, m in self.maps:
if not len(key): continue
self.send(":sunmap %s\n" % key)
for key, m in self.buffer_maps:
if not len(key): continue
self.send(":sunmap <buffer> %s\n" % key)
self.send(":let g:UltiSnipsRemoveSelectModeMappings=1\n")
self.send(":let g:UltiSnipsMappingsToIgnore= []\n")
class SelectModeMappings_RemoveBeforeSelecting_ECR(_SelectModeMappings):
maps = ("H", "x")
wanted = "Hello"
class SelectModeMappings_DisableRemoveBeforeSelecting_ECR(_SelectModeMappings):
do_unmapping = False
maps = ("H", "x")
wanted = "xello"
class SelectModeMappings_IgnoreMappings_ECR(_SelectModeMappings):
ignores = ["e"]
maps = ("H", "x"), ("e", "l")
wanted = "Hello"
class SelectModeMappings_IgnoreMappings1_ECR(_SelectModeMappings):
ignores = ["H"]
maps = ("H", "x"), ("e", "l")
wanted = "xello"
class SelectModeMappings_IgnoreMappings2_ECR(_SelectModeMappings):
ignores = ["e", "H"]
maps = ("e", "l"), ("H", "x")
wanted = "xello"
class SelectModeMappings_BufferLocalMappings_ECR(_SelectModeMappings):
buffer_maps = ("H", "blah")
wanted = "Hello"
########################################################################### ###########################################################################
# END OF TEST # # END OF TEST #