class SnippetUtil(object): """ Provides easy access to indentation, etc. """ def __init__(self, initial_indent, cur=""): self._ind = IndentUtil() self._initial_indent = self._ind.indent_to_spaces(initial_indent) self._reset(cur) def _reset(self, cur): """ Gets the snippet ready for another update. :cur: the new value for c. """ self._ind.reset() self._c = cur self._rv = "" self._changed = False self.reset_indent() def shift(self, amount=1): """ Shifts the indentation level. Note that this uses the shiftwidth because thats what code formatters use. :amount: the amount by which to shift. """ self.indent += " " * self._ind.sw * amount def unshift(self, amount=1): """ Unshift the indentation level. Note that this uses the shiftwidth because thats what code formatters use. :amount: the amount by which to unshift. """ by = -self._ind.sw * amount try: self.indent = self.indent[:by] except IndexError: indent = "" def mkline(self, line="", indent=None): """ Creates a properly set up line. :line: the text to add :indent: the indentation to have at the beginning if None, it uses the default amount """ if indent == None: indent = self.indent # this deals with the fact that the first line is # already properly indented if '\n' not in self._rv: try: indent = indent[len(self._initial_indent):] except IndexError: indent = "" indent = self._ind.spaces_to_indent(indent) return indent + line def reset_indent(self): """ Clears the indentation. """ self.indent = self._initial_indent # Utility methods @property def fn(self): """ The filename. """ return vim.eval('expand("%:t")') or "" @property def basename(self): """ The filename without extension. """ return vim.eval('expand("%:t:r")') or "" @property def ft(self): """ The filetype. """ return self.opt("&filetype", "") # Necessary stuff def rv(): """ The return value. This is a list of lines to insert at the location of the placeholder. Deprecates res. """ def fget(self): return self._rv def fset(self, value): self._changed = True self._rv = value return locals() rv = property(**rv()) @property def _rv_changed(self): """ True if rv has changed. """ return self._changed @property def c(self): """ The current text of the placeholder. Deprecates cur. """ return self._c def opt(self, option, default=None): """ Gets a vim variable. """ if vim.eval("exists('%s')" % option) == "1": try: return vim.eval(option) except vim.error: pass return default # Syntatic sugar def __add__(self, value): """ Appends the given line to rv using mkline. """ self.rv += '\n' # handles the first line properly self.rv += self.mkline(value) return self def __lshift__(self, other): """ Same as unshift. """ self.unshift(other) def __rshift__(self, other): """ Same as shift. """ self.shift(other)
class Snippet(object): _INDENT = re.compile(r"^[ \t]*") _TABS = re.compile(r"^\t*") def __init__(self, trigger, value, descr, options, globals): self._t = as_unicode(trigger) self._v = as_unicode(value) self._d = as_unicode(descr) self._opts = options self._matched = "" self._last_re = None self._globals = globals self._util = IndentUtil() def __repr__(self): return "Snippet(%s,%s,%s)" % (self._t,self._d,self._opts) def _words_for_line(self, before, num_words=None): """ Gets the final num_words words from before. If num_words is None, then use the number of words in the trigger. """ words = '' if not len(before): return '' if num_words is None: num_words = len(self._t.split()) word_list = before.split() if len(word_list) <= num_words: return before.strip() else: before_words = before for i in range(-1, -(num_words + 1), -1): left = before_words.rfind(word_list[i]) before_words = before_words[:left] return before[len(before_words):].strip() def _re_match(self, trigger): """ Test if a the current regex trigger matches `trigger`. If so, set _last_re and _matched. """ for match in re.finditer(self._t, trigger): if match.end() != len(trigger): continue else: self._matched = trigger[match.start():match.end()] self._last_re = match return match return False def matches(self, trigger): # If user supplies both "w" and "i", it should perhaps be an # error, but if permitted it seems that "w" should take precedence # (since matching at word boundary and within a word == matching at word # boundary). self._matched = "" # Don't expand on whitespace if trigger and trigger.rstrip() is not trigger: return False words = self._words_for_line(trigger) if "r" in self._opts: match = self._re_match(trigger) elif "w" in self._opts: words_len = len(self._t) words_prefix = words[:-words_len] words_suffix = words[-words_len:] match = (words_suffix == self._t) if match and words_prefix: # Require a word boundary between prefix and suffix. boundaryChars = words_prefix[-1:] + words_suffix[:1] match = re.match(r'.\b.', boundaryChars) elif "i" in self._opts: match = words.endswith(self._t) else: match = (words == self._t) # By default, we match the whole trigger if match and not self._matched: self._matched = self._t # Ensure the match was on a word boundry if needed if "b" in self._opts and match: text_before = trigger.rstrip()[:-len(self._matched)] if text_before.strip(" \t") != '': self._matched = "" return False return match def could_match(self, trigger): self._matched = "" # Don't expand on whitespace if trigger and trigger.rstrip() is not trigger: return False words = self._words_for_line(trigger) if "r" in self._opts: # Test for full match only match = self._re_match(trigger) elif "w" in self._opts: # Trim non-empty prefix up to word boundary, if present. words_suffix = re.sub(r'^.+\b(.+)$', r'\1', words) match = self._t.startswith(words_suffix) self._matched = words_suffix # TODO: list_snippets() function cannot handle partial-trigger # matches yet, so for now fail if we trimmed the prefix. if words_suffix != words: match = False elif "i" in self._opts: # TODO: It is hard to define when a inword snippet could match, # therefore we check only for full-word trigger. match = self._t.startswith(words) else: match = self._t.startswith(words) # By default, we match the words from the trigger if match and not self._matched: self._matched = words # Ensure the match was on a word boundry if needed if "b" in self._opts and match: text_before = trigger.rstrip()[:-len(self._matched)] if text_before.strip(" \t") != '': self._matched = "" return False return match def keep_formatoptions_unchanged(self): return "f" in self._opts keep_formatoptions_unchanged = property(keep_formatoptions_unchanged) def overwrites_previous(self): return "!" in self._opts overwrites_previous = property(overwrites_previous) def description(self): return ("(%s) %s" % (self._t, self._d)).strip() description = property(description) def trigger(self): return self._t trigger = property(trigger) def matched(self): """ The last text that was matched. """ return self._matched matched = property(matched) def launch(self, text_before, parent, start, end = None): indent = self._INDENT.match(text_before).group(0) lines = (self._v + "\n").splitlines() self._util.reset() v = [] for line_num, line in enumerate(lines): if "t" in self._opts: tabs = 0 else: tabs = len(self._TABS.match(line).group(0)) line_ind = tabs * self._util.sw * " " line_ind = self._util.indent_to_spaces(line_ind) line_ind = self._util.spaces_to_indent(line_ind) if line_num != 0: line_ind = indent + line_ind v.append(line_ind + line[tabs:]) v = os.linesep.join(v) if parent is None: return SnippetInstance(StartMarker(start), indent, v, last_re = self._last_re, globals = self._globals) else: return SnippetInstance(parent, indent, v, start, end, last_re = self._last_re, globals = self._globals)
class Snippet(object): _INDENT = re.compile(r"^[ \t]*") _TABS = re.compile(r"^\t*") def __init__(self, trigger, value, descr, options, globals): self._t = as_unicode(trigger) self._v = as_unicode(value) self._d = as_unicode(descr) self._opts = options self._matched = "" self._last_re = None self._globals = globals self._util = IndentUtil() def __repr__(self): return "Snippet(%s,%s,%s)" % (self._t, self._d, self._opts) def _words_for_line(self, before, num_words=None): """ Gets the final num_words words from before. If num_words is None, then use the number of words in the trigger. """ words = '' if not len(before): return '' if num_words is None: num_words = len(self._t.split()) word_list = before.split() if len(word_list) <= num_words: return before.strip() else: before_words = before for i in range(-1, -(num_words + 1), -1): left = before_words.rfind(word_list[i]) before_words = before_words[:left] return before[len(before_words):].strip() def _re_match(self, trigger): """ Test if a the current regex trigger matches `trigger`. If so, set _last_re and _matched. """ for match in re.finditer(self._t, trigger): if match.end() != len(trigger): continue else: self._matched = trigger[match.start():match.end()] self._last_re = match return match return False def matches(self, trigger): # If user supplies both "w" and "i", it should perhaps be an # error, but if permitted it seems that "w" should take precedence # (since matching at word boundary and within a word == matching at word # boundary). self._matched = "" # Don't expand on whitespace if trigger and trigger.rstrip() is not trigger: return False words = self._words_for_line(trigger) if "r" in self._opts: match = self._re_match(trigger) elif "w" in self._opts: words_len = len(self._t) words_prefix = words[:-words_len] words_suffix = words[-words_len:] match = (words_suffix == self._t) if match and words_prefix: # Require a word boundary between prefix and suffix. boundaryChars = words_prefix[-1:] + words_suffix[:1] match = re.match(r'.\b.', boundaryChars) elif "i" in self._opts: match = words.endswith(self._t) else: match = (words == self._t) # By default, we match the whole trigger if match and not self._matched: self._matched = self._t # Ensure the match was on a word boundry if needed if "b" in self._opts and match: text_before = trigger.rstrip()[:-len(self._matched)] if text_before.strip(" \t") != '': self._matched = "" return False return match def could_match(self, trigger): self._matched = "" # Don't expand on whitespace if trigger and trigger.rstrip() is not trigger: return False words = self._words_for_line(trigger) if "r" in self._opts: # Test for full match only match = self._re_match(trigger) elif "w" in self._opts: # Trim non-empty prefix up to word boundary, if present. words_suffix = re.sub(r'^.+\b(.+)$', r'\1', words) match = self._t.startswith(words_suffix) self._matched = words_suffix # TODO: list_snippets() function cannot handle partial-trigger # matches yet, so for now fail if we trimmed the prefix. if words_suffix != words: match = False elif "i" in self._opts: # TODO: It is hard to define when a inword snippet could match, # therefore we check only for full-word trigger. match = self._t.startswith(words) else: match = self._t.startswith(words) # By default, we match the words from the trigger if match and not self._matched: self._matched = words # Ensure the match was on a word boundry if needed if "b" in self._opts and match: text_before = trigger.rstrip()[:-len(self._matched)] if text_before.strip(" \t") != '': self._matched = "" return False return match def keep_formatoptions_unchanged(self): return "f" in self._opts keep_formatoptions_unchanged = property(keep_formatoptions_unchanged) def overwrites_previous(self): return "!" in self._opts overwrites_previous = property(overwrites_previous) def description(self): return ("(%s) %s" % (self._t, self._d)).strip() description = property(description) def trigger(self): return self._t trigger = property(trigger) def matched(self): """ The last text that was matched. """ return self._matched matched = property(matched) def launch(self, text_before, parent, start, end=None): indent = self._INDENT.match(text_before).group(0) lines = (self._v + "\n").splitlines() self._util.reset() v = [] for line_num, line in enumerate(lines): if "t" in self._opts: tabs = 0 else: tabs = len(self._TABS.match(line).group(0)) line_ind = tabs * self._util.sw * " " line_ind = self._util.indent_to_spaces(line_ind) line_ind = self._util.spaces_to_indent(line_ind) if line_num != 0: line_ind = indent + line_ind v.append(line_ind + line[tabs:]) v = os.linesep.join(v) if parent is None: return SnippetInstance(StartMarker(start), indent, v, last_re=self._last_re, globals=self._globals) else: return SnippetInstance(parent, indent, v, start, end, last_re=self._last_re, globals=self._globals)