Fixed vim and zsh

This commit is contained in:
2018-04-05 13:06:54 +02:00
parent f9db886bd3
commit 0331f6518a
2009 changed files with 256303 additions and 0 deletions

View File

@ -0,0 +1,10 @@
#!/usr/bin/env python
# encoding: utf-8
"""Sources of snippet definitions."""
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, \
find_all_snippet_files, find_snippet_files

View File

@ -0,0 +1,97 @@
#!/usr/bin/env python
# encoding: utf-8
"""Base class for snippet sources."""
from collections import defaultdict
from UltiSnips.snippet.source._snippet_dictionary import SnippetDictionary
class SnippetSource(object):
"""See module docstring."""
def __init__(self):
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 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)
return [ft for ft in deep_extends if ft in self._snippets]
def get_snippets(self, filetypes, before, possible, autotrigger_only,
visual_content):
"""Returns the snippets for all 'filetypes' (in order) and their
parents matching the text 'before'. If 'possible' is true, a partial
match is enough. Base classes can override this method to provide means
of creating snippets on the fly.
Returns a list of SnippetDefinition s.
"""
result = []
for ft in self._get_existing_deep_extends(filetypes):
snips = self._snippets[ft]
result.extend(snips.get_matching_snippets(before, possible,
autotrigger_only,
visual_content))
return result
def get_clear_priority(self, 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):
"""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, 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

View File

@ -0,0 +1,60 @@
#!/usr/bin/env python
# encoding: utf-8
"""Implements a container for parsed snippets."""
class SnippetDictionary(object):
"""See module docstring."""
def __init__(self):
self._snippets = []
self._cleared = {}
self._clear_priority = float("-inf")
def add_snippet(self, snippet):
"""Add 'snippet' to this dictionary."""
self._snippets.append(snippet)
def get_matching_snippets(self, trigger, potentially, autotrigger_only,
visual_content):
"""Returns all snippets matching the given trigger.
If 'potentially' is true, returns all that could_match().
If 'autotrigger_only' is true, function will return only snippets which
are marked with flag 'A' (should be automatically expanded without
trigger key press).
It's handled specially to avoid walking down the list of all snippets,
which can be very slow, because function will be called on each change
made in insert mode.
"""
all_snippets = self._snippets
if autotrigger_only:
all_snippets = [s for s in all_snippets if s.has_option('A')]
if not potentially:
return [s for s in all_snippets if s.matches(trigger,
visual_content)]
else:
return [s for s in all_snippets if s.could_match(trigger)]
def clear_snippets(self, priority, triggers):
"""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
else:
for trigger in triggers:
if (trigger not in self._cleared or
priority > self._cleared[trigger]):
self._cleared[trigger] = priority
def __len__(self):
return len(self._snippets)

View File

@ -0,0 +1,15 @@
#!/usr/bin/env python
# encoding: utf-8
"""Handles manually added snippets UltiSnips_Manager.add_snippet()."""
from UltiSnips.snippet.source._base import SnippetSource
class AddedSnippetsSource(SnippetSource):
"""See module docstring."""
def add_snippet(self, ft, snippet):
"""Adds the given 'snippet' for 'ft'."""
self._snippets[ft].add_snippet(snippet)

View File

@ -0,0 +1 @@
"""Snippet sources that are file based."""

View File

