128 lines
4.0 KiB
Python
128 lines
4.0 KiB
Python
#!/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
|