#!/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