@ -0,0 +1,112 @@
#!/usr/bin/env python
# encoding: utf-8
"""Code to provide access to UltiSnips files from disk."""
from collections import defaultdict
import hashlib
import os
from UltiSnips import _vim
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."""
def __init__(self, filename, line_index, msg):
RuntimeError.__init__(self, '%s in %s:%d' % (
msg, filename, line_index))
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
for ft in self.get_deep_extends(filetypes):
if self._needs_update(ft):
self._load_snippets_for(ft)
self._ensure_cached = True
def _get_all_snippet_files_for(self, ft):
"""Returns a set of all files that define snippets for 'ft'."""
raise NotImplementedError()
def _parse_snippet_file(self, filedata, filename):
"""Parses 'filedata' as a snippet file and yields events."""
raise NotImplementedError()
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
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
# 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)
file_data = compatibility.open_ascii_file(filename, 'r').read()
for event, data in self._parse_snippet_file(file_data, filename):
if event == 'error':
msg, line_index = data
filename = _vim.eval("""fnamemodify(%s, ":~:.")""" %
_vim.escape(filename))
raise SnippetSyntaxError(filename, line_index, msg)
elif event == 'clearsnippets':
priority, triggers = data
self._snippets[ft].clear_snippets(priority, triggers)
elif event == 'extends':
# TODO(sirver): extends information is more global
# than one snippet source.
filetypes, = data
self.update_extends(ft, filetypes)
elif event == 'snippet':
snippet, = data
self._snippets[ft].add_snippet(snippet)
else:
assert False, 'Unhandled %s: %r' % (event, data)

View File

@ -0,0 +1,29 @@
#!/usr/bin/env python
# encoding: utf-8
"""Common code for snipMate and UltiSnips snippet files."""
def handle_extends(tail, line_index):
"""Handles an extends line in a snippet."""
if tail:
return 'extends', ([p.strip() for p in tail.split(',')],)
else:
return 'error', ("'extends' without file types", line_index)
def handle_action(head, tail, line_index):
if tail:
action = tail.strip('"').replace(r'\"', '"').replace(r'\\\\', r'\\')
return head, (action,)
else:
return 'error', ("'{}' without specified action".format(head),
line_index)
def handle_context(tail, line_index):
if tail:
return 'context', tail.strip('"').replace(r'\"', '"')\
.replace(r'\\\\', r'\\')
else:
return 'error', ("'context' without body", line_index)

View File

@ -0,0 +1,127 @@
#!/usr/bin/env python
# encoding: utf-8
"""Parses snipMate files."""
import os
import glob
from UltiSnips import _vim
from UltiSnips.snippet.definition import SnipMateSnippetDefinition
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 _splitall(path):
"""Split 'path' into all its components."""
# From http://my.safaribooksonline.com/book/programming/
# python/0596001673/files/pythoncook-chp-4-sect-16
allparts = []
while True:
parts = os.path.split(path)
if parts[0] == path: # sentinel for absolute paths
allparts.insert(0, parts[0])
break
elif parts[1] == path: # sentinel for relative paths
allparts.insert(0, parts[1])
break
else:
path = parts[0]
allparts.insert(0, parts[1])
return allparts
def snipmate_files_for(ft):
"""Returns all snipMate files we need to look at for 'ft'."""
if ft == 'all':
ft = '_'
patterns = [
'%s.snippets' % ft,
os.path.join(ft, '*.snippets'),
os.path.join(ft, '*.snippet'),
os.path.join(ft, '*/*.snippet'),
]
ret = set()
for rtp in _vim.eval('&runtimepath').split(','):
path = os.path.realpath(os.path.expanduser(
os.path.join(rtp, 'snippets')))
for pattern in patterns:
for fn in glob.glob(os.path.join(path, pattern)):
ret.add(fn)
return ret
def _parse_snippet_file(content, full_filename):
"""Parses 'content' assuming it is a .snippet file and yields events."""
filename = full_filename[:-len('.snippet')] # strip extension
segments = _splitall(filename)
segments = segments[segments.index('snippets') + 1:]
assert len(segments) in (2, 3)
trigger = segments[1]
description = segments[2] if 2 < len(segments) else ''
# Chomp \n if any.
if content and content.endswith(os.linesep):
content = content[:-len(os.linesep)]
yield 'snippet', (SnipMateSnippetDefinition(trigger, content,
description, full_filename),)
def _parse_snippet(line, lines, filename):
"""Parse a snippet defintions."""
start_line_index = lines.line_index
trigger, description = head_tail(line[len('snippet'):].lstrip())
content = ''
while True:
next_line = lines.peek()
if next_line is None:
break
if next_line.strip() and not next_line.startswith('\t'):
break
line = next(lines)
if line[0] == '\t':
line = line[1:]
content += line
content = content[:-1] # Chomp the last newline
return 'snippet', (SnipMateSnippetDefinition(
trigger, content, description, '%s:%i' % (filename, start_line_index)),)
def _parse_snippets_file(data, filename):
"""Parse 'data' assuming it is a .snippets file.
Yields events in the file.
"""
lines = LineIterator(data)
for line in lines:
if not line.strip():
continue
head, tail = head_tail(line)
if head == 'extends':
yield handle_extends(tail, lines.line_index)
elif head in 'snippet':
snippet = _parse_snippet(line, lines, filename)
if snippet is not None:
yield snippet
elif head and not head.startswith('#'):
yield 'error', ('Invalid line %r' % line.rstrip(), lines.line_index)
class SnipMateFileSource(SnippetFileSource):
"""Manages all snipMate snippet definitions found in rtp."""
def _get_all_snippet_files_for(self, ft):
return snipmate_files_for(ft)
def _parse_snippet_file(self, filedata, filename):
if filename.lower().endswith('snippet'):
for event, data in _parse_snippet_file(filedata, filename):
yield event, data
else:
for event, data in _parse_snippets_file(filedata, filename):
yield event, data

View File

@ -0,0 +1,187 @@
#!/usr/bin/env python
# encoding: utf-8
"""Parsing of snippet files."""
from collections import defaultdict
import glob
import os
from UltiSnips import _vim
from UltiSnips.snippet.definition import UltiSnipsSnippetDefinition
from UltiSnips.snippet.source.file._base import SnippetFileSource
from UltiSnips.snippet.source.file._common import handle_extends, \
handle_action, handle_context
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 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')
if len(snippet_dirs) == 1 and os.path.isabs(snippet_dirs[0]):
check_dirs = ['']
else:
check_dirs = _vim.eval('&runtimepath').split(',')
patterns = ['%s.snippets', '%s_*.snippets', os.path.join('%s', '*')]
ret = set()
for rtp in check_dirs:
for snippet_dir in snippet_dirs:
if snippet_dir == 'snippets':
raise RuntimeError(
"You have 'snippets' in UltiSnipsSnippetDirectories. This "
'directory is reserved for snipMate snippets. Use another '
'directory for UltiSnips snippets.')
pth = os.path.realpath(os.path.expanduser(
os.path.join(rtp, snippet_dir)))
for pattern in patterns:
for fn in glob.glob(os.path.join(pth, pattern % ft)):
ret.add(fn)
return ret
def _handle_snippet_or_global(
filename, line, lines, python_globals, priority, pre_expand, context
):
"""Parses the snippet that begins at the current line."""
start_line_index = lines.line_index
descr = ''
opts = ''
# Ensure this is a snippet
snip = line.split()[0]
# Get and strip options if they exist
remain = line[len(snip):].strip()
words = remain.split()
if len(words) > 2:
# second to last word ends with a quote
if '"' not in words[-1] and words[-2][-1] == '"':
opts = words[-1]
remain = remain[:-len(opts) - 1].rstrip()
if 'e' in opts and not context:
left = remain[:-1].rfind('"')
if left != -1 and left != 0:
context, remain = remain[left:].strip('"'), remain[:left]
# Get and strip description if it exists
remain = remain.strip()
if len(remain.split()) > 1 and remain[-1] == '"':
left = remain[:-1].rfind('"')
if left != -1 and left != 0:
descr, remain = remain[left:], remain[:left]
# The rest is the trigger
trig = remain.strip()
if len(trig.split()) > 1 or 'r' in opts:
if trig[0] != trig[-1]:
return 'error', ("Invalid multiword trigger: '%s'" % trig,
lines.line_index)
trig = trig[1:-1]
end = 'end' + snip
content = ''
found_end = False
for line in lines:
if line.rstrip() == end:
content = content[:-1] # Chomp the last newline
found_end = True
break
content += line
if not found_end:
return 'error', ("Missing 'endsnippet' for %r" %
trig, lines.line_index)
if snip == 'global':
python_globals[trig].append(content)
elif snip == 'snippet':
definition = UltiSnipsSnippetDefinition(
priority, trig, content,
descr, opts, python_globals,
'%s:%i' % (filename, start_line_index),
context, pre_expand)
return 'snippet', (definition,)
else:
return 'error', ("Invalid snippet type: '%s'" % snip, lines.line_index)
def _parse_snippets_file(data, filename):
"""Parse 'data' assuming it is a snippet file.
Yields events in the file.
"""
python_globals = defaultdict(list)
lines = LineIterator(data)
current_priority = 0
actions = {}
context = None
for line in lines:
if not line.strip():
continue
head, tail = head_tail(line)
if head in ('snippet', 'global'):
snippet = _handle_snippet_or_global(
filename, line, lines,
python_globals,
current_priority,
actions,
context
)
actions = {}
context = None
if snippet is not None:
yield snippet
elif head == 'extends':
yield handle_extends(tail, lines.line_index)
elif head == 'clearsnippets':
yield 'clearsnippets', (current_priority, tail.split())
elif head == 'context':
head, context, = handle_context(tail, lines.line_index)
if head == 'error':
yield (head, tail)
elif head == 'priority':
try:
current_priority = int(tail.split()[0])
except (ValueError, IndexError):
yield 'error', ('Invalid priority %r' % tail, lines.line_index)
elif head in ['pre_expand', 'post_expand', 'post_jump']:
head, tail = handle_action(head, tail, lines.line_index)
if head == 'error':
yield (head, tail)
else:
actions[head], = tail
elif head and not head.startswith('#'):
yield 'error', ('Invalid line %r' % line.rstrip(), lines.line_index)
class UltiSnipsFileSource(SnippetFileSource):
"""Manages all snippets definitions found in rtp for ultisnips."""
def _get_all_snippet_files_for(self, ft):
return find_all_snippet_files(ft)
def _parse_snippet_file(self, filedata, filename):
for event, data in _parse_snippets_file(filedata, filename):
yield event, data