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.
This commit is contained in:
Holger Rapp 2018-04-11 00:52:48 +02:00 committed by UltiBot
parent 9014274598
commit 4eac979888
6 changed files with 35 additions and 50 deletions

View File

@ -131,7 +131,6 @@ function! UltiSnips#Anon(value, ...)
return "" return ""
endfunction endfunction
function! UltiSnips#CursorMoved() function! UltiSnips#CursorMoved()
exec g:_uspy "UltiSnips_Manager._cursor_moved()" exec g:_uspy "UltiSnips_Manager._cursor_moved()"
endf endf
@ -147,4 +146,8 @@ endfunction
function! UltiSnips#TrackChange() function! UltiSnips#TrackChange()
exec g:_uspy "UltiSnips_Manager._track_change()" exec g:_uspy "UltiSnips_Manager._track_change()"
endfunction endfunction
function! UltiSnips#RefreshSnippets()
exec g:_uspy "UltiSnips_Manager._refresh_snippets()"
endfunction
" }}} " }}}

View File

@ -751,8 +751,9 @@ with a backslash, '\'.
To illustrate plaintext snippets, let's begin with a simple example. You can 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 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 will be added to the 'all.snippets' file, so you'll want to open it in Vim for
editing as well. > editing as well in the same Vim instance. You can use |UltiSnipsEdit| for this,
~/.vim/UltiSnips/all.snippets but you can also just run >
:tabedit ~/.vim/UltiSnips/all.snippets
Add this snippet to 'all.snippets' and save the file. Add this snippet to 'all.snippets' and save the file.

View File

@ -17,6 +17,15 @@ setlocal commentstring=#%s
setlocal noexpandtab setlocal noexpandtab
setlocal autoindent nosmartindent nocindent 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 <buffer> call UltiSnips#RefreshSnippets()
augroup END
" Define match words for use with matchit plugin " Define match words for use with matchit plugin
" http://www.vim.org/scripts/script.php?script_id=39 " http://www.vim.org/scripts/script.php?script_id=39
if exists("loaded_matchit") && !exists("b:match_words") if exists("loaded_matchit") && !exists("b:match_words")

View File

@ -16,16 +16,14 @@ class SnippetSource(object):
self._snippets = defaultdict(SnippetDictionary) self._snippets = defaultdict(SnippetDictionary)
self._extends = defaultdict(set) self._extends = defaultdict(set)
def ensure(self, filetypes, cached): def ensure(self, filetypes):
"""Update/reload the snippets in the source when needed. """Ensures that snippets are loaded."""
It makes sure that the snippets are not outdated.
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): def _get_existing_deep_extends(self, base_filetypes):
"""Helper for get all existing filetypes extended by base filetypes.""" """Helper for get all existing filetypes extended by base filetypes."""
deep_extends = self.get_deep_extends(base_filetypes) deep_extends = self.get_deep_extends(base_filetypes)

View File

@ -4,7 +4,6 @@
"""Code to provide access to UltiSnips files from disk.""" """Code to provide access to UltiSnips files from disk."""
from collections import defaultdict from collections import defaultdict
import hashlib
import os import os
from UltiSnips import _vim from UltiSnips import _vim
@ -12,13 +11,6 @@ from UltiSnips import compatibility
from UltiSnips.snippet.source._base import SnippetSource 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): class SnippetSyntaxError(RuntimeError):
"""Thrown when a syntax error is found in a file.""" """Thrown when a syntax error is found in a file."""
@ -29,24 +21,18 @@ class SnippetSyntaxError(RuntimeError):
class SnippetFileSource(SnippetSource): class SnippetFileSource(SnippetSource):
"""Base class that abstracts away 'extends' info and file hashes.""" """Base class that abstracts away 'extends' info and file hashes."""
def __init__(self): def __init__(self):
SnippetSource.__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): for ft in self.get_deep_extends(filetypes):
if self._needs_update(ft): if self._needs_update(ft):
self._load_snippets_for(ft) self._load_snippets_for(ft)
self._ensure_cached = True def refresh(self):
self.__init__()
def _get_all_snippet_files_for(self, ft): def _get_all_snippet_files_for(self, ft):
"""Returns a set of all files that define snippets for 'ft'.""" """Returns a set of all files that define snippets for 'ft'."""
@ -59,38 +45,22 @@ class SnippetFileSource(SnippetSource):
def _needs_update(self, ft): def _needs_update(self, ft):
"""Returns true if any files for 'ft' have changed and must be """Returns true if any files for 'ft' have changed and must be
reloaded.""" reloaded."""
existing_files = self._get_all_snippet_files_for(ft) return not (ft in self._snippets)
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
def _load_snippets_for(self, ft): def _load_snippets_for(self, ft):
"""Load all snippets for the given 'ft'.""" """Load all snippets for the given 'ft'."""
if ft in self._snippets: assert(ft not in self._snippets)
del self._snippets[ft] for fn in self._get_all_snippet_files_for(ft):
del self._extends[ft]
try:
for fn in self._files_for_ft[ft]:
self._parse_snippets(ft, fn) self._parse_snippets(ft, fn)
except:
del self._files_for_ft[ft]
raise
# Now load for the parents # Now load for the parents
for parent_ft in self.get_deep_extends([ft]): for parent_ft in self.get_deep_extends([ft]):
if parent_ft != ft and self._needs_update(parent_ft): if parent_ft != ft and self._needs_update(parent_ft):
self._load_snippets_for(parent_ft) self._load_snippets_for(parent_ft)
def _parse_snippets(self, ft, filename): def _parse_snippets(self, ft, filename):
"""Parse the 'filename' for the given 'ft' and watch it for changes in """Parse the 'filename' for the given 'ft'."""
the future."""
self._file_hashes[filename] = _hash_file(filename)
file_data = compatibility.open_ascii_file(filename, 'r').read() 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): for event, data in self._parse_snippet_file(file_data, filename):
if event == 'error': if event == 'error':
msg, line_index = data msg, line_index = data

View File

@ -569,7 +569,7 @@ class SnippetManager(object):
clear_priority = None clear_priority = None
cleared = {} cleared = {}
for _, source in self._snippet_sources: for _, source in self._snippet_sources:
source.ensure(filetypes, cached=autotrigger_only) source.ensure(filetypes)
# Collect cleared information from sources. # Collect cleared information from sources.
for _, source in self._snippet_sources: for _, source in self._snippet_sources:
@ -859,6 +859,10 @@ class SnippetManager(object):
self._should_reset_visual = True 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 UltiSnips_Manager = SnippetManager( # pylint:disable=invalid-name
vim.eval('g:UltiSnipsExpandTrigger'), vim.eval('g:UltiSnipsExpandTrigger'),