diff --git a/autoload/UltiSnips.vim b/autoload/UltiSnips.vim index d93e4c2..d9ba063 100644 --- a/autoload/UltiSnips.vim +++ b/autoload/UltiSnips.vim @@ -30,13 +30,17 @@ function! s:compensate_for_pum() endif endfunction -function! UltiSnips#Edit(...) +function! UltiSnips#Edit(bang, ...) if a:0 == 1 && a:1 != '' let type = a:1 else exec g:_uspy "vim.command(\"let type = '%s'\" % UltiSnips_Manager._primary_filetype)" endif - exec g:_uspy "vim.command(\"let file = '%s'\" % UltiSnips_Manager._file_to_edit(vim.eval(\"type\")))" + exec g:_uspy "vim.command(\"let file = '%s'\" % UltiSnips_Manager._file_to_edit(vim.eval(\"type\"), vim.eval('a:bang')))" + + if !len(file) + return + endif let mode = 'e' if exists('g:UltiSnipsEditSplit') diff --git a/doc/UltiSnips.txt b/doc/UltiSnips.txt index 3fe62a4..157f521 100644 --- a/doc/UltiSnips.txt +++ b/doc/UltiSnips.txt @@ -167,10 +167,10 @@ https://github.com/honza/vim-snippets 3.1 Commands *UltiSnips-commands* ------------ *:UltiSnipsEdit* -The UltiSnipsEdit command opens the private snippet definition file for the -current filetype for editing. If a definition file does not exist, a new file -is opened with the appropriate name. Snippet definition files are standard -text files and can be edited directly. UltiSnipsEdit makes it easier. +The UltiSnipsEdit command opens a private snippet definition file for the +current filetype. If no snippet file exists, a new file is created. If used as +UltiSnipsEdit! all public snippet files are taken into account too. If +multiple files match the search, the user gets to choose the file. There are several variables associated with the UltiSnipsEdit command. @@ -191,6 +191,7 @@ g:UltiSnipsSnippetsDir directories named "snippets" are reserved for snipMate snippets and cannot be used. + *:UltiSnipsAddFiletypes* The UltiSnipsAddFiletypes command allows for explicit merging of other snippet filetypes for the current buffer. For example, if you edit a .rst file but diff --git a/plugin/UltiSnips.vim b/plugin/UltiSnips.vim index 0f9617f..bad9016 100644 --- a/plugin/UltiSnips.vim +++ b/plugin/UltiSnips.vim @@ -11,8 +11,8 @@ if exists('did_UltiSnips_plugin') || &cp || version < 700 endif " The Commands we define. -command! -nargs=? -complete=customlist,UltiSnips#FileTypeComplete UltiSnipsEdit - \ :call UltiSnips#Edit() +command! -bang -nargs=? -complete=customlist,UltiSnips#FileTypeComplete UltiSnipsEdit + \ :call UltiSnips#Edit(, ) command! -nargs=1 UltiSnipsAddFiletypes :call UltiSnips#AddFiletypes() diff --git a/pythonx/UltiSnips/snippet/source/__init__.py b/pythonx/UltiSnips/snippet/source/__init__.py index e4f189f..08c20ac 100644 --- a/pythonx/UltiSnips/snippet/source/__init__.py +++ b/pythonx/UltiSnips/snippet/source/__init__.py @@ -7,4 +7,4 @@ from UltiSnips.snippet.source._base import SnippetSource from UltiSnips.snippet.source.added import AddedSnippetsSource from UltiSnips.snippet.source.file.snipmate import SnipMateFileSource from UltiSnips.snippet.source.file.ultisnips import UltiSnipsFileSource, \ - base_snippet_files_for + find_all_snippet_files, find_snippet_files diff --git a/pythonx/UltiSnips/snippet/source/file/ultisnips.py b/pythonx/UltiSnips/snippet/source/file/ultisnips.py index 8cbe230..495ab98 100644 --- a/pythonx/UltiSnips/snippet/source/file/ultisnips.py +++ b/pythonx/UltiSnips/snippet/source/file/ultisnips.py @@ -13,34 +13,26 @@ from UltiSnips.snippet.source.file._base import SnippetFileSource from UltiSnips.snippet.source.file._common import handle_extends from UltiSnips.text import LineIterator, head_tail +def find_snippet_files(ft, directory): + """Returns all matching snippet files for 'ft' in 'directory'.""" + patterns = ["%s.snippets", "%s_*.snippets", os.path.join("%s", "*")] + ret = set() + directory = os.path.expanduser(directory) + for pattern in patterns: + for fn in glob.glob(os.path.join(directory, pattern % ft)): + ret.add(os.path.realpath(fn)) + return ret -def _plugin_dir(): - """Calculates the plugin directory for UltiSnips.""" - directory = __file__ - for _ in range(10): - directory = os.path.dirname(directory) - if (os.path.isdir(os.path.join(directory, "plugin")) and - os.path.isdir(os.path.join(directory, "doc"))): - return directory - raise Exception("Unable to find the plugin directory.") - -def base_snippet_files_for(ft, default=True): - """Returns a list of snippet files matching the given filetype (ft). - If default is set to false, it doesn't include shipped files. - - Searches through each path in 'runtimepath' in reverse order, - in each of these, it searches each directory name listed in - 'g:UltiSnipsSnippetDirectories' in order, then looks for files in these - directories called 'ft.snippets' or '*_ft.snippets' replacing ft with - the filetype. - """ +def find_all_snippet_files(ft): + """Returns all snippet files matching 'ft' in the given runtime path + directory.""" if _vim.eval("exists('b:UltiSnipsSnippetDirectories')") == "1": snippet_dirs = _vim.eval("b:UltiSnipsSnippetDirectories") else: snippet_dirs = _vim.eval("g:UltiSnipsSnippetDirectories") - base_snippets = os.path.realpath(os.path.join(_plugin_dir(), "UltiSnips")) - ret = [] + patterns = ["%s.snippets", "%s_*.snippets", os.path.join("%s", "*")] + ret = set() for rtp in _vim.eval("&runtimepath").split(','): for snippet_dir in snippet_dirs: if snippet_dir == "snippets": @@ -50,14 +42,9 @@ def base_snippet_files_for(ft, default=True): "directory for UltiSnips snippets.") pth = os.path.realpath(os.path.expanduser( os.path.join(rtp, snippet_dir))) - patterns = ["%s.snippets", "%s_*.snippets", os.path.join("%s", "*")] - if not default and pth == base_snippets: - patterns.remove("%s.snippets") - for pattern in patterns: for fn in glob.glob(os.path.join(pth, pattern % ft)): - if fn not in ret: - ret.append(fn) + ret.add(fn) return ret def _handle_snippet_or_global(line, lines, python_globals, priority): @@ -146,7 +133,7 @@ class UltiSnipsFileSource(SnippetFileSource): """Manages all snippets definitions found in rtp for ultisnips.""" def _get_all_snippet_files_for(self, ft): - return set(base_snippet_files_for(ft)) + return find_all_snippet_files(ft) def _parse_snippet_file(self, filedata, filename): for event, data in _parse_snippets_file(filedata): diff --git a/pythonx/UltiSnips/snippet_manager.py b/pythonx/UltiSnips/snippet_manager.py index 96627d8..86a3d65 100644 --- a/pythonx/UltiSnips/snippet_manager.py +++ b/pythonx/UltiSnips/snippet_manager.py @@ -6,6 +6,7 @@ from collections import defaultdict from functools import wraps import os +import platform import traceback from UltiSnips import _vim @@ -14,23 +15,20 @@ from UltiSnips.compatibility import as_unicode from UltiSnips.position import Position from UltiSnips.snippet.definition import UltiSnipsSnippetDefinition from UltiSnips.snippet.source import UltiSnipsFileSource, SnipMateFileSource, \ - base_snippet_files_for, AddedSnippetsSource + find_all_snippet_files, find_snippet_files, AddedSnippetsSource from UltiSnips.vim_state import VimState, VisualContentPreserver -def _ask_snippets(snippets): - """ Given a list of snippets, ask the user which one they - want to use, and return it. - """ - display = [as_unicode("%i: %s") % (i+1, s.description) for - i, s in enumerate(snippets)] +def _ask_user(a, formatted): + """Asks the user using inputlist() and returns the selected element or + None.""" try: - rv = _vim.eval("inputlist(%s)" % _vim.escape(display)) + rv = _vim.eval("inputlist(%s)" % _vim.escape(formatted)) if rv is None or rv == '0': return None rv = int(rv) - if rv > len(snippets): - rv = len(snippets) - return snippets[rv-1] + if rv > len(a): + rv = len(a) + return a[rv-1] except _vim.error: # Likely "invalid expression", but might be translated. We have no way # of knowing the exact error, therefore, we ignore all errors silently. @@ -38,6 +36,13 @@ def _ask_snippets(snippets): except KeyboardInterrupt: return None +def _ask_snippets(snippets): + """ Given a list of snippets, ask the user which one they + want to use, and return it. + """ + display = [as_unicode("%i: %s") % (i+1, s.description) for + i, s in enumerate(snippets)] + return _ask_user(snippets, display) def err_to_scratch_buffer(func): """Decorator that will catch any Exception that 'func' throws and displays @@ -495,42 +500,43 @@ class SnippetManager(object): any arguments.""" return self._buffer_filetypes[_vim.buf.number][0] - # TODO(sirver): this should talk directly to the UltiSnipsFileSource. - def _file_to_edit(self, ft): # pylint: disable=no-self-use - """ Gets a file to edit based on the given filetype. - If no filetype is given, uses the current filetype from Vim. - - Checks 'g:UltiSnipsSnippetsDir' and uses it if it exists - If a non-shipped file already exists, it uses it. - Otherwise uses a file in ~/.vim/ or ~/vimfiles + def _file_to_edit(self, ft, bang): # pylint: disable=no-self-use + """Returns a file to be edited for the given ft. If 'bang' is empty + only private files in g:UltiSnipsSnippetsDir are considered, otherwise + all files are considered and the user gets to choose. """ # This method is not using self, but is called by UltiSnips.vim and is # therefore in this class because it is the facade to Vim. - edit = None - existing = base_snippet_files_for(ft, False) - filename = ft + ".snippets" + potentials = set() if _vim.eval("exists('g:UltiSnipsSnippetsDir')") == "1": - snipdir = _vim.eval("g:UltiSnipsSnippetsDir") - edit = os.path.join(snipdir, filename) - elif existing: - edit = existing[-1] # last sourced + snippet_dir = _vim.eval("g:UltiSnipsSnippetsDir") else: - home = _vim.eval("$HOME") - rtp = [os.path.realpath(os.path.expanduser(p)) - for p in _vim.eval("&rtp").split(",")] - snippet_dirs = ["UltiSnips"] + \ - _vim.eval("g:UltiSnipsSnippetDirectories") - us = snippet_dirs[-1] + if platform.system() == "Windows": + snippet_dir = os.path.join(_vim.eval("$HOME"), + "_vimfiles", "UltiSnips") + else: + snippet_dir = os.path.join(_vim.eval("$HOME"), + ".vim", "UltiSnips") + potentials.update(find_snippet_files(ft, snippet_dir)) + potentials.add(os.path.join(snippet_dir, ft + '.snippets')) - path = os.path.join(home, ".vim", us) - for dirname in [".vim", "vimfiles"]: - pth = os.path.join(home, dirname) - if pth in rtp: - path = os.path.join(pth, us) + if bang: + potentials.update(find_all_snippet_files(ft)) - if not os.path.isdir(path): - os.mkdir(path) + potentials = set(os.path.realpath(p) for p in potentials) - edit = os.path.join(path, filename) + if len(potentials) > 1: + files = sorted(potentials) + formatted = [as_unicode('%i: %s') % (i, fn) for + i, fn in enumerate(files, 1)] + edit = _ask_user(files, formatted) + if edit is None: + return "" + else: + edit = potentials.pop() + + dirname = os.path.dirname(edit) + if not os.path.exists(dirname): + os.makedirs(dirname) return edit