diff --git a/plugin/UltiSnips.vim b/plugin/UltiSnips.vim index 362f37e..4a99d47 100644 --- a/plugin/UltiSnips.vim +++ b/plugin/UltiSnips.vim @@ -167,7 +167,7 @@ function! UltiSnips_MapKeys() exec "inoremap " . g:UltiSnipsJumpForwardTrigger . " =UltiSnips_JumpForwards()" exec "snoremap " . g:UltiSnipsJumpForwardTrigger . " :call UltiSnips_JumpForwards()" endif - exec 'xnoremap ' . g:UltiSnipsExpandTrigger. ' :call UltiSnips_SaveLastVisualSelection()gvs' + exec 'xnoremap ' . g:UltiSnipsExpandTrigger. ' :call UltiSnips_SaveLastVisualSelection()gvs' exec "inoremap " . g:UltiSnipsJumpBackwardTrigger . " =UltiSnips_JumpBackwards()" exec "snoremap " . g:UltiSnipsJumpBackwardTrigger . " :call UltiSnips_JumpBackwards()" exec "inoremap " . g:UltiSnipsListSnippets . " =UltiSnips_ListSnippets()" diff --git a/plugin/UltiSnips/TextObjects/_lexer.py b/plugin/UltiSnips/TextObjects/_lexer.py index 1962f61..886ebe1 100644 --- a/plugin/UltiSnips/TextObjects/_lexer.py +++ b/plugin/UltiSnips/TextObjects/_lexer.py @@ -136,28 +136,15 @@ class TabStopToken(Token): 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 + return stream.peek(len(klass.TOKEN)) == klass.TOKEN 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 diff --git a/plugin/UltiSnips/TextObjects/_visual.py b/plugin/UltiSnips/TextObjects/_visual.py index 12b03bd..600bf6b 100755 --- a/plugin/UltiSnips/TextObjects/_visual.py +++ b/plugin/UltiSnips/TextObjects/_visual.py @@ -1,6 +1,13 @@ #!/usr/bin/env python # encoding: utf-8 +import re + +import vim + +from ..Compatibility import as_unicode +from ..Util import IndentUtil + from ._base import NoneditableTextObject class Visual(NoneditableTextObject): @@ -9,30 +16,39 @@ class Visual(NoneditableTextObject): selected and insert it here. If there was no text visually selected, this will be the empty string """ - def __init__(self, parent, token): - # TODO: rework this: get indent directly from vim buffer and - # only update once. + __REPLACE_NON_WS = re.compile(r"[^ \t]") + def __init__(self, parent, token): # Find our containing snippet for visual_content snippet = parent while snippet: try: - self._visual_content = snippet.visual_content.splitlines(True) + self._text = snippet.visual_content.text + self._mode = snippet.visual_content.mode break except AttributeError: snippet = snippet._parent - text = "" - for idx, line in enumerate(self._visual_content): - text += token.leading_whitespace - text += line - - self._text = text - - NoneditableTextObject.__init__(self, parent, token, initial_text = self._text) + NoneditableTextObject.__init__(self, parent, token) def _update(self, done, not_done): - self.overwrite(self._text) + if self._mode != "v": + # Keep the indent for Line/Block Selection + text_before = as_unicode(vim.current.buffer[self.start.line])[:self.start.col] + indent = self.__REPLACE_NON_WS.sub(" ", text_before) + iu = IndentUtil() + indent = iu.indent_to_spaces(indent) + indent = iu.spaces_to_indent(indent) + text = "" + for idx, line in enumerate(self._text.splitlines(True)): + if idx != 0: + text += indent + text += line + text = text[:-1] # Strip final '\n' + else: + text = self._text + + self.overwrite(text) self._parent._del_child(self) return True diff --git a/plugin/UltiSnips/__init__.py b/plugin/UltiSnips/__init__.py index c33a529..1f187f6 100644 --- a/plugin/UltiSnips/__init__.py +++ b/plugin/UltiSnips/__init__.py @@ -662,6 +662,43 @@ class VimState(object): # Private functions below # ########################### +from debug import debug + +class VisualContentPreserver(object): + def __init__(self): + self.reset() + + def reset(self): + self._mode = "" + self._text = as_unicode("") + + def conserve(self): + sl, sc = map(int, (vim.eval("""line("'<")"""), vim.eval("""virtcol("'<")"""))) + el, ec = map(int, (vim.eval("""line("'>")"""), vim.eval("""virtcol("'>")"""))) + self._mode = vim.eval("visualmode()") + + 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._text = text + + @property + def text(self): + return self._text + + @property + def mode(self): + return self._mode + + class SnippetManager(object): def __init__(self): self._vstate = VimState() @@ -676,7 +713,7 @@ class SnippetManager(object): self._lvb = TextBuffer("") self._test_error = test_error self._snippets = {} - self._visual_content = as_unicode("") + self._visual_content = VisualContentPreserver() while len(self._csnippets): self._current_snippet_is_done() @@ -738,21 +775,7 @@ class SnippetManager(object): 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 + self._visual_content.conserve() def snippet_dict(self, ft): if ft not in self._snippets: @@ -1045,14 +1068,14 @@ class SnippetManager(object): # TODO: private parts? maybe handle this in add_child si = snippet.launch(text_before, self._visual_content, self._cs._find_parent_for_new_to(start), start, end) - self._visual_content = "" + self._visual_content.reset() self._csnippets.append(si) else: start = Position(lineno-1, len(text_before)) end = Position(lineno-1, len(before)) self._csnippets.append(snippet.launch(text_before, self._visual_content, None, start, end)) - self._visual_content = "" + self._visual_content.reset() self._ignore_movements = True self._lvb = TextBuffer('\n'.join(vim.current.buffer)) # TODO: no need to cache everything diff --git a/test.py b/test.py index 5b9fe1d..29842fb 100755 --- a/test.py +++ b/test.py @@ -1500,35 +1500,47 @@ class Visual_CrossOneLine(_VimTest): wanted = "bla hblub\n hellobi" # TODO: with indent in default text -class Visual_LineSelect(_VimTest): +class Visual_LineSelect_Simple(_VimTest): snippets = ("test", "h${VISUAL}b") keys = "hello\nnice\nworld" + ESC + "Vkk" + EX + "test" + EX - wanted = "hhello\nnice\nworld\nb" + wanted = "hhello\n nice\n worldb" 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" + wanted = "hbefhello\n nice\n worldaftbhi" 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): +class Visual_LineSelect_CheckIndentSimple(_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_InDefaultText_IndentSpacesToTabstop_NoOverwrite(_VimTest): + snippets = ("test", "h${1:beforea${VISUAL}aft}b") + keys = "hello\nnice\nworld" + ESC + "Vkk" + EX + "test" + EX + JF + "hi" + wanted = "hbeforeahello\n\tnice\n\tworldaftbhi" +class Visual_InDefaultText_IndentSpacesToTabstop_Overwrite(_VimTest): + snippets = ("test", "h${1:beforea${VISUAL}aft}b") + keys = "hello\nnice\nworld" + ESC + "Vkk" + EX + "test" + EX + "ups" + JF + "hi" + wanted = "hupsbhi" +class Visual_InDefaultText_IndentSpacesToTabstop_NoOverwrite1(_VimTest): + snippets = ("test", "h${1:beforeaaa${VISUAL}aft}b") + keys = "hello\nnice\nworld" + ESC + "Vkk" + EX + "test" + EX + JF + "hi" + wanted = "hbeforeaaahello\n\t nice\n\t worldaftbhi" 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" - - - +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" # End: ${VISUAL} #}}} # Recursive (Nested) Snippets {{{#