From 5c87806d7b2b32837f5ea11a9952343d8b27868b Mon Sep 17 00:00:00 2001 From: cwahbong Date: Wed, 16 Jul 2014 19:42:03 +0800 Subject: [PATCH 1/5] Implement priority-based clearsnippets. --- pythonx/UltiSnips/snippet/source/_base.py | 42 +++++++++++++++++++ .../snippet/source/_snippet_dictionary.py | 21 ++++++---- .../UltiSnips/snippet/source/file/_base.py | 7 ++-- .../snippet/source/file/ultisnips.py | 2 +- pythonx/UltiSnips/snippet_manager.py | 18 +++++++- 5 files changed, 75 insertions(+), 15 deletions(-) diff --git a/pythonx/UltiSnips/snippet/source/_base.py b/pythonx/UltiSnips/snippet/source/_base.py index 6c5fdf0..03de530 100644 --- a/pythonx/UltiSnips/snippet/source/_base.py +++ b/pythonx/UltiSnips/snippet/source/_base.py @@ -13,6 +13,9 @@ class SnippetSource(object): def __init__(self): self._snippets = defaultdict(SnippetDictionary) + def ensure(self, filetypes): + pass + def get_snippets(self, filetypes, before, possible): """Returns the snippets for all 'filetypes' (in order) and their parents matching the text 'before'. If 'possible' is true, a partial match is @@ -26,6 +29,45 @@ class SnippetSource(object): found_snippets += self._find_snippets(ft, before, possible) return found_snippets + def get_clear_priority(self, filetypes, seen=None): + priority = None + if not seen: + seen = set() + for ft in filetypes: + seen.add(ft) + snippets = self._snippets[ft] + if priority is None or snippets._clear_priority > priority: + priority = snippets._clear_priority + todo_parent_fts = [] + for ft in filetypes: + extends = self._snippets[ft].extends + havnt_seen = filter(lambda ft: ft not in seen, extends) + seen.update(havnt_seen) + todo_parent_fts.extend(havnt_seen) + if todo_parent_fts: + return max(priority, self.get_clear_priority(todo_parent_fts, seen)) + return priority + + def get_cleared(self, filetypes, seen=None): + cleared = {} + if not seen: + seen = set() + for ft in filetypes: + seen.add(ft) + snippets = self._snippets[ft] + for key, value in snippets._cleared.items(): + if key not in cleared or value > cleared[key]: + cleared[key] = value + todo_parent_fts = [] + for ft in filetypes: + extends = self._snippets[ft].extends + havnt_seen = filter(lambda ft: ft not in seen, extends) + seen.update(havnt_seen) + todo_parent_fts.extend(havnt_seen) + if todo_parent_fts: + cleared.update(self.get_cleared(todo_parent_fts, seen)) + return cleared + def _find_snippets(self, ft, trigger, potentially=False, seen=None): """Find snippets matching 'trigger' for 'ft'. If 'potentially' is True, partial matches are enough.""" diff --git a/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py b/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py index 5be6775..b075333 100644 --- a/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py +++ b/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py @@ -10,6 +10,8 @@ class SnippetDictionary(object): def __init__(self): self._snippets = [] self._extends = [] + self._cleared = {} + self._clear_priority = None def add_snippet(self, snippet): """Add 'snippet' to this dictionary.""" @@ -24,16 +26,17 @@ class SnippetDictionary(object): else: return [s for s in all_snippets if s.could_match(trigger)] - def clear_snippets(self, triggers): - """Remove all snippets that match each trigger in 'triggers'. When - 'triggers' is None, empties this dictionary completely.""" + def clear_snippets(self, priority, triggers): + """Mark snippets as cleared with priority. When 'triggers' is + None, then it updated the clear_priority.""" if not triggers: - self._snippets = [] - return - for trigger in triggers: - for snippet in self.get_matching_snippets(trigger, False): - if snippet in self._snippets: - self._snippets.remove(snippet) + if self._clear_priority is None or priority > self._clear_priority: + self._clear_priority = priority + else: + for trigger in triggers: + if (trigger not in self._cleared or + priority > self._cleared[trigger]): + self._cleared[trigger] = priority @property def extends(self): diff --git a/pythonx/UltiSnips/snippet/source/file/_base.py b/pythonx/UltiSnips/snippet/source/file/_base.py index 425e3dd..1ec325e 100644 --- a/pythonx/UltiSnips/snippet/source/file/_base.py +++ b/pythonx/UltiSnips/snippet/source/file/_base.py @@ -31,10 +31,9 @@ class SnippetFileSource(SnippetSource): self._files_for_ft = defaultdict(set) self._file_hashes = defaultdict(lambda: None) - def get_snippets(self, filetypes, before, possible): + def ensure(self, filetypes): for ft in filetypes: self._ensure_loaded(ft, set()) - return SnippetSource.get_snippets(self, filetypes, before, possible) def _get_all_snippet_files_for(self, ft): """Returns a set of all files that define snippets for 'ft'.""" @@ -94,8 +93,8 @@ class SnippetFileSource(SnippetSource): _vim.escape(filename)) raise SnippetSyntaxError(filename, line_index, msg) elif event == "clearsnippets": - triggers, = data - self._snippets[ft].clear_snippets(triggers) + priority, triggers = data + self._snippets[ft].clear_snippets(priority, triggers) elif event == "extends": # TODO(sirver): extends information is more global # than one snippet source. diff --git a/pythonx/UltiSnips/snippet/source/file/ultisnips.py b/pythonx/UltiSnips/snippet/source/file/ultisnips.py index de0ae96..81878a0 100644 --- a/pythonx/UltiSnips/snippet/source/file/ultisnips.py +++ b/pythonx/UltiSnips/snippet/source/file/ultisnips.py @@ -122,7 +122,7 @@ def _parse_snippets_file(data, filename): elif head == "extends": yield handle_extends(tail, lines.line_index) elif head == "clearsnippets": - yield "clearsnippets", (tail.split(),) + yield "clearsnippets", (current_priority, tail.split()) elif head == "priority": try: current_priority = int(tail.split()[0]) diff --git a/pythonx/UltiSnips/snippet_manager.py b/pythonx/UltiSnips/snippet_manager.py index 22c28e7..c649029 100644 --- a/pythonx/UltiSnips/snippet_manager.py +++ b/pythonx/UltiSnips/snippet_manager.py @@ -445,9 +445,25 @@ class SnippetManager(object): partial is True, then get also return partial matches. """ filetypes = self._buffer_filetypes[_vim.buf.number][::-1] matching_snippets = defaultdict(list) + clear_priority = None + cleared = {} + # Collect cleared information fomr sources. + for _, source in self._snippet_sources: + source.ensure(filetypes) + sclear_priority = source.get_clear_priority(filetypes) + if sclear_priority is not None and (clear_priority is None + or sclear_priority > clear_priority): + clear_priority = sclear_priority + for key, value in source.get_cleared(filetypes).items(): + if key not in cleared or value > cleared[key]: + cleared[key] = value + for _, source in self._snippet_sources: for snippet in source.get_snippets(filetypes, before, partial): - matching_snippets[snippet.trigger].append(snippet) + if ((clear_priority is None or snippet.priority > clear_priority) + and (snippet.trigger not in cleared or + snippet.priority > cleared[snippet.trigger])): + matching_snippets[snippet.trigger].append(snippet) if not matching_snippets: return [] From 20f3832ffa3a477007d3c09914f0ee8bc0118510 Mon Sep 17 00:00:00 2001 From: cwahbong Date: Wed, 16 Jul 2014 22:57:24 +0800 Subject: [PATCH 2/5] Code cleanup. Full tested and does not break and old tests. --- pythonx/UltiSnips/snippet/source/_base.py | 113 ++++++++---------- .../snippet/source/_snippet_dictionary.py | 12 +- 2 files changed, 58 insertions(+), 67 deletions(-) diff --git a/pythonx/UltiSnips/snippet/source/_base.py b/pythonx/UltiSnips/snippet/source/_base.py index 03de530..d051b46 100644 --- a/pythonx/UltiSnips/snippet/source/_base.py +++ b/pythonx/UltiSnips/snippet/source/_base.py @@ -24,66 +24,57 @@ class SnippetSource(object): Returns a list of SnippetDefinition s. """ - found_snippets = [] - for ft in filetypes: - found_snippets += self._find_snippets(ft, before, possible) - return found_snippets + def __inner(fts): + result = [] + existing_fts = filter(lambda ft: ft in self._snippets, fts) + for ft in existing_fts: + snips = self._snippets[ft] + result.extend(snips.get_matching_snippets(before, possible)) + return result + return __inner(filetypes) + __inner(self._extends_all(filetypes)) - def get_clear_priority(self, filetypes, seen=None): - priority = None - if not seen: - seen = set() - for ft in filetypes: - seen.add(ft) - snippets = self._snippets[ft] - if priority is None or snippets._clear_priority > priority: - priority = snippets._clear_priority - todo_parent_fts = [] - for ft in filetypes: - extends = self._snippets[ft].extends - havnt_seen = filter(lambda ft: ft not in seen, extends) + def get_clear_priority(self, filetypes): + def __inner(fts): + pri = None + existing_fts = filter(lambda ft: ft in self._snippets, fts) + for ft in existing_fts: + snippets = self._snippets[ft] + if pri is None or snippets._clear_priority > pri: + pri = snippets._clear_priority + return pri + priority = __inner(filetypes) + deep_clear_priority = __inner(self._extends_all(filetypes)) + if deep_clear_priority is None: + return priority + elif priority is None: + return deep_clear_priority + else: + return max(priority, deep_clear_priority) + + def get_cleared(self, filetypes): + def __inner(fts): + cleared = {} + existing_fts = filter(lambda ft: ft in self._snippets, fts) + for ft in existing_fts: + snippets = self._snippets[ft] + for key, value in snippets._cleared.items(): + if key not in cleared or value > cleared[key]: + cleared[key] = value + return cleared + return dict(__inner(filetypes).items() + __inner(self._extends_all(filetypes)).items()) + + def _extends_all(self, filetypes, seen=None): + """Return deep extends dependency, excluding the filetype itself + """ + if seen is None: + seen = set(filetypes) + + shallow_extends = set() + for filetype in filetypes: + ft_extends = self._snippets[filetype].extends + havnt_seen = set(filter(lambda ft: ft not in seen, ft_extends)) seen.update(havnt_seen) - todo_parent_fts.extend(havnt_seen) - if todo_parent_fts: - return max(priority, self.get_clear_priority(todo_parent_fts, seen)) - return priority - - def get_cleared(self, filetypes, seen=None): - cleared = {} - if not seen: - seen = set() - for ft in filetypes: - seen.add(ft) - snippets = self._snippets[ft] - for key, value in snippets._cleared.items(): - if key not in cleared or value > cleared[key]: - cleared[key] = value - todo_parent_fts = [] - for ft in filetypes: - extends = self._snippets[ft].extends - havnt_seen = filter(lambda ft: ft not in seen, extends) - seen.update(havnt_seen) - todo_parent_fts.extend(havnt_seen) - if todo_parent_fts: - cleared.update(self.get_cleared(todo_parent_fts, seen)) - return cleared - - def _find_snippets(self, ft, trigger, potentially=False, seen=None): - """Find snippets matching 'trigger' for 'ft'. If 'potentially' is True, - partial matches are enough.""" - snips = self._snippets.get(ft, None) - if not snips: - return [] - if not seen: - seen = set() - seen.add(ft) - parent_results = [] - # TODO(sirver): extends information is not bound to one - # source. It should be tracked further up. - for parent_ft in snips.extends: - if parent_ft not in seen: - seen.add(parent_ft) - parent_results += self._find_snippets(parent_ft, trigger, - potentially, seen) - return parent_results + snips.get_matching_snippets( - trigger, potentially) + shallow_extends.update(havnt_seen) + if not shallow_extends: + return shallow_extends + return shallow_extends | self._extends_all(shallow_extends, seen) diff --git a/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py b/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py index b075333..89b92cf 100644 --- a/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py +++ b/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py @@ -30,13 +30,13 @@ class SnippetDictionary(object): """Mark snippets as cleared with priority. When 'triggers' is None, then it updated the clear_priority.""" if not triggers: - if self._clear_priority is None or priority > self._clear_priority: - self._clear_priority = priority + if self._clear_priority is None or priority > self._clear_priority: + self._clear_priority = priority else: - for trigger in triggers: - if (trigger not in self._cleared or - priority > self._cleared[trigger]): - self._cleared[trigger] = priority + for trigger in triggers: + if (trigger not in self._cleared or + priority > self._cleared[trigger]): + self._cleared[trigger] = priority @property def extends(self): From 4784a9e1262c8cc04669dbbdd8fc99684fb9dbe7 Mon Sep 17 00:00:00 2001 From: cwahbong Date: Tue, 22 Jul 2014 15:06:13 +0800 Subject: [PATCH 3/5] Refactor: snippet dictionary does not keep the extend info now. --- .../UltiSnips/snippet/definition/snipmate.py | 0 pythonx/UltiSnips/snippet/parsing/_base.py | 0 pythonx/UltiSnips/snippet/source/_base.py | 40 ++++++++----------- .../snippet/source/_snippet_dictionary.py | 7 ---- .../UltiSnips/snippet/source/file/_base.py | 33 ++++----------- .../UltiSnips/snippet/source/file/_common.py | 0 pythonx/UltiSnips/snippet_manager.py | 4 +- 7 files changed, 26 insertions(+), 58 deletions(-) mode change 100755 => 100644 pythonx/UltiSnips/snippet/definition/snipmate.py mode change 100755 => 100644 pythonx/UltiSnips/snippet/parsing/_base.py mode change 100755 => 100644 pythonx/UltiSnips/snippet/source/file/_common.py diff --git a/pythonx/UltiSnips/snippet/definition/snipmate.py b/pythonx/UltiSnips/snippet/definition/snipmate.py old mode 100755 new mode 100644 diff --git a/pythonx/UltiSnips/snippet/parsing/_base.py b/pythonx/UltiSnips/snippet/parsing/_base.py old mode 100755 new mode 100644 diff --git a/pythonx/UltiSnips/snippet/source/_base.py b/pythonx/UltiSnips/snippet/source/_base.py index d051b46..b795d46 100644 --- a/pythonx/UltiSnips/snippet/source/_base.py +++ b/pythonx/UltiSnips/snippet/source/_base.py @@ -12,6 +12,7 @@ class SnippetSource(object): def __init__(self): self._snippets = defaultdict(SnippetDictionary) + self._extends = defaultdict(set) def ensure(self, filetypes): pass @@ -31,7 +32,7 @@ class SnippetSource(object): snips = self._snippets[ft] result.extend(snips.get_matching_snippets(before, possible)) return result - return __inner(filetypes) + __inner(self._extends_all(filetypes)) + return __inner(self.get_deep_extends(filetypes)) def get_clear_priority(self, filetypes): def __inner(fts): @@ -42,14 +43,7 @@ class SnippetSource(object): if pri is None or snippets._clear_priority > pri: pri = snippets._clear_priority return pri - priority = __inner(filetypes) - deep_clear_priority = __inner(self._extends_all(filetypes)) - if deep_clear_priority is None: - return priority - elif priority is None: - return deep_clear_priority - else: - return max(priority, deep_clear_priority) + return __inner(self.get_deep_extends(filetypes)) def get_cleared(self, filetypes): def __inner(fts): @@ -61,20 +55,18 @@ class SnippetSource(object): if key not in cleared or value > cleared[key]: cleared[key] = value return cleared - return dict(__inner(filetypes).items() + __inner(self._extends_all(filetypes)).items()) + return __inner(self.get_deep_extends(filetypes)) - def _extends_all(self, filetypes, seen=None): - """Return deep extends dependency, excluding the filetype itself - """ - if seen is None: - seen = set(filetypes) + def update_extends(self, child_ft, parent_fts): + self._extends[child_ft].update(parent_fts) - shallow_extends = set() - for filetype in filetypes: - ft_extends = self._snippets[filetype].extends - havnt_seen = set(filter(lambda ft: ft not in seen, ft_extends)) - seen.update(havnt_seen) - shallow_extends.update(havnt_seen) - if not shallow_extends: - return shallow_extends - return shallow_extends | self._extends_all(shallow_extends, seen) + def get_deep_extends(self, root_filetypes): + seen = set(root_filetypes) + l = list(set(root_filetypes)) + while l: + top = l.pop() + for ft in self._extends[top]: + if ft not in seen: + seen.add(ft) + l.append(ft) + return seen diff --git a/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py b/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py index 89b92cf..351ba68 100644 --- a/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py +++ b/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py @@ -3,13 +3,11 @@ """Implements a container for parsed snippets.""" -# TODO(sirver): This class should not keep track of extends. class SnippetDictionary(object): """See module docstring.""" def __init__(self): self._snippets = [] - self._extends = [] self._cleared = {} self._clear_priority = None @@ -37,8 +35,3 @@ class SnippetDictionary(object): if (trigger not in self._cleared or priority > self._cleared[trigger]): self._cleared[trigger] = priority - - @property - def extends(self): - """The list of filetypes this filetype extends.""" - return self._extends diff --git a/pythonx/UltiSnips/snippet/source/file/_base.py b/pythonx/UltiSnips/snippet/source/file/_base.py index 1ec325e..20f67e8 100644 --- a/pythonx/UltiSnips/snippet/source/file/_base.py +++ b/pythonx/UltiSnips/snippet/source/file/_base.py @@ -32,8 +32,9 @@ class SnippetFileSource(SnippetSource): self._file_hashes = defaultdict(lambda: None) def ensure(self, filetypes): - for ft in filetypes: - self._ensure_loaded(ft, set()) + for ft in self.get_deep_extends(filetypes): + if self._needs_update(ft): + self._load_snippets_for(ft) def _get_all_snippet_files_for(self, ft): """Returns a set of all files that define snippets for 'ft'.""" @@ -43,19 +44,6 @@ class SnippetFileSource(SnippetSource): """Parses 'filedata' as a snippet file and yields events.""" raise NotImplementedError() - def _ensure_loaded(self, ft, already_loaded): - """Make sure that the snippets for 'ft' and everything it extends are - loaded.""" - if ft in already_loaded: - return - already_loaded.add(ft) - - if self._needs_update(ft): - self._load_snippets_for(ft) - - for parent in self._snippets[ft].extends: - self._ensure_loaded(parent, already_loaded) - def _needs_update(self, ft): """Returns true if any files for 'ft' have changed and must be reloaded.""" @@ -74,11 +62,12 @@ class SnippetFileSource(SnippetSource): """Load all snippets for the given 'ft'.""" if ft in self._snippets: del self._snippets[ft] + del self._extends[ft] for fn in self._files_for_ft[ft]: self._parse_snippets(ft, fn) # Now load for the parents - for parent_ft in self._snippets[ft].extends: - if parent_ft not in self._snippets: + 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): @@ -99,17 +88,9 @@ class SnippetFileSource(SnippetSource): # TODO(sirver): extends information is more global # than one snippet source. filetypes, = data - self._add_extending_info(ft, filetypes) + self.update_extends(ft, filetypes) elif event == "snippet": snippet, = data self._snippets[ft].add_snippet(snippet) else: assert False, "Unhandled %s: %r" % (event, data) - - def _add_extending_info(self, ft, parents): - """Add the list of 'parents' as being extended by the 'ft'.""" - sd = self._snippets[ft] - for parent in parents: - if parent in sd.extends: - continue - sd.extends.append(parent) diff --git a/pythonx/UltiSnips/snippet/source/file/_common.py b/pythonx/UltiSnips/snippet/source/file/_common.py old mode 100755 new mode 100644 diff --git a/pythonx/UltiSnips/snippet_manager.py b/pythonx/UltiSnips/snippet_manager.py index c649029..8dcbcb8 100644 --- a/pythonx/UltiSnips/snippet_manager.py +++ b/pythonx/UltiSnips/snippet_manager.py @@ -447,9 +447,11 @@ class SnippetManager(object): matching_snippets = defaultdict(list) clear_priority = None cleared = {} - # Collect cleared information fomr sources. for _, source in self._snippet_sources: source.ensure(filetypes) + + # Collect cleared information from sources. + for _, source in self._snippet_sources: sclear_priority = source.get_clear_priority(filetypes) if sclear_priority is not None and (clear_priority is None or sclear_priority > clear_priority): From 63f65ea87421b63617692a29e0dfb4820b264e84 Mon Sep 17 00:00:00 2001 From: cwahbong Date: Sat, 26 Jul 2014 16:10:22 +0800 Subject: [PATCH 4/5] Add/modify test cases for priority-based clearsnippets. --- test/test_ParseSnippets.py | 8 ++--- test/test_SnippetPriorities.py | 65 ++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/test/test_ParseSnippets.py b/test/test_ParseSnippets.py index 072f0e0..4a870a5 100644 --- a/test/test_ParseSnippets.py +++ b/test/test_ParseSnippets.py @@ -64,6 +64,8 @@ class ParseSnippets_ClearAll(_VimTest): class ParseSnippets_ClearOne(_VimTest): files = { "us/all.snippets": r""" + clearsnippets toclear + snippet testsnip "Test snippet" This is a test. endsnippet @@ -71,14 +73,14 @@ class ParseSnippets_ClearOne(_VimTest): snippet toclear "Snippet to clear" Do not expand. endsnippet - - clearsnippets toclear """} keys = "toclear" + EX + "\n" + "testsnip" + EX wanted = "toclear" + EX + "\n" + "This is a test." class ParseSnippets_ClearTwo(_VimTest): files = { "us/all.snippets": r""" + clearsnippets testsnip toclear + snippet testsnip "Test snippet" This is a test. endsnippet @@ -86,8 +88,6 @@ class ParseSnippets_ClearTwo(_VimTest): snippet toclear "Snippet to clear" Do not expand. endsnippet - - clearsnippets testsnip toclear """} keys = "toclear" + EX + "\n" + "testsnip" + EX wanted = "toclear" + EX + "\n" + "testsnip" + EX diff --git a/test/test_SnippetPriorities.py b/test/test_SnippetPriorities.py index ae2ea8c..a43a3ea 100644 --- a/test/test_SnippetPriorities.py +++ b/test/test_SnippetPriorities.py @@ -10,6 +10,7 @@ class SnippetPriorities_MultiWordTriggerOverwriteExisting(_VimTest): ) keys = "test me" + EX wanted = "We overwrite" + class SnippetPriorities_DoNotCareAboutNonMatchings(_VimTest): snippets = ( ("test1", "Hallo", "Types Hallo"), @@ -17,6 +18,7 @@ class SnippetPriorities_DoNotCareAboutNonMatchings(_VimTest): ) keys = "test1" + EX wanted = "Hallo" + class SnippetPriorities_OverwriteExisting(_VimTest): snippets = ( ("test", "${1:Hallo}", "Types Hallo"), @@ -25,6 +27,7 @@ class SnippetPriorities_OverwriteExisting(_VimTest): ) keys = "test" + EX wanted = "We overwrite" + class SnippetPriorities_OverwriteTwice_ECR(_VimTest): snippets = ( ("test", "${1:Hallo}", "Types Hallo"), @@ -34,6 +37,7 @@ class SnippetPriorities_OverwriteTwice_ECR(_VimTest): ) keys = "test" + EX wanted = "again" + class SnippetPriorities_OverwriteThenChoose_ECR(_VimTest): snippets = ( ("test", "${1:Hallo}", "Types Hallo"), @@ -43,6 +47,7 @@ class SnippetPriorities_OverwriteThenChoose_ECR(_VimTest): ) keys = "test" + EX + "1\n\n" + "test" + EX + "2\n" wanted = "We overwrite\nNo overwrite" + class SnippetPriorities_AddedHasHigherThanFile(_VimTest): files = { "us/all.snippets": r""" snippet test "Test Snippet" b @@ -54,6 +59,7 @@ class SnippetPriorities_AddedHasHigherThanFile(_VimTest): ) keys = "test" + EX wanted = "We overwrite" + class SnippetPriorities_FileHasHigherThanAdded(_VimTest): files = { "us/all.snippets": r""" snippet test "Test Snippet" b @@ -65,6 +71,7 @@ class SnippetPriorities_FileHasHigherThanAdded(_VimTest): ) keys = "test" + EX wanted = "This is a test snippet" + class SnippetPriorities_FileHasHigherThanAdded(_VimTest): files = { "us/all.snippets": r""" priority -3 @@ -77,4 +84,62 @@ class SnippetPriorities_FileHasHigherThanAdded(_VimTest): ) keys = "test" + EX wanted = "This is a test snippet" + +class SnippetPriorities_SimpleClear(_VimTest): + files = { + "us/all.snippets": r""" + priority 1 + clearsnippets + priority -1 + snippet test "Test Snippet" + Should not expand to this. + endsnippet + """ + } + keys = "test" + EX + wanted = "test" + EX + +class SnippetPriorities_SimpleClear2(_VimTest): + files = { + "us/all.snippets": r""" + clearsnippets + snippet test "Test snippet" + Should not expand to this. + endsnippet + """ + } + keys = "test" + EX + wanted = "test" + EX + +class SnippetPriorities_ClearedByParent(_VimTest): + files = { + "us/p.snippets": r""" + clearsnippets + """, + "us/c.snippets": r""" + extends p + snippet test "Test snippets" + Should not expand to this. + endsnippet + """ + } + keys = ESC + ":set ft=c\n" + "itest" + EX + wanted = "test" + EX + +class SnippetPriorities_ClearedByChild(_VimTest): + files = { + "us/p.snippets": r""" + snippet test "Test snippets" + Should only expand in p. + endsnippet + """, + "us/c.snippets": r""" + extends p + clearsnippets + """ + } + keys = (ESC + ":set ft=p\n" + "itest" + EX + "\n" + + ESC + ":set ft=c\n" + "itest" + EX + ESC + ":set ft=p") + wanted = "Should only expand in p.\ntest" + EX + # End: Snippet Priority #}}} From 0abfc3bececa47eeec6fcdbaac0318fd92f98c76 Mon Sep 17 00:00:00 2001 From: cwahbong Date: Thu, 31 Jul 2014 21:50:43 +0800 Subject: [PATCH 5/5] Some refinements. 1. Add docstrings. 2. Re-implement get_deep_extends(). 3. Remove all inners. --- pythonx/UltiSnips/snippet/source/_base.py | 85 +++++++++++-------- .../snippet/source/_snippet_dictionary.py | 4 +- 2 files changed, 51 insertions(+), 38 deletions(-) diff --git a/pythonx/UltiSnips/snippet/source/_base.py b/pythonx/UltiSnips/snippet/source/_base.py index b795d46..bca5079 100644 --- a/pythonx/UltiSnips/snippet/source/_base.py +++ b/pythonx/UltiSnips/snippet/source/_base.py @@ -15,8 +15,18 @@ class SnippetSource(object): self._extends = defaultdict(set) def ensure(self, filetypes): + """Update/reload the snippets in the source when needed. It makes sure + that the snippets are not outdated. + """ pass + 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) + return [ft for ft in deep_extends if ft in self._snippets] + def get_snippets(self, filetypes, before, possible): """Returns the snippets for all 'filetypes' (in order) and their parents matching the text 'before'. If 'possible' is true, a partial match is @@ -25,48 +35,51 @@ class SnippetSource(object): Returns a list of SnippetDefinition s. """ - def __inner(fts): - result = [] - existing_fts = filter(lambda ft: ft in self._snippets, fts) - for ft in existing_fts: - snips = self._snippets[ft] - result.extend(snips.get_matching_snippets(before, possible)) - return result - return __inner(self.get_deep_extends(filetypes)) + result = [] + for ft in self._get_existing_deep_extends(filetypes): + snips = self._snippets[ft] + result.extend(snips.get_matching_snippets(before, possible)) + return result def get_clear_priority(self, filetypes): - def __inner(fts): - pri = None - existing_fts = filter(lambda ft: ft in self._snippets, fts) - for ft in existing_fts: - snippets = self._snippets[ft] - if pri is None or snippets._clear_priority > pri: - pri = snippets._clear_priority - return pri - return __inner(self.get_deep_extends(filetypes)) + """Get maximum clearsnippets priority without arguments for specified + filetypes, if any. It returns None if there are no clearsnippets. + """ + pri = None + for ft in self._get_existing_deep_extends(filetypes): + snippets = self._snippets[ft] + if pri is None or snippets._clear_priority > pri: + pri = snippets._clear_priority + return pri def get_cleared(self, filetypes): - def __inner(fts): - cleared = {} - existing_fts = filter(lambda ft: ft in self._snippets, fts) - for ft in existing_fts: - snippets = self._snippets[ft] - for key, value in snippets._cleared.items(): - if key not in cleared or value > cleared[key]: - cleared[key] = value - return cleared - return __inner(self.get_deep_extends(filetypes)) + """Get a set of cleared snippets marked by clearsnippets with arguments + for specified filetypes. + """ + cleared = {} + for ft in self._get_existing_deep_extends(filetypes): + snippets = self._snippets[ft] + for key, value in snippets._cleared.items(): + if key not in cleared or value > cleared[key]: + cleared[key] = value + return cleared def update_extends(self, child_ft, parent_fts): + """Update the extending relation by given child filetype and + its parent filetypes. + """ self._extends[child_ft].update(parent_fts) - def get_deep_extends(self, root_filetypes): - seen = set(root_filetypes) - l = list(set(root_filetypes)) - while l: - top = l.pop() - for ft in self._extends[top]: - if ft not in seen: - seen.add(ft) - l.append(ft) + def get_deep_extends(self, base_filetypes): + """Get a list of filetypes that is either directed or indirected + extended by given base filetypes. Note that the returned list + include the root filetype itself. + """ + seen = set(base_filetypes) + todo_fts = list(set(base_filetypes)) + while todo_fts: + todo_ft = todo_fts.pop() + unseen_extends = set(ft for ft in self._extends[todo_ft] if ft not in seen) + seen.update(unseen_extends) + todo_fts.extend(unseen_extends) return seen diff --git a/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py b/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py index 351ba68..5b23a32 100644 --- a/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py +++ b/pythonx/UltiSnips/snippet/source/_snippet_dictionary.py @@ -25,8 +25,8 @@ class SnippetDictionary(object): return [s for s in all_snippets if s.could_match(trigger)] def clear_snippets(self, priority, triggers): - """Mark snippets as cleared with priority. When 'triggers' is - None, then it updated the clear_priority.""" + """Clear the snippets by mark them as cleared. If trigger is + None, it updates the value of clear priority instead.""" if not triggers: if self._clear_priority is None or priority > self._clear_priority: self._clear_priority = priority