Added basic support for the token. No documentation yet, but 16 new tests
This commit is contained in:
parent
88f2c863aa
commit
0c63f205aa
@ -144,6 +144,15 @@ function! UltiSnips_ListSnippets()
|
||||
return ""
|
||||
endfunction
|
||||
|
||||
function! UltiSnips_SaveLastVisualSelection()
|
||||
if has("python3")
|
||||
python3 UltiSnips_Manager.save_last_visual_selection()
|
||||
else
|
||||
python UltiSnips_Manager.save_last_visual_selection()
|
||||
endif
|
||||
return ""
|
||||
endfunction
|
||||
|
||||
function! UltiSnips_JumpBackwards()
|
||||
call CompensateForPUM()
|
||||
if has("python3")
|
||||
@ -221,6 +230,7 @@ function! UltiSnips_MapKeys()
|
||||
exec "inoremap <silent> " . g:UltiSnipsJumpForwardTrigger . " <C-R>=UltiSnips_JumpForwards()<cr>"
|
||||
exec "snoremap <silent> " . g:UltiSnipsJumpForwardTrigger . " <Esc>:call UltiSnips_JumpForwards()<cr>"
|
||||
endif
|
||||
exec 'xnoremap ' . g:UltiSnipsExpandTrigger. ' <Esc>:call UltiSnips_SaveLastVisualSelection()<cr>gvs'
|
||||
exec "inoremap <silent> " . g:UltiSnipsJumpBackwardTrigger . " <C-R>=UltiSnips_JumpBackwards()<cr>"
|
||||
exec "snoremap <silent> " . g:UltiSnipsJumpBackwardTrigger . " <Esc>:call UltiSnips_JumpBackwards()<cr>"
|
||||
exec "inoremap <silent> " . g:UltiSnipsListSnippets . " <C-R>=UltiSnips_ListSnippets()<cr>"
|
||||
|
@ -15,7 +15,7 @@ __all__ = ['as_unicode', 'compatible_exec', 'CheapTotalOrdering', 'vim_cursor',
|
||||
if sys.version_info >= (3,0):
|
||||
from UltiSnips.Compatibility_py3 import *
|
||||
|
||||
def set_vim_cursor(line, col): # TODO
|
||||
def set_vim_cursor(line, col):
|
||||
"""Wrapper around vims access to window.cursor. It can't handle
|
||||
multibyte chars, we therefore have to compensate"""
|
||||
|
||||
@ -24,7 +24,7 @@ if sys.version_info >= (3,0):
|
||||
|
||||
vim.current.window.cursor = line, nbytes
|
||||
|
||||
def vim_cursor(): # TODO
|
||||
def vim_cursor():
|
||||
"""Returns the character position (not the byte position) of the
|
||||
vim cursor"""
|
||||
|
||||
@ -60,7 +60,7 @@ if sys.version_info >= (3,0):
|
||||
else:
|
||||
from UltiSnips.Compatibility_py2 import *
|
||||
|
||||
def set_vim_cursor(line, col): # TODO
|
||||
def set_vim_cursor(line, col):
|
||||
"""Wrapper around vims access to window.cursor. It can't handle
|
||||
multibyte chars, we therefore have to compensate"""
|
||||
|
||||
@ -69,7 +69,7 @@ else:
|
||||
|
||||
vim.current.window.cursor = line, nbytes
|
||||
|
||||
def vim_cursor(): # TODO
|
||||
def vim_cursor():
|
||||
"""Returns the character position (not the byte position) of the
|
||||
vim cursor"""
|
||||
|
||||
|
@ -12,7 +12,7 @@ import re
|
||||
from UltiSnips.Geometry import Position
|
||||
|
||||
__all__ = [
|
||||
"tokenize", "EscapeCharToken", "TransformationToken", "TabStopToken",
|
||||
"tokenize", "EscapeCharToken", "VisualToken", "TransformationToken", "TabStopToken",
|
||||
"MirrorToken", "PythonCodeToken", "VimLCodeToken", "ShellCodeToken"
|
||||
]
|
||||
|
||||
@ -43,8 +43,10 @@ class _TextIterator(object):
|
||||
return rv
|
||||
|
||||
def peek(self, count = 1):
|
||||
try:
|
||||
if count > 1: # This might return '' if nothing is found
|
||||
return self._text[self._idx:self._idx + count]
|
||||
try:
|
||||
return self._text[self._idx]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
@ -114,7 +116,7 @@ class TabStopToken(Token):
|
||||
|
||||
@classmethod
|
||||
def starts_here(klass, stream):
|
||||
return klass.CHECK.match(stream.peek(10)) != None
|
||||
return klass.CHECK.match(stream.peek(10)) is not None
|
||||
|
||||
def _parse(self, stream, indent):
|
||||
stream.next() # $
|
||||
@ -131,12 +133,41 @@ class TabStopToken(Token):
|
||||
self.start, self.end, self.no, self.initial_text
|
||||
)
|
||||
|
||||
class VisualToken(Token):
|
||||
TOKEN = "${VISUAL}"
|
||||
CHECK = re.compile(r"^[ \t]*\${VISUAL}")
|
||||
|
||||
@classmethod
|
||||
def starts_here(klass, stream):
|
||||
return klass.CHECK.match(stream.peek(10000)) is not None
|
||||
|
||||
def _parse(self, stream, indent):
|
||||
self.leading_whitespace = ""
|
||||
while stream.peek() != self.TOKEN[0]:
|
||||
self.leading_whitespace += stream.next()
|
||||
|
||||
for i in range(len(self.TOKEN)):
|
||||
stream.next()
|
||||
|
||||
# Make sure that a ${VISUAL} at the end of a line behaves like a block
|
||||
# of text and does not introduce another line break.
|
||||
while 1:
|
||||
nc = stream.peek()
|
||||
if nc is None or nc not in '\r\n':
|
||||
break
|
||||
stream.next()
|
||||
|
||||
def __repr__(self):
|
||||
return "VisualToken(%r,%r)" % (
|
||||
self.start, self.end
|
||||
)
|
||||
|
||||
class TransformationToken(Token):
|
||||
CHECK = re.compile(r'^\${\d+\/')
|
||||
|
||||
@classmethod
|
||||
def starts_here(klass, stream):
|
||||
return klass.CHECK.match(stream.peek(10)) != None
|
||||
return klass.CHECK.match(stream.peek(10)) is not None
|
||||
|
||||
def _parse(self, stream, indent):
|
||||
stream.next() # $
|
||||
@ -160,7 +191,7 @@ class MirrorToken(Token):
|
||||
|
||||
@classmethod
|
||||
def starts_here(klass, stream):
|
||||
return klass.CHECK.match(stream.peek(10)) != None
|
||||
return klass.CHECK.match(stream.peek(10)) is not None
|
||||
|
||||
def _parse(self, stream, indent):
|
||||
stream.next() # $
|
||||
@ -250,7 +281,7 @@ class VimLCodeToken(Token):
|
||||
# End: Tokens }}}
|
||||
|
||||
__ALLOWED_TOKENS = [
|
||||
EscapeCharToken, TransformationToken, TabStopToken, MirrorToken,
|
||||
EscapeCharToken, VisualToken, TransformationToken, TabStopToken, MirrorToken,
|
||||
PythonCodeToken, VimLCodeToken, ShellCodeToken
|
||||
]
|
||||
def tokenize(text, indent):
|
||||
|
@ -11,8 +11,9 @@ from UltiSnips.Buffer import TextBuffer
|
||||
from UltiSnips.Compatibility import CheapTotalOrdering
|
||||
from UltiSnips.Compatibility import compatible_exec, as_unicode
|
||||
from UltiSnips.Geometry import Span, Position
|
||||
from UltiSnips.Lexer import tokenize, EscapeCharToken, TransformationToken, \
|
||||
TabStopToken, MirrorToken, PythonCodeToken, VimLCodeToken, ShellCodeToken
|
||||
from UltiSnips.Lexer import tokenize, EscapeCharToken, VisualToken, \
|
||||
TransformationToken, TabStopToken, MirrorToken, PythonCodeToken, \
|
||||
VimLCodeToken, ShellCodeToken
|
||||
from UltiSnips.Util import IndentUtil
|
||||
|
||||
__all__ = [ "Mirror", "Transformation", "SnippetInstance", "StartMarker" ]
|
||||
@ -166,6 +167,8 @@ class _TOParser(object):
|
||||
k._do_parse(all_tokens, seen_ts)
|
||||
elif isinstance(token, EscapeCharToken):
|
||||
EscapedChar(self._parent_to, token)
|
||||
elif isinstance(token, VisualToken):
|
||||
Visual(self._parent_to, token)
|
||||
elif isinstance(token, ShellCodeToken):
|
||||
ShellCode(self._parent_to, token)
|
||||
elif isinstance(token, PythonCodeToken):
|
||||
@ -420,6 +423,34 @@ class Mirror(TextObject):
|
||||
def __repr__(self):
|
||||
return "Mirror(%s -> %s)" % (self._start, self._end)
|
||||
|
||||
class Visual(TextObject):
|
||||
"""
|
||||
A ${VISUAL} placeholder that will use the text that was last visually
|
||||
selected and insert it here. If there was no text visually selected,
|
||||
this will be the empty string
|
||||
"""
|
||||
def __init__(self, parent, token):
|
||||
|
||||
# Find our containing snippet for visual_content
|
||||
snippet = parent
|
||||
while snippet and not isinstance(snippet, SnippetInstance):
|
||||
snippet = snippet._parent
|
||||
|
||||
text = ""
|
||||
for idx, line in enumerate(snippet.visual_content.splitlines(True)):
|
||||
text += token.leading_whitespace
|
||||
text += line
|
||||
|
||||
self._text = text
|
||||
|
||||
TextObject.__init__(self, parent, token, initial_text = self._text)
|
||||
|
||||
def _do_update(self):
|
||||
self.current_text = self._text
|
||||
|
||||
def __repr__(self):
|
||||
return "Visual(%s -> %s)" % (self._start, self._end)
|
||||
|
||||
|
||||
class Transformation(Mirror):
|
||||
def __init__(self, parent, ts, token):
|
||||
@ -715,7 +746,7 @@ class SnippetInstance(TextObject):
|
||||
also a TextObject because it has a start an end
|
||||
"""
|
||||
|
||||
def __init__(self, parent, indent, initial_text, start = None, end = None, last_re = None, globals = None):
|
||||
def __init__(self, parent, indent, initial_text, start, end, visual_content, last_re, globals):
|
||||
if start is None:
|
||||
start = Position(0,0)
|
||||
if end is None:
|
||||
@ -723,6 +754,7 @@ class SnippetInstance(TextObject):
|
||||
|
||||
self.locals = {"match" : last_re}
|
||||
self.globals = globals
|
||||
self.visual_content = visual_content
|
||||
|
||||
TextObject.__init__(self, parent, start, end, initial_text)
|
||||
|
||||
|
@ -48,6 +48,12 @@ class IndentUtil(object):
|
||||
new_ind.append(ch)
|
||||
return "".join(new_ind)
|
||||
|
||||
def ntabs_to_proper_indent(self, ntabs):
|
||||
line_ind = ntabs * self.sw * " "
|
||||
line_ind = self.indent_to_spaces(line_ind)
|
||||
line_ind = self.spaces_to_indent(line_ind)
|
||||
return line_ind
|
||||
|
||||
def indent_to_spaces(self, indent):
|
||||
""" Converts indentation to spaces respecting vim settings. """
|
||||
indent = self._strip_tabs(indent, self.ts)
|
||||
|
@ -1,8 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
#
|
||||
from debug import debug # TODO
|
||||
|
||||
from functools import wraps
|
||||
import glob
|
||||
import hashlib
|
||||
@ -275,7 +273,6 @@ class Snippet(object):
|
||||
self._matched = ""
|
||||
self._last_re = None
|
||||
self._globals = globals
|
||||
self._util = IndentUtil()
|
||||
|
||||
def __repr__(self):
|
||||
return "Snippet(%s,%s,%s)" % (self._t,self._d,self._opts)
|
||||
@ -421,11 +418,12 @@ class Snippet(object):
|
||||
return self._matched
|
||||
matched = property(matched)
|
||||
|
||||
def launch(self, text_before, parent, start, end = None):
|
||||
def launch(self, text_before, visual_content, parent, start, end = None):
|
||||
indent = self._INDENT.match(text_before).group(0)
|
||||
lines = (self._v + "\n").splitlines()
|
||||
self._util.reset()
|
||||
ind_util = IndentUtil()
|
||||
|
||||
# Replace leading tabs in the snippet definition via proper indenting
|
||||
v = []
|
||||
for line_num, line in enumerate(lines):
|
||||
if "t" in self._opts:
|
||||
@ -433,10 +431,8 @@ class Snippet(object):
|
||||
else:
|
||||
tabs = len(self._TABS.match(line).group(0))
|
||||
|
||||
line_ind = ind_util.ntabs_to_proper_indent(tabs)
|
||||
|
||||
line_ind = tabs * self._util.sw * " "
|
||||
line_ind = self._util.indent_to_spaces(line_ind)
|
||||
line_ind = self._util.spaces_to_indent(line_ind)
|
||||
if line_num != 0:
|
||||
line_ind = indent + line_ind
|
||||
|
||||
@ -444,11 +440,11 @@ class Snippet(object):
|
||||
v = os.linesep.join(v)
|
||||
|
||||
if parent is None:
|
||||
return SnippetInstance(StartMarker(start), indent,
|
||||
v, last_re = self._last_re, globals = self._globals)
|
||||
return SnippetInstance(StartMarker(start), indent, v, None, None, visual_content = visual_content,
|
||||
last_re = self._last_re, globals = self._globals)
|
||||
else:
|
||||
return SnippetInstance(parent, indent, v, start,
|
||||
end, last_re = self._last_re, globals = self._globals)
|
||||
return SnippetInstance(parent, indent, v, start, end, visual_content,
|
||||
last_re = self._last_re, globals = self._globals)
|
||||
|
||||
class VimState(object):
|
||||
def __init__(self):
|
||||
@ -656,6 +652,7 @@ class SnippetManager(object):
|
||||
def reset(self, test_error=False):
|
||||
self._test_error = test_error
|
||||
self._snippets = {}
|
||||
self._visual_content = as_unicode("")
|
||||
|
||||
while len(self._csnippets):
|
||||
self._current_snippet_is_done()
|
||||
@ -710,6 +707,29 @@ class SnippetManager(object):
|
||||
if not rv:
|
||||
self._handle_failure(self.expand_trigger)
|
||||
|
||||
@err_to_scratch_buffer
|
||||
def save_last_visual_selection(self):
|
||||
"""
|
||||
This is called when the expand trigger is pressed in visual mode.
|
||||
Our job is to remember everything between '< and '> and pass it on to
|
||||
${VISUAL} in case it will be needed.
|
||||
"""
|
||||
sl, sc = map(int, (vim.eval("""line("'<")"""), vim.eval("""virtcol("'<")""")))
|
||||
el, ec = map(int, (vim.eval("""line("'>")"""), vim.eval("""virtcol("'>")""")))
|
||||
|
||||
def _vim_line_with_eol(ln):
|
||||
return as_unicode(vim.current.buffer[ln] + '\n')
|
||||
|
||||
if sl == el:
|
||||
text = _vim_line_with_eol(sl-1)[sc-1:ec]
|
||||
else:
|
||||
text = _vim_line_with_eol(sl-1)[sc-1:]
|
||||
for cl in range(sl,el-1):
|
||||
text += _vim_line_with_eol(cl)
|
||||
text += _vim_line_with_eol(el-1)[:ec]
|
||||
|
||||
self._visual_content = text
|
||||
|
||||
def snippet_dict(self, ft):
|
||||
if ft not in self._snippets:
|
||||
self._snippets[ft] = _SnippetDictionary()
|
||||
@ -1034,7 +1054,8 @@ class SnippetManager(object):
|
||||
end = Position(pos.line - p_start.line, pos.col)
|
||||
start = Position(end.line, end.col - len(snippet.matched))
|
||||
|
||||
si = snippet.launch(text_before, self._ctab, start, end)
|
||||
si = snippet.launch(text_before, self._visual_content, self._ctab, start, end)
|
||||
self._visual_content = ""
|
||||
|
||||
self._update_vim_buffer()
|
||||
|
||||
@ -1045,7 +1066,8 @@ class SnippetManager(object):
|
||||
self._vb = VimBuffer(text_before, after)
|
||||
|
||||
start = Position(lineno-1, len(text_before))
|
||||
self._csnippets.append(snippet.launch(text_before, None, start))
|
||||
self._csnippets.append(snippet.launch(text_before, self._visual_content, None, start))
|
||||
self._visual_content = ""
|
||||
|
||||
self._vb.replace_lines(lineno-1, lineno-1,
|
||||
self._cs._current_text)
|
||||
|
70
test.py
70
test.py
@ -2577,9 +2577,79 @@ ${1:Welt} }}}""")
|
||||
Ball }}}"""
|
||||
|
||||
|
||||
###################
|
||||
# ${VISUAL} tests #
|
||||
###################
|
||||
class Visual_NoVisualSelection_Ignore(_VimTest):
|
||||
snippets = ("test", "h${VISUAL}b")
|
||||
keys = "test" + EX + "abc"
|
||||
wanted = "hbabc"
|
||||
class Visual_SelectOneWord(_VimTest):
|
||||
snippets = ("test", "h${VISUAL}b")
|
||||
keys = "blablub" + ESC + "0v6l" + EX + "test" + EX
|
||||
wanted = "hblablubb"
|
||||
|
||||
class Visual_ExpandTwice(_VimTest):
|
||||
snippets = ("test", "h${VISUAL}b")
|
||||
keys = "blablub" + ESC + "0v6l" + EX + "test" + EX + "\ntest" + EX
|
||||
wanted = "hblablubb\nhb"
|
||||
|
||||
class Visual_SelectOneWord_TwiceVisual(_VimTest):
|
||||
snippets = ("test", "h${VISUAL}b${VISUAL}a")
|
||||
keys = "blablub" + ESC + "0v6l" + EX + "test" + EX
|
||||
wanted = "hblablubbblabluba"
|
||||
class Visual_SelectOneWord_Inword(_VimTest):
|
||||
snippets = ("test", "h${VISUAL}b", "Description", "i")
|
||||
keys = "blablub" + ESC + "0lv4l" + EX + "test" + EX
|
||||
wanted = "bhlablubb"
|
||||
class Visual_SelectOneWord_TillEndOfLine(_VimTest):
|
||||
snippets = ("test", "h${VISUAL}b", "Description", "i")
|
||||
keys = "blablub" + ESC + "0v$" + EX + "test" + EX + ESC + "o"
|
||||
wanted = "hblablub\nb"
|
||||
class Visual_SelectOneWordWithTabstop_TillEndOfLine(_VimTest):
|
||||
snippets = ("test", "h${2:ahh}${VISUAL}${1:ups}b", "Description", "i")
|
||||
keys = "blablub" + ESC + "0v$" + EX + "test" + EX + "mmm" + JF + "n" + JF + "done" + ESC + "o"
|
||||
wanted = "hnblablub\nmmmbdone"
|
||||
class Visual_InDefaultText_SelectOneWord_NoOverwrite(_VimTest):
|
||||
snippets = ("test", "h${1:${VISUAL}}b")
|
||||
keys = "blablub" + ESC + "0v6l" + EX + "test" + EX + JF + "hello"
|
||||
wanted = "hblablubbhello"
|
||||
class Visual_InDefaultText_SelectOneWord(_VimTest):
|
||||
snippets = ("test", "h${1:${VISUAL}}b")
|
||||
keys = "blablub" + ESC + "0v6l" + EX + "test" + EX + "hello"
|
||||
wanted = "hhellob"
|
||||
|
||||
class Visual_CrossOneLine(_VimTest):
|
||||
snippets = ("test", "h${VISUAL}b")
|
||||
keys = "bla blub\n helloi" + ESC + "0k4lvjll" + EX + "test" + EX
|
||||
wanted = "bla hblub\n hellobi"
|
||||
|
||||
class Visual_LineSelect(_VimTest):
|
||||
snippets = ("test", "h${VISUAL}b")
|
||||
keys = "hello\nnice\nworld" + ESC + "Vkk" + EX + "test" + EX
|
||||
wanted = "hhello\nnice\nworld\nb"
|
||||
class Visual_InDefaultText_LineSelect_NoOverwrite(_VimTest):
|
||||
snippets = ("test", "h${1:bef${VISUAL}aft}b")
|
||||
keys = "hello\nnice\nworld" + ESC + "Vkk" + EX + "test" + EX + JF + "hi"
|
||||
wanted = "hbefhello\nnice\nworld\naftbhi"
|
||||
class Visual_InDefaultText_LineSelect_Overwrite(_VimTest):
|
||||
snippets = ("test", "h${1:bef${VISUAL}aft}b")
|
||||
keys = "hello\nnice\nworld" + ESC + "Vkk" + EX + "test" + EX + "jup" + JF + "hi"
|
||||
wanted = "hjupbhi"
|
||||
class Visual_LineSelect_CheckIndent(_VimTest):
|
||||
snippets = ("test", "beg\n\t${VISUAL}\nend")
|
||||
keys = "hello\nnice\nworld" + ESC + "Vkk" + EX + "test" + EX
|
||||
wanted = "beg\n\thello\n\tnice\n\tworld\nend"
|
||||
|
||||
class Visual_LineSelect_CheckIndentTwice(_VimTest):
|
||||
snippets = ("test", "beg\n\t${VISUAL}\nend")
|
||||
keys = " hello\n nice\n\tworld" + ESC + "Vkk" + EX + "test" + EX
|
||||
wanted = "beg\n\t hello\n\t nice\n\t\tworld\nend"
|
||||
|
||||
class Visual_LineSelect_WithTabStop(_VimTest):
|
||||
snippets = ("test", "beg\n\t${VISUAL}\n\t${1:here_we_go}\nend")
|
||||
keys = "hello\nnice\nworld" + ESC + "Vkk" + EX + "test" + EX + "super" + JF + "done"
|
||||
wanted = "beg\n\thello\n\tnice\n\tworld\n\tsuper\nenddone"
|
||||
|
||||
###########################################################################
|
||||
# END OF TEST #
|
||||
|
Loading…
Reference in New Issue
Block a user