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 "" return ""
endfunction 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() function! UltiSnips_JumpBackwards()
call CompensateForPUM() call CompensateForPUM()
if has("python3") if has("python3")
@ -221,6 +230,7 @@ function! UltiSnips_MapKeys()
exec "inoremap <silent> " . g:UltiSnipsJumpForwardTrigger . " <C-R>=UltiSnips_JumpForwards()<cr>" exec "inoremap <silent> " . g:UltiSnipsJumpForwardTrigger . " <C-R>=UltiSnips_JumpForwards()<cr>"
exec "snoremap <silent> " . g:UltiSnipsJumpForwardTrigger . " <Esc>:call UltiSnips_JumpForwards()<cr>" exec "snoremap <silent> " . g:UltiSnipsJumpForwardTrigger . " <Esc>:call UltiSnips_JumpForwards()<cr>"
endif endif
exec 'xnoremap ' . g:UltiSnipsExpandTrigger. ' <Esc>:call UltiSnips_SaveLastVisualSelection()<cr>gvs'
exec "inoremap <silent> " . g:UltiSnipsJumpBackwardTrigger . " <C-R>=UltiSnips_JumpBackwards()<cr>" exec "inoremap <silent> " . g:UltiSnipsJumpBackwardTrigger . " <C-R>=UltiSnips_JumpBackwards()<cr>"
exec "snoremap <silent> " . g:UltiSnipsJumpBackwardTrigger . " <Esc>:call UltiSnips_JumpBackwards()<cr>" exec "snoremap <silent> " . g:UltiSnipsJumpBackwardTrigger . " <Esc>:call UltiSnips_JumpBackwards()<cr>"
exec "inoremap <silent> " . g:UltiSnipsListSnippets . " <C-R>=UltiSnips_ListSnippets()<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): if sys.version_info >= (3,0):
from UltiSnips.Compatibility_py3 import * 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 """Wrapper around vims access to window.cursor. It can't handle
multibyte chars, we therefore have to compensate""" multibyte chars, we therefore have to compensate"""
@ -24,7 +24,7 @@ if sys.version_info >= (3,0):
vim.current.window.cursor = line, nbytes vim.current.window.cursor = line, nbytes
def vim_cursor(): # TODO def vim_cursor():
"""Returns the character position (not the byte position) of the """Returns the character position (not the byte position) of the
vim cursor""" vim cursor"""
@ -60,7 +60,7 @@ if sys.version_info >= (3,0):
else: else:
from UltiSnips.Compatibility_py2 import * 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 """Wrapper around vims access to window.cursor. It can't handle
multibyte chars, we therefore have to compensate""" multibyte chars, we therefore have to compensate"""
@ -69,7 +69,7 @@ else:
vim.current.window.cursor = line, nbytes vim.current.window.cursor = line, nbytes
def vim_cursor(): # TODO def vim_cursor():
"""Returns the character position (not the byte position) of the """Returns the character position (not the byte position) of the
vim cursor""" vim cursor"""

View File

@ -12,7 +12,7 @@ import re
from UltiSnips.Geometry import Position from UltiSnips.Geometry import Position
__all__ = [ __all__ = [
"tokenize", "EscapeCharToken", "TransformationToken", "TabStopToken", "tokenize", "EscapeCharToken", "VisualToken", "TransformationToken", "TabStopToken",
"MirrorToken", "PythonCodeToken", "VimLCodeToken", "ShellCodeToken" "MirrorToken", "PythonCodeToken", "VimLCodeToken", "ShellCodeToken"
] ]
@ -43,8 +43,10 @@ class _TextIterator(object):
return rv return rv
def peek(self, count = 1): def peek(self, count = 1):
try: if count > 1: # This might return '' if nothing is found
return self._text[self._idx:self._idx + count] return self._text[self._idx:self._idx + count]
try:
return self._text[self._idx]
except IndexError: except IndexError:
return None return None
@ -114,7 +116,7 @@ class TabStopToken(Token):
@classmethod @classmethod
def starts_here(klass, stream): 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): def _parse(self, stream, indent):
stream.next() # $ stream.next() # $
@ -131,12 +133,41 @@ class TabStopToken(Token):
self.start, self.end, self.no, self.initial_text 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): class TransformationToken(Token):
CHECK = re.compile(r'^\${\d+\/') CHECK = re.compile(r'^\${\d+\/')
@classmethod @classmethod
def starts_here(klass, stream): 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): def _parse(self, stream, indent):
stream.next() # $ stream.next() # $
@ -160,7 +191,7 @@ class MirrorToken(Token):
@classmethod @classmethod
def starts_here(klass, stream): 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): def _parse(self, stream, indent):
stream.next() # $ stream.next() # $
@ -250,7 +281,7 @@ class VimLCodeToken(Token):
# End: Tokens }}} # End: Tokens }}}
__ALLOWED_TOKENS = [ __ALLOWED_TOKENS = [
EscapeCharToken, TransformationToken, TabStopToken, MirrorToken, EscapeCharToken, VisualToken, TransformationToken, TabStopToken, MirrorToken,
PythonCodeToken, VimLCodeToken, ShellCodeToken PythonCodeToken, VimLCodeToken, ShellCodeToken
] ]
def tokenize(text, indent): def tokenize(text, indent):

View File

@ -11,8 +11,9 @@ from UltiSnips.Buffer import TextBuffer
from UltiSnips.Compatibility import CheapTotalOrdering from UltiSnips.Compatibility import CheapTotalOrdering
from UltiSnips.Compatibility import compatible_exec, as_unicode from UltiSnips.Compatibility import compatible_exec, as_unicode
from UltiSnips.Geometry import Span, Position from UltiSnips.Geometry import Span, Position
from UltiSnips.Lexer import tokenize, EscapeCharToken, TransformationToken, \ from UltiSnips.Lexer import tokenize, EscapeCharToken, VisualToken, \
TabStopToken, MirrorToken, PythonCodeToken, VimLCodeToken, ShellCodeToken TransformationToken, TabStopToken, MirrorToken, PythonCodeToken, \
VimLCodeToken, ShellCodeToken
from UltiSnips.Util import IndentUtil from UltiSnips.Util import IndentUtil
__all__ = [ "Mirror", "Transformation", "SnippetInstance", "StartMarker" ] __all__ = [ "Mirror", "Transformation", "SnippetInstance", "StartMarker" ]
@ -166,6 +167,8 @@ class _TOParser(object):
k._do_parse(all_tokens, seen_ts) k._do_parse(all_tokens, seen_ts)
elif isinstance(token, EscapeCharToken): elif isinstance(token, EscapeCharToken):
EscapedChar(self._parent_to, token) EscapedChar(self._parent_to, token)
elif isinstance(token, VisualToken):
Visual(self._parent_to, token)
elif isinstance(token, ShellCodeToken): elif isinstance(token, ShellCodeToken):
ShellCode(self._parent_to, token) ShellCode(self._parent_to, token)
elif isinstance(token, PythonCodeToken): elif isinstance(token, PythonCodeToken):
@ -420,6 +423,34 @@ class Mirror(TextObject):
def __repr__(self): def __repr__(self):
return "Mirror(%s -> %s)" % (self._start, self._end) 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): class Transformation(Mirror):
def __init__(self, parent, ts, token): def __init__(self, parent, ts, token):
@ -715,7 +746,7 @@ class SnippetInstance(TextObject):
also a TextObject because it has a start an end 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: if start is None:
start = Position(0,0) start = Position(0,0)
if end is None: if end is None:
@ -723,6 +754,7 @@ class SnippetInstance(TextObject):
self.locals = {"match" : last_re} self.locals = {"match" : last_re}
self.globals = globals self.globals = globals
self.visual_content = visual_content
TextObject.__init__(self, parent, start, end, initial_text) TextObject.__init__(self, parent, start, end, initial_text)

View File

@ -48,6 +48,12 @@ class IndentUtil(object):
new_ind.append(ch) new_ind.append(ch)
return "".join(new_ind) 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): def indent_to_spaces(self, indent):
""" Converts indentation to spaces respecting vim settings. """ """ Converts indentation to spaces respecting vim settings. """
indent = self._strip_tabs(indent, self.ts) indent = self._strip_tabs(indent, self.ts)

View File

@ -1,8 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# encoding: utf-8 # encoding: utf-8
# #
from debug import debug # TODO
from functools import wraps from functools import wraps
import glob import glob
import hashlib import hashlib
@ -275,7 +273,6 @@ class Snippet(object):
self._matched = "" self._matched = ""
self._last_re = None self._last_re = None
self._globals = globals self._globals = globals
self._util = IndentUtil()
def __repr__(self): def __repr__(self):
return "Snippet(%s,%s,%s)" % (self._t,self._d,self._opts) return "Snippet(%s,%s,%s)" % (self._t,self._d,self._opts)
@ -421,11 +418,12 @@ class Snippet(object):
return self._matched return self._matched
matched = property(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) indent = self._INDENT.match(text_before).group(0)
lines = (self._v + "\n").splitlines() lines = (self._v + "\n").splitlines()
self._util.reset() ind_util = IndentUtil()
# Replace leading tabs in the snippet definition via proper indenting
v = [] v = []
for line_num, line in enumerate(lines): for line_num, line in enumerate(lines):
if "t" in self._opts: if "t" in self._opts:
@ -433,10 +431,8 @@ class Snippet(object):
else: else:
tabs = len(self._TABS.match(line).group(0)) 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: if line_num != 0:
line_ind = indent + line_ind line_ind = indent + line_ind
@ -444,11 +440,11 @@ class Snippet(object):
v = os.linesep.join(v) v = os.linesep.join(v)
if parent is None: if parent is None:
return SnippetInstance(StartMarker(start), indent, return SnippetInstance(StartMarker(start), indent, v, None, None, visual_content = visual_content,
v, last_re = self._last_re, globals = self._globals) last_re = self._last_re, globals = self._globals)
else: else:
return SnippetInstance(parent, indent, v, start, return SnippetInstance(parent, indent, v, start, end, visual_content,
end, last_re = self._last_re, globals = self._globals) last_re = self._last_re, globals = self._globals)
class VimState(object): class VimState(object):
def __init__(self): def __init__(self):
@ -656,6 +652,7 @@ class SnippetManager(object):
def reset(self, test_error=False): def reset(self, test_error=False):
self._test_error = test_error self._test_error = test_error
self._snippets = {} self._snippets = {}
self._visual_content = as_unicode("")
while len(self._csnippets): while len(self._csnippets):
self._current_snippet_is_done() self._current_snippet_is_done()
@ -710,6 +707,29 @@ class SnippetManager(object):
if not rv: if not rv:
self._handle_failure(self.expand_trigger) 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): def snippet_dict(self, ft):
if ft not in self._snippets: if ft not in self._snippets:
self._snippets[ft] = _SnippetDictionary() self._snippets[ft] = _SnippetDictionary()
@ -1034,7 +1054,8 @@ class SnippetManager(object):
end = Position(pos.line - p_start.line, pos.col) end = Position(pos.line - p_start.line, pos.col)
start = Position(end.line, end.col - len(snippet.matched)) 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() self._update_vim_buffer()
@ -1045,7 +1066,8 @@ class SnippetManager(object):
self._vb = VimBuffer(text_before, after) self._vb = VimBuffer(text_before, after)
start = Position(lineno-1, len(text_before)) 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._vb.replace_lines(lineno-1, lineno-1,
self._cs._current_text) self._cs._current_text)

70
test.py
View File

@ -2577,9 +2577,79 @@ ${1:Welt} }}}""")
Ball }}}""" 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 # # END OF TEST #