Visual now supports transformations
This commit is contained in:
parent
92209f99d2
commit
c1e5bb6c5f
@ -375,19 +375,23 @@ some text in visual mode and press the key to expand a trigger
|
||||
(see g:UltiSnipsExpandTrigger) the selected text will get deleted and you will
|
||||
drop to insert mode. The next snippet you expand will have the selected text
|
||||
in the place of the ${VISUAL}. The place holder can also contain default text
|
||||
when there is no visual text to be selected: ${VISUAL:default text}.
|
||||
As a small example, let's take this snippet:
|
||||
when there is no visual text to be selected: ${VISUAL:default text}. And it
|
||||
can also contain transformations on the contents of the visual (See
|
||||
*UltiSnips-transformations* ). This will take the form
|
||||
${VISUAL:default/search/replace/option}.
|
||||
|
||||
As a small example, let's take this snippet. It will take the visual text,
|
||||
replace all "should" via "is" and put it in.
|
||||
------------------- SNIP -------------------
|
||||
snippet t
|
||||
<tag>${VISUAL:inside text}</tag>
|
||||
<tag>${VISUAL:inside text/should/is/g}</tag>
|
||||
endsnippet
|
||||
------------------- SNAP -------------------
|
||||
And starting with this line: >
|
||||
this should be cool
|
||||
position the cursor on the should, then press viw (visual mode -> select inner
|
||||
word). Then press tab, type "t" and press tab again >
|
||||
-> this <tag>should</tag> be cool
|
||||
-> this <tag>is</tag> be cool
|
||||
If you expand this snippet without having been in visual mode before, e.g. by
|
||||
writing in insert mode t<tab>, you will get >
|
||||
<tag>inside text</tag>
|
||||
|
@ -87,21 +87,25 @@ def _parse_till_closing_brace(stream):
|
||||
rv += c
|
||||
return rv
|
||||
|
||||
def _parse_till_unescaped_char(stream, char):
|
||||
def _parse_till_unescaped_char(stream, chars):
|
||||
"""
|
||||
Returns all chars till a non-escaped `char` is found.
|
||||
Returns all chars till a non-escaped char is found.
|
||||
|
||||
Will also consume the closing `char`, but not return it
|
||||
Will also consume the closing char, but and return it as second
|
||||
return value
|
||||
"""
|
||||
rv = ""
|
||||
while True:
|
||||
if EscapeCharToken.starts_here(stream, char):
|
||||
rv += stream.next() + stream.next()
|
||||
else:
|
||||
escaped = False
|
||||
for c in chars:
|
||||
if EscapeCharToken.starts_here(stream, c):
|
||||
rv += stream.next() + stream.next()
|
||||
escaped = True
|
||||
if not escaped:
|
||||
c = stream.next()
|
||||
if c == char: break
|
||||
if c in chars: break
|
||||
rv += c
|
||||
return rv
|
||||
return rv, c
|
||||
# End: Helper functions }}}
|
||||
|
||||
# Tokens {{{
|
||||
@ -135,7 +139,7 @@ class TabStopToken(Token):
|
||||
)
|
||||
|
||||
class VisualToken(Token):
|
||||
CHECK = re.compile(r"^\${VISUAL[:}]")
|
||||
CHECK = re.compile(r"^\${VISUAL[:}/]")
|
||||
|
||||
@classmethod
|
||||
def starts_here(klass, stream):
|
||||
@ -147,7 +151,16 @@ class VisualToken(Token):
|
||||
|
||||
if stream.peek() == ":":
|
||||
stream.next()
|
||||
self.alternative_text = _parse_till_closing_brace(stream)
|
||||
self.alternative_text, c = _parse_till_unescaped_char(stream, '/}')
|
||||
|
||||
if c == '/': # Transformation going on
|
||||
self.search = _parse_till_unescaped_char(stream, '/')[0]
|
||||
self.replace = _parse_till_unescaped_char(stream, '/')[0]
|
||||
self.options = _parse_till_closing_brace(stream)
|
||||
else:
|
||||
self.search = None
|
||||
self.replace = None
|
||||
self.options = None
|
||||
|
||||
def __repr__(self):
|
||||
return "VisualToken(%r,%r)" % (
|
||||
@ -169,8 +182,8 @@ class TransformationToken(Token):
|
||||
|
||||
stream.next() # /
|
||||
|
||||
self.search = _parse_till_unescaped_char(stream, '/')
|
||||
self.replace = _parse_till_unescaped_char(stream, '/')
|
||||
self.search = _parse_till_unescaped_char(stream, '/')[0]
|
||||
self.replace = _parse_till_unescaped_char(stream, '/')[0]
|
||||
self.options = _parse_till_closing_brace(stream)
|
||||
|
||||
def __repr__(self):
|
||||
@ -217,7 +230,7 @@ class ShellCodeToken(Token):
|
||||
|
||||
def _parse(self, stream, indent):
|
||||
stream.next() # `
|
||||
self.code = _parse_till_unescaped_char(stream, '`')
|
||||
self.code = _parse_till_unescaped_char(stream, '`')[0]
|
||||
|
||||
def __repr__(self):
|
||||
return "ShellCodeToken(%r,%r,%r)" % (
|
||||
@ -237,7 +250,7 @@ class PythonCodeToken(Token):
|
||||
if stream.peek() in '\t ':
|
||||
stream.next()
|
||||
|
||||
code = _parse_till_unescaped_char(stream, '`')
|
||||
code = _parse_till_unescaped_char(stream, '`')[0]
|
||||
|
||||
# Strip the indent if any
|
||||
if len(indent):
|
||||
@ -264,7 +277,7 @@ class VimLCodeToken(Token):
|
||||
def _parse(self, stream, indent):
|
||||
for i in range(4):
|
||||
stream.next() # `!v
|
||||
self.code = _parse_till_unescaped_char(stream, '`')
|
||||
self.code = _parse_till_unescaped_char(stream, '`')[0]
|
||||
|
||||
def __repr__(self):
|
||||
return "VimLCodeToken(%r,%r,%r)" % (
|
||||
|
@ -100,10 +100,11 @@ class _CleverReplace(object):
|
||||
|
||||
return self._unescape(self._schar_escape(tv))
|
||||
|
||||
|
||||
class Transformation(Mirror):
|
||||
def __init__(self, parent, ts, token):
|
||||
Mirror.__init__(self, parent, ts, token)
|
||||
class TextObjectTransformation(object):
|
||||
def __init__(self, token):
|
||||
self._find = None
|
||||
if token.search is None:
|
||||
return
|
||||
|
||||
flags = 0
|
||||
self._match_this_many = 1
|
||||
@ -116,9 +117,17 @@ class Transformation(Mirror):
|
||||
self._find = re.compile(token.search, flags | re.DOTALL)
|
||||
self._replace = _CleverReplace(token.replace)
|
||||
|
||||
def _transform(self, text):
|
||||
if self._find is None:
|
||||
return text
|
||||
return self._find.subn(self._replace.replace, text, self._match_this_many)[0]
|
||||
|
||||
class Transformation(Mirror, TextObjectTransformation):
|
||||
def __init__(self, parent, ts, token):
|
||||
Mirror.__init__(self, parent, ts, token)
|
||||
TextObjectTransformation.__init__(self, token)
|
||||
|
||||
def _get_text(self):
|
||||
return self._find.subn(
|
||||
self._replace.replace, self._ts.current_text, self._match_this_many
|
||||
)[0]
|
||||
return self._transform(self._ts.current_text)
|
||||
|
||||
|
||||
|
@ -5,9 +5,10 @@ import re
|
||||
|
||||
import UltiSnips._vim as _vim
|
||||
from UltiSnips.util import IndentUtil
|
||||
from UltiSnips.text_objects._transformation import TextObjectTransformation
|
||||
from UltiSnips.text_objects._base import NoneditableTextObject
|
||||
|
||||
class Visual(NoneditableTextObject):
|
||||
class Visual(NoneditableTextObject,TextObjectTransformation):
|
||||
"""
|
||||
A ${VISUAL} placeholder that will use the text that was last visually
|
||||
selected and insert it here. If there was no text visually selected,
|
||||
@ -30,6 +31,7 @@ class Visual(NoneditableTextObject):
|
||||
self._mode = "v"
|
||||
|
||||
NoneditableTextObject.__init__(self, parent, token)
|
||||
TextObjectTransformation.__init__(self, token)
|
||||
|
||||
def _update(self, done, not_done):
|
||||
if self._mode != "v":
|
||||
@ -48,8 +50,10 @@ class Visual(NoneditableTextObject):
|
||||
else:
|
||||
text = self._text
|
||||
|
||||
text = self._transform(text)
|
||||
self.overwrite(text)
|
||||
self._parent._del_child(self)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
28
test.py
28
test.py
@ -1607,6 +1607,34 @@ class Visual_LineSelect_CheckIndentWithTS_NoOverwrite(_VimTest):
|
||||
snippets = ("test", "beg\n\t${0:${VISUAL}}\nend")
|
||||
keys = "hello\nnice\nworld" + ESC + "Vkk" + EX + "test" + EX
|
||||
wanted = "beg\n\thello\n\tnice\n\tworld\nend"
|
||||
|
||||
class VisualTransformation_SelectOneWord(_VimTest):
|
||||
snippets = ("test", r"h${VISUAL/./\U$0\E/g}b")
|
||||
keys = "blablub" + ESC + "0v6l" + EX + "test" + EX
|
||||
wanted = "hBLABLUBb"
|
||||
# TODO: python code access to visual
|
||||
# TODO: document the changes
|
||||
class VisualTransformationWithDefault_ExpandWithoutVisual(_VimTest):
|
||||
snippets = ("test", "h${VISUAL:world/./\U$0\E/g}b")
|
||||
keys = "test" + EX + "hi"
|
||||
wanted = "hWORLDbhi"
|
||||
class VisualTransformationWithDefault_ExpandWithVisual(_VimTest):
|
||||
snippets = ("test", "h${VISUAL:world/./\U$0\E/g}b")
|
||||
keys = "blablub" + ESC + "0v6l" + EX + "test" + EX
|
||||
wanted = "hBLABLUBb"
|
||||
class VisualTransformation_LineSelect_Simple(_VimTest):
|
||||
snippets = ("test", r"h${VISUAL/./\U$0\E/g}b")
|
||||
keys = "hello\nnice\nworld" + ESC + "Vkk" + EX + "test" + EX
|
||||
wanted = "hHELLO\n NICE\n WORLDb"
|
||||
class VisualTransformation_InDefaultText_LineSelect_NoOverwrite(_VimTest):
|
||||
snippets = ("test", "h${1:bef${VISUAL/./\U$0\E/g}aft}b")
|
||||
keys = "hello\nnice\nworld" + ESC + "Vkk" + EX + "test" + EX + JF + "hi"
|
||||
wanted = "hbefHELLO\n NICE\n WORLDaftbhi"
|
||||
class VisualTransformation_InDefaultText_LineSelect_Overwrite(_VimTest):
|
||||
snippets = ("test", "h${1:bef${VISUAL/./\U$0\E/g}aft}b")
|
||||
keys = "hello\nnice\nworld" + ESC + "Vkk" + EX + "test" + EX + "jup" + JF + "hi"
|
||||
wanted = "hjupbhi"
|
||||
|
||||
# End: ${VISUAL} #}}}
|
||||
|
||||
# Recursive (Nested) Snippets {{{#
|
||||
|
Loading…
Reference in New Issue
Block a user