From 4eac9798885f508d388d07847946a925dea583a5 Mon Sep 17 00:00:00 2001 From: Holger Rapp Date: Wed, 11 Apr 2018 00:52:48 +0200 Subject: [PATCH] Disable auto detecting changes to snippet files on expansion (#967) Instead, trigger an auto command whenever a .snippets file is saved that will reload the snippets. This is a performance improvement with a loss of functionality: externally (i.e. outside of the current Vim instance) generated snippet files will not be picked up until UltiSnips#RefreshSnippets is called. I decided to not expose a command for this, since I assume it is rarely useful. I think the command would add confusion of when it was supposed to be ran. Fixes #932. --- autoload/UltiSnips.vim | 5 +- doc/UltiSnips.txt | 5 +- ftplugin/snippets.vim | 9 ++++ pythonx/UltiSnips/snippet/source/_base.py | 12 ++--- .../UltiSnips/snippet/source/file/_base.py | 48 ++++--------------- pythonx/UltiSnips/snippet_manager.py | 6 ++- 6 files changed, 35 insertions(+), 50 deletions(-) diff --git a/autoload/UltiSnips.vim b/autoload/UltiSnips.vim index 1ce68c2..bde2857 100644 --- a/autoload/UltiSnips.vim +++ b/autoload/UltiSnips.vim @@ -131,7 +131,6 @@ function! UltiSnips#Anon(value, ...) return "" endfunction - function! UltiSnips#CursorMoved() exec g:_uspy "UltiSnips_Manager._cursor_moved()" endf @@ -147,4 +146,8 @@ endfunction function! UltiSnips#TrackChange() exec g:_uspy "UltiSnips_Manager._track_change()" endfunction + +function! UltiSnips#RefreshSnippets() + exec g:_uspy "UltiSnips_Manager._refresh_snippets()" +endfunction " }}} diff --git a/doc/UltiSnips.txt b/doc/UltiSnips.txt index 6a97fc6..d23d1ac 100644 --- a/doc/UltiSnips.txt +++ b/doc/UltiSnips.txt @@ -751,8 +751,9 @@ with a backslash, '\'. To illustrate plaintext snippets, let's begin with a simple example. You can try the examples yourself. Simply edit a new file with Vim. Example snippets will be added to the 'all.snippets' file, so you'll want to open it in Vim for -editing as well. > - ~/.vim/UltiSnips/all.snippets +editing as well in the same Vim instance. You can use |UltiSnipsEdit| for this, +but you can also just run > + :tabedit ~/.vim/UltiSnips/all.snippets Add this snippet to 'all.snippets' and save the file. diff --git a/ftplugin/snippets.vim b/ftplugin/snippets.vim index 8479a45..ddbabfb 100644 --- a/ftplugin/snippets.vim +++ b/ftplugin/snippets.vim @@ -17,6 +17,15 @@ setlocal commentstring=#%s setlocal noexpandtab setlocal autoindent nosmartindent nocindent +" Whenever a snippets file is written, we ask UltiSnips to reload all snippet +" files. This feels like auto-updating, but is of course just an +" approximation: If files change outside of the current Vim instance, we will +" not notice. +augroup ultisnips_snippets.vim +autocmd! +autocmd BufWritePost call UltiSnips#RefreshSnippets() +augroup END + " Define match words for use with matchit plugin " http://www.vim.org/scripts/script.php?script_id=39 if exists("loaded_matchit") && !exists("b:match_words") diff --git a/pythonx/UltiSnips/snippet/source/_base.py b/pythonx/UltiSnips/snippet/source/_base.py index 93ba5b0..56670db 100644 --- a/pythonx/UltiSnips/snippet/source/_base.py +++ b/pythonx/UltiSnips/snippet/source/_base.py @@ -16,16 +16,14 @@ class SnippetSource(object): self._snippets = defaultdict(SnippetDictionary) self._extends = defaultdict(set) - def ensure(self, filetypes, cached): - """Update/reload the snippets in the source when needed. - - It makes sure that the snippets are not outdated. + def ensure(self, filetypes): + """Ensures that snippets are loaded.""" + def refresh(self): + """Resets all snippets, so that they are reloaded on the next call to + ensure. """ - def loaded(self, filetypes): - return len(self._snippets) > 0 - def _get_existing_deep_extends(self, base_filetypes): """Helper for get all existing filetypes extended by base filetypes.""" deep_extends = self.get_deep_extends(base_filetypes) diff --git a/pythonx/UltiSnips/snippet/source/file/_base.py b/pythonx/UltiSnips/snippet/source/file/_base.py index daa3f1d..473da30 100644 --- a/pythonx/UltiSnips/snippet/source/file/_base.py +++ b/pythonx/UltiSnips/snippet/source/file/_base.py @@ -4,7 +4,6 @@ """Code to provide access to UltiSnips files from disk.""" from collections import defaultdict -import hashlib import os from UltiSnips import _vim @@ -12,13 +11,6 @@ from UltiSnips import compatibility from UltiSnips.snippet.source._base import SnippetSource -def _hash_file(path): - """Returns a hashdigest of 'path'.""" - if not os.path.isfile(path): - return False - return hashlib.sha1(open(path, 'rb').read()).hexdigest() - - class SnippetSyntaxError(RuntimeError): """Thrown when a syntax error is found in a file.""" @@ -29,24 +21,18 @@ class SnippetSyntaxError(RuntimeError): class SnippetFileSource(SnippetSource): - """Base class that abstracts away 'extends' info and file hashes.""" def __init__(self): SnippetSource.__init__(self) - self._files_for_ft = defaultdict(set) - self._file_hashes = defaultdict(lambda: None) - self._ensure_cached = False - - def ensure(self, filetypes, cached): - if cached and self._ensure_cached: - return + def ensure(self, filetypes): for ft in self.get_deep_extends(filetypes): if self._needs_update(ft): self._load_snippets_for(ft) - self._ensure_cached = True + def refresh(self): + self.__init__() def _get_all_snippet_files_for(self, ft): """Returns a set of all files that define snippets for 'ft'.""" @@ -59,38 +45,22 @@ class SnippetFileSource(SnippetSource): def _needs_update(self, ft): """Returns true if any files for 'ft' have changed and must be reloaded.""" - existing_files = self._get_all_snippet_files_for(ft) - if existing_files != self._files_for_ft[ft]: - self._files_for_ft[ft] = existing_files - return True - - for filename in self._files_for_ft[ft]: - if _hash_file(filename) != self._file_hashes[filename]: - return True - - return False + return not (ft in self._snippets) def _load_snippets_for(self, ft): """Load all snippets for the given 'ft'.""" - if ft in self._snippets: - del self._snippets[ft] - del self._extends[ft] - try: - for fn in self._files_for_ft[ft]: - self._parse_snippets(ft, fn) - except: - del self._files_for_ft[ft] - raise + assert(ft not in self._snippets) + for fn in self._get_all_snippet_files_for(ft): + self._parse_snippets(ft, fn) # Now load for the parents for parent_ft in self.get_deep_extends([ft]): if parent_ft != ft and self._needs_update(parent_ft): self._load_snippets_for(parent_ft) def _parse_snippets(self, ft, filename): - """Parse the 'filename' for the given 'ft' and watch it for changes in - the future.""" - self._file_hashes[filename] = _hash_file(filename) + """Parse the 'filename' for the given 'ft'.""" file_data = compatibility.open_ascii_file(filename, 'r').read() + self._snippets[ft] # Make sure the dictionary exists for event, data in self._parse_snippet_file(file_data, filename): if event == 'error': msg, line_index = data diff --git a/pythonx/UltiSnips/snippet_manager.py b/pythonx/UltiSnips/snippet_manager.py index 3e38125..75c900c 100644 --- a/pythonx/UltiSnips/snippet_manager.py +++ b/pythonx/UltiSnips/snippet_manager.py @@ -569,7 +569,7 @@ class SnippetManager(object): clear_priority = None cleared = {} for _, source in self._snippet_sources: - source.ensure(filetypes, cached=autotrigger_only) + source.ensure(filetypes) # Collect cleared information from sources. for _, source in self._snippet_sources: @@ -859,6 +859,10 @@ class SnippetManager(object): self._should_reset_visual = True + @err_to_scratch_buffer.wrap + def _refresh_snippets(self): + for _, source in self._snippet_sources: + source.refresh() UltiSnips_Manager = SnippetManager( # pylint:disable=invalid-name vim.eval('g:UltiSnipsExpandTrigger'),