def _handle_failure(self, trigger): """Mainly make sure that we play well with SuperTab.""" if trigger.lower() == "<tab>": feedkey = "\\" + trigger elif trigger.lower() == "<s-tab>": feedkey = "\\" + trigger else: feedkey = None mode = "n" if not self._supertab_keys: if vim_helper.eval("exists('g:SuperTabMappingForward')") != "0": self._supertab_keys = ( vim_helper.eval("g:SuperTabMappingForward"), vim_helper.eval("g:SuperTabMappingBackward"), ) else: self._supertab_keys = ["", ""] for idx, sttrig in enumerate(self._supertab_keys): if trigger.lower() == sttrig.lower(): if idx == 0: feedkey = r"\<Plug>SuperTabForward" mode = "n" elif idx == 1: feedkey = r"\<Plug>SuperTabBackward" mode = "p" # Use remap mode so SuperTab mappings will be invoked. break if feedkey in (r"\<Plug>SuperTabForward", r"\<Plug>SuperTabBackward"): vim_helper.command("return SuperTab(%s)" % vim_helper.escape(mode)) elif feedkey: vim_helper.command("return %s" % vim_helper.escape(feedkey))
def __init__(self): self._poss = deque(maxlen=5) self._lvb = None self._text_to_expect = "" self._unnamed_reg_cached = False # We store the cached value of the unnamed register in Vim directly to # avoid any Unicode issues with saving and restoring the unnamed # register across the Python bindings. The unnamed register can contain # data that cannot be coerced to Unicode, and so a simple vim.eval('@"') # fails badly. Keeping the cached value in Vim directly, sidesteps the # problem. vim_helper.command('let g:_ultisnips_unnamed_reg_cache = ""')
def remember_unnamed_register(self, text_to_expect): """Save the unnamed register. 'text_to_expect' is text that we expect to be contained in the register the next time this method is called - this could be text from the tabstop that was selected and might have been overwritten. We will not cache that then. """ self._unnamed_reg_cached = True escaped_text = self._text_to_expect.replace("'", "''") res = int(vim_helper.eval('@" != ' + "'" + escaped_text + "'")) if res: vim_helper.command('let g:_ultisnips_unnamed_reg_cache = @"') self._text_to_expect = text_to_expect
def snippets_in_current_scope(self, searchAll): """Returns the snippets that could be expanded to Vim as a global variable.""" before = "" if searchAll else vim_helper.buf.line_till_cursor snippets = self._snips(before, True) # Sort snippets alphabetically snippets.sort(key=lambda x: x.trigger) for snip in snippets: description = snip.description[ snip.description.find(snip.trigger) + len(snip.trigger) + 2 : ] location = snip.location if snip.location else "" key = as_unicode(snip.trigger) description = as_unicode(description) # remove surrounding "" or '' in snippet description if it exists if len(description) > 2: if description[0] == description[-1] and description[0] in "'\"": description = description[1:-1] vim_helper.command( as_unicode("let g:current_ulti_dict['{key}'] = '{val}'").format( key=key.replace("'", "''"), val=description.replace("'", "''") ) ) if searchAll: vim_helper.command( as_unicode( ( "let g:current_ulti_dict_info['{key}'] = {{" "'description': '{description}'," "'location': '{location}'," "}}" ) ).format( key=key.replace("'", "''"), location=location.replace("'", "''"), description=description.replace("'", "''"), ) )
def _try_expand(self, autotrigger_only=False): """Try to expand a snippet in the current place.""" before, snippets = self._can_expand(autotrigger_only) if snippets: # prefer snippets with context if any snippets_with_context = [s for s in snippets if s.context] if snippets_with_context: snippets = snippets_with_context if not snippets: # No snippet found return False vim_helper.command("let &undolevels = &undolevels") if len(snippets) == 1: snippet = snippets[0] else: snippet = _ask_snippets(snippets) if not snippet: return True self._do_snippet(snippet, before) vim_helper.command("let &undolevels = &undolevels") return True
def jump_backwards(self): """Jumps to the previous tabstop.""" vim_helper.command("let g:ulti_jump_backwards_res = 1") vim_helper.command("let &undolevels = &undolevels") if not self._jump(True): vim_helper.command("let g:ulti_jump_backwards_res = 0") return self._handle_failure(self.backward_trigger)
def jump_forwards(self): """Jumps to the next tabstop.""" vim_helper.command("let g:ulti_jump_forwards_res = 1") vim_helper.command("let &undolevels = &undolevels") if not self._jump(): vim_helper.command("let g:ulti_jump_forwards_res = 0") return self._handle_failure(self.forward_trigger)
def expand_or_jump(self): """This function is used for people who wants to have the same trigger for expansion and forward jumping. It first tries to expand a snippet, if this fails, it tries to jump forward. """ vim_helper.command("let g:ulti_expand_or_jump_res = 1") rv = self._try_expand() if not rv: vim_helper.command("let g:ulti_expand_or_jump_res = 2") rv = self._jump() if not rv: vim_helper.command("let g:ulti_expand_or_jump_res = 0") self._handle_failure(self.expand_trigger)
def _teardown_inner_state(self): """Reverse _setup_inner_state.""" if not self._inner_state_up: return try: vim_helper.command( "silent doautocmd <nomodeline> User UltiSnipsExitLastSnippet" ) if self.expand_trigger != self.forward_trigger: vim_helper.command("iunmap <buffer> %s" % self.forward_trigger) vim_helper.command("sunmap <buffer> %s" % self.forward_trigger) vim_helper.command("iunmap <buffer> %s" % self.backward_trigger) vim_helper.command("sunmap <buffer> %s" % self.backward_trigger) vim_helper.command("augroup UltiSnips") vim_helper.command("autocmd!") vim_helper.command("augroup END") self._inner_state_up = False except vim_helper.error: # This happens when a preview window was opened. This issues # CursorMoved, but not BufLeave. We have no way to unmap, until we # are back in our buffer pass
def _setup_inner_state(self): """Map keys and create autocommands that should only be defined when a snippet is active.""" if self._inner_state_up: return if self.expand_trigger != self.forward_trigger: vim_helper.command( "inoremap <buffer><nowait><silent> " + self.forward_trigger + " <C-R>=UltiSnips#JumpForwards()<cr>" ) vim_helper.command( "snoremap <buffer><nowait><silent> " + self.forward_trigger + " <Esc>:call UltiSnips#JumpForwards()<cr>" ) vim_helper.command( "inoremap <buffer><nowait><silent> " + self.backward_trigger + " <C-R>=UltiSnips#JumpBackwards()<cr>" ) vim_helper.command( "snoremap <buffer><nowait><silent> " + self.backward_trigger + " <Esc>:call UltiSnips#JumpBackwards()<cr>" ) # Setup the autogroups. vim_helper.command("augroup UltiSnips") vim_helper.command("autocmd!") vim_helper.command("autocmd CursorMovedI * call UltiSnips#CursorMoved()") vim_helper.command("autocmd CursorMoved * call UltiSnips#CursorMoved()") vim_helper.command("autocmd InsertLeave * call UltiSnips#LeavingInsertMode()") vim_helper.command("autocmd BufLeave * call UltiSnips#LeavingBuffer()") vim_helper.command("autocmd CmdwinEnter * call UltiSnips#LeavingBuffer()") vim_helper.command("autocmd CmdwinLeave * call UltiSnips#LeavingBuffer()") # Also exit the snippet when we enter a unite complete buffer. vim_helper.command("autocmd Filetype unite call UltiSnips#LeavingBuffer()") vim_helper.command("augroup END") vim_helper.command( "silent doautocmd <nomodeline> User UltiSnipsEnterFirstSnippet" ) self._inner_state_up = True
def expand(self): """Try to expand a snippet at the current position.""" vim_helper.command("let g:ulti_expand_res = 1") if not self._try_expand(): vim_helper.command("let g:ulti_expand_res = 0") self._handle_failure(self.expand_trigger)
def _teardown_inner_state(self): """Reverse _setup_inner_state.""" if not self._inner_state_up: return try: vim_helper.command( "silent doautocmd <nomodeline> User UltiSnipsExitLastSnippet") if self.expand_trigger != self.forward_trigger: vim_helper.command("iunmap <buffer> %s" % self.forward_trigger) vim_helper.command("sunmap <buffer> %s" % self.forward_trigger) vim_helper.command("iunmap <buffer> %s" % self.backward_trigger) vim_helper.command("sunmap <buffer> %s" % self.backward_trigger) vim_helper.command("augroup UltiSnips") vim_helper.command("autocmd!") vim_helper.command("augroup END") if vim_helper.eval( 'g:UltiSnipsDisablePreviewWindowInSnips') == '1': # restore completeopt vim_helper.command( 'let &completeopt = g:ultisnips_completopt_old') except vim_helper.error: # This happens when a preview window was opened. This issues # CursorMoved, but not BufLeave. We have no way to unmap, until we # are back in our buffer pass finally: self._inner_state_up = False
def _show_user_warning(msg): """Shows a Vim warning message to the user.""" vim_helper.command("echohl WarningMsg") vim_helper.command('echom "%s"' % msg.replace('"', '\\"')) vim_helper.command("echohl None")
def restore_unnamed_register(self): """Restores the unnamed register and forgets what we cached.""" if not self._unnamed_reg_cached: return vim_helper.command('let @" = g:_ultisnips_unnamed_reg_cache') self._unnamed_reg_cached = False
def _jump(self, backwards=False): """Helper method that does the actual jump.""" if self._should_update_textobjects: self._should_reset_visual = False self._cursor_moved() # we need to set 'onemore' there, because of limitations of the vim # API regarding cursor movements; without that test # 'CanExpandAnonSnippetInJumpActionWhileSelected' will fail with vim_helper.option_set_to("ve", "onemore"): jumped = False # We need to remember current snippets stack here because of # post-jump action on the last tabstop should be able to access # snippet instance which is ended just now. stack_for_post_jump = self._active_snippets[:] # If next tab has length 1 and the distance between itself and # self._ctab is 1 then there is 1 less CursorMove events. We # cannot ignore next movement in such case. ntab_short_and_near = False if self._current_snippet: snippet_for_action = self._current_snippet elif stack_for_post_jump: snippet_for_action = stack_for_post_jump[-1] else: snippet_for_action = None if self._current_snippet: ntab = self._current_snippet.select_next_tab(backwards) if ntab: if self._current_snippet.snippet.has_option("s"): lineno = vim_helper.buf.cursor.line vim_helper.buf[lineno] = vim_helper.buf[lineno].rstrip() vim_helper.select(ntab.start, ntab.end) jumped = True if ( self._ctab is not None and ntab.start - self._ctab.end == Position(0, 1) and ntab.end - ntab.start == Position(0, 1) ): ntab_short_and_near = True self._ctab = ntab # Run interpolations again to update new placeholder # values, binded to currently newly jumped placeholder. self._visual_content.conserve_placeholder(self._ctab) self._current_snippet.current_placeholder = ( self._visual_content.placeholder ) self._should_reset_visual = False self._active_snippets[0].update_textobjects(vim_helper.buf) # Open any folds this might have created vim_helper.command("normal! zv") self._vstate.remember_buffer(self._active_snippets[0]) if ntab.number == 0 and self._active_snippets: self._current_snippet_is_done() else: # This really shouldn't happen, because a snippet should # have been popped when its final tabstop was used. # Cleanup by removing current snippet and recursing. self._current_snippet_is_done() jumped = self._jump(backwards) if jumped: if self._ctab: self._vstate.remember_position() self._vstate.remember_unnamed_register(self._ctab.current_text) if not ntab_short_and_near: self._ignore_movements = True if len(stack_for_post_jump) > 0 and ntab is not None: with use_proxy_buffer(stack_for_post_jump, self._vstate): snippet_for_action.snippet.do_post_jump( ntab.number, -1 if backwards else 1, stack_for_post_jump, snippet_for_action, ) return jumped
def _do_snippet(self, snippet, before): """Expands the given snippet, and handles everything that needs to be done with it.""" self._setup_inner_state() self._snip_expanded_in_action = False self._should_update_textobjects = False # Adjust before, maybe the trigger is not the complete word text_before = before if snippet.matched: text_before = before[: -len(snippet.matched)] with use_proxy_buffer(self._active_snippets, self._vstate): with self._action_context(): cursor_set_in_action = snippet.do_pre_expand( self._visual_content.text, self._active_snippets ) if cursor_set_in_action: text_before = vim_helper.buf.line_till_cursor before = vim_helper.buf.line_till_cursor with suspend_proxy_edits(): start = Position(vim_helper.buf.cursor.line, len(text_before)) end = Position(vim_helper.buf.cursor.line, len(before)) parent = None if self._current_snippet: # If cursor is set in pre-action, then action was modified # cursor line, in that case we do not need to do any edits, it # can break snippet if not cursor_set_in_action: # It could be that our trigger contains the content of # TextObjects in our containing snippet. If this is indeed # the case, we have to make sure that those are properly # killed. We do this by pretending that the user deleted # and retyped the text that our trigger matched. edit_actions = [ ("D", start.line, start.col, snippet.matched), ("I", start.line, start.col, snippet.matched), ] self._active_snippets[0].replay_user_edits(edit_actions) parent = self._current_snippet.find_parent_for_new_to(start) snippet_instance = snippet.launch( text_before, self._visual_content, parent, start, end ) # Open any folds this might have created vim_helper.command("normal! zv") self._visual_content.reset() self._active_snippets.append(snippet_instance) with use_proxy_buffer(self._active_snippets, self._vstate): with self._action_context(): snippet.do_post_expand( snippet_instance._start, snippet_instance._end, self._active_snippets, ) self._vstate.remember_buffer(self._active_snippets[0]) if not self._snip_expanded_in_action: self._jump() elif self._current_snippet.current_text != "": self._jump() else: self._current_snippet_is_done() if self._inside_action: self._snip_expanded_in_action = True
def _setup_inner_state(self): """Map keys and create autocommands that should only be defined when a snippet is active.""" if self._inner_state_up: return if self.expand_trigger != self.forward_trigger: vim_helper.command("inoremap <buffer><nowait><silent> " + self.forward_trigger + " <C-R>=UltiSnips#JumpForwards()<cr>") vim_helper.command("snoremap <buffer><nowait><silent> " + self.forward_trigger + " <Esc>:call UltiSnips#JumpForwards()<cr>") vim_helper.command("inoremap <buffer><nowait><silent> " + self.backward_trigger + " <C-R>=UltiSnips#JumpBackwards()<cr>") vim_helper.command("snoremap <buffer><nowait><silent> " + self.backward_trigger + " <Esc>:call UltiSnips#JumpBackwards()<cr>") if vim_helper.eval('g:UltiSnipsDisablePreviewWindowInSnips') == '1': # backup completeopt vim_helper.command('let g:ultisnips_completopt_old = &completeopt') # and remove the preview option vim_helper.command('set completeopt-=preview') # Setup the autogroups. vim_helper.command("augroup UltiSnips") vim_helper.command("autocmd!") vim_helper.command( "autocmd CursorMovedI * call UltiSnips#CursorMoved()") vim_helper.command( "autocmd CursorMoved * call UltiSnips#CursorMoved()") vim_helper.command( "autocmd InsertLeave * call UltiSnips#LeavingInsertMode()") vim_helper.command("autocmd BufEnter * call UltiSnips#LeavingBuffer()") vim_helper.command( "autocmd CmdwinEnter * call UltiSnips#LeavingBuffer()") vim_helper.command( "autocmd CmdwinLeave * call UltiSnips#LeavingBuffer()") # Also exit the snippet when we enter a unite complete buffer. vim_helper.command( "autocmd Filetype unite call UltiSnips#LeavingBuffer()") vim_helper.command("augroup END") vim_helper.command( "silent doautocmd <nomodeline> User UltiSnipsEnterFirstSnippet") self._inner_state_up = True