Fixed vim and zsh
This commit is contained in:
@ -0,0 +1 @@
|
||||
"""Snippet sources that are file based."""
|
@ -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)
|
@ -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)
|
@ -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
|
@ -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
|
Reference in New Issue
Block a user