def find_all_snippet_directories() -> List[str]: """Returns a list of the absolute path of all potential snippet directories, no matter if they exist or not.""" if vim_helper.eval("exists('b:UltiSnipsSnippetDirectories')") == "1": snippet_dirs = vim_helper.eval("b:UltiSnipsSnippetDirectories") else: snippet_dirs = vim_helper.eval("g:UltiSnipsSnippetDirectories") if len(snippet_dirs) == 1: # To reduce confusion and increase consistency with # `UltiSnipsSnippetsDir`, we expand ~ here too. full_path = os.path.expanduser(snippet_dirs[0]) if os.path.isabs(full_path): return [full_path] all_dirs = [] check_dirs = vim_helper.eval("&runtimepath").split(",") for rtp in check_dirs: for snippet_dir in snippet_dirs: if snippet_dir == "snippets": raise PebkacError( "You have 'snippets' in UltiSnipsSnippetDirectories. This " "directory is reserved for snipMate snippets. Use another " "directory for UltiSnips snippets.") pth = normalize_file_path( os.path.expanduser(os.path.join(rtp, snippet_dir))) # Runtimepath entries may contain wildcards. all_dirs.extend(glob.glob(pth)) return all_dirs
def _parse(self, stream, indent): next(stream) # $ next(stream) # { self.number = _parse_number(stream) if self.number == 0: raise PebkacError("Choices selection is not supported on $0") next(stream) # | choices_text = _parse_till_unescaped_char(stream, "|")[0] choice_list = [] # inside choice item, comma can be escaped by "\," # we need to do a little bit smarter parsing than simply splitting choice_stream = _TextIterator(choices_text, Position(0, 0)) while True: cur_col = choice_stream.pos.col try: result = _parse_till_unescaped_char(choice_stream, ",")[0] if not result: continue choice_list.append(self._get_unescaped_choice_item(result)) except: last_choice_item = self._get_unescaped_choice_item( choices_text[cur_col:]) if last_choice_item: choice_list.append(last_choice_item) break self.choice_list = choice_list self.initial_text = "|{0}|".format(",".join(choice_list)) _parse_till_closing_brace(stream)
def validate_buffer(self): """ Raises exception if buffer is changes beyound proxy object. """ if self.is_buffer_changed_outside(): raise PebkacError( "buffer was modified using vim.command or " + "vim.current.buffer; that changes are untrackable and leads to " + "errors in snippet expansion; use special variable `snip.buffer` " "for buffer modifications.\n\n" + "See :help UltiSnips-buffer-proxy for more info." )
def update_textobjects(self, buf): """Update the text objects that should change automagically after the users edits have been replayed. This might also move the Cursor """ done = set() not_done = set() def _find_recursive(obj): """Finds all text objects and puts them into 'not_done'.""" cursorInsideLowest = None if isinstance(obj, EditableTextObject): if obj.start <= vim_helper.buf.cursor <= obj.end and not ( isinstance(obj, TabStop) and obj.number == 0): cursorInsideLowest = obj for child in obj._children: cursorInsideLowest = _find_recursive( child) or cursorInsideLowest not_done.add(obj) return cursorInsideLowest cursorInsideLowest = _find_recursive(self) if cursorInsideLowest is not None: vc = _VimCursor(cursorInsideLowest) counter = 10 while (done != not_done) and counter: # Order matters for python locals! for obj in sorted(not_done - done): if obj._update(done, buf): done.add(obj) counter -= 1 if not counter: raise PebkacError( "The snippets content did not converge: Check for Cyclic " "dependencies or random strings in your snippet. You can use " "'if not snip.c' to make sure to only expand random output " "once.") if cursorInsideLowest is not None: vc.to_vim() cursorInsideLowest._del_child(vc)
def _execute_action( self, action, context, additional_locals={}, compiled_action=None ): mark_to_use = "`" with vim_helper.save_mark(mark_to_use): vim_helper.set_mark_from_pos(mark_to_use, vim_helper.get_cursor_pos()) cursor_line_before = vim_helper.buf.line_till_cursor locals = {"context": context} locals.update(additional_locals) snip = self._eval_code(action, locals, compiled_action) if snip.cursor.is_set(): vim_helper.buf.cursor = Position( snip.cursor._cursor[0], snip.cursor._cursor[1] ) else: new_mark_pos = vim_helper.get_mark_pos(mark_to_use) cursor_invalid = False if vim_helper._is_pos_zero(new_mark_pos): cursor_invalid = True else: vim_helper.set_cursor_from_pos(new_mark_pos) if cursor_line_before != vim_helper.buf.line_till_cursor: cursor_invalid = True if cursor_invalid: raise PebkacError( "line under the cursor was modified, but " + '"snip.cursor" variable is not set; either set set ' + '"snip.cursor" to new cursor position, or do not ' + "modify cursor line" ) return snip
def _parse(self, stream, indent): for _ in range(8): # ${VISUAL next(stream) if stream.peek() == ":": next(stream) self.alternative_text, char = _parse_till_unescaped_char(stream, "/}") self.alternative_text = unescape(self.alternative_text) if char == "/": # Transformation going on try: self.search = _parse_till_unescaped_char(stream, "/")[0] self.replace = _parse_till_unescaped_char(stream, "/")[0] self.options = _parse_till_closing_brace(stream) except StopIteration: raise PebkacError( "Invalid ${VISUAL} transformation! Forgot to escape a '/'?" ) else: self.search = None self.replace = None self.options = None
def get_dot_vim(): """Returns the likely place for ~/.vim for the current setup.""" home = vim.eval("$HOME") candidates = [] if platform.system() == "Windows": candidates.append(os.path.join(home, "vimfiles")) if vim.eval("has('nvim')") == "1": xdg_home_config = vim.eval("$XDG_CONFIG_HOME") or os.path.join( home, ".config") candidates.append(os.path.join(xdg_home_config, "nvim")) candidates.append(os.path.join(home, ".vim")) if "MYVIMRC" in os.environ: my_vimrc = os.path.expandvars(os.environ["MYVIMRC"]) candidates.append(normalize_file_path(os.path.dirname(my_vimrc))) for candidate in candidates: if os.path.isdir(candidate): return normalize_file_path(candidate) raise PebkacError( "Unable to find user configuration directory. I tried '%s'." % candidates)