Added basic support for the token. No documentation yet, but 16 new tests

This commit is contained in:
Holger Rapp 2012-01-11 19:17:13 +01:00
parent 88f2c863aa
commit 0c63f205aa
7 changed files with 198 additions and 27 deletions

View File

@ -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>"

View File

@ -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"""

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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
View File

@ -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 #