--- /dev/null
+import re, os, vim, string, random
+from collections import deque, namedtuple
+
+_Placeholder = namedtuple("_FrozenPlaceholder", ["current_text", "start", "end"])
+_VisualContent = namedtuple("_VisualContent", ["mode", "text"])
+_Position = namedtuple("_Position", ["line", "col"])
+
+
+class _SnippetUtilCursor(object):
+ def __init__(self, cursor):
+ self._cursor = [cursor[0] - 1, cursor[1]]
+ self._set = False
+
+ def preserve(self):
+ self._set = True
+ self._cursor = [vim.buf.cursor[0], vim.buf.cursor[1]]
+
+ def is_set(self):
+ return self._set
+
+ def set(self, line, column):
+ self.__setitem__(0, line)
+ self.__setitem__(1, column)
+
+ def to_vim_cursor(self):
+ return (self._cursor[0] + 1, self._cursor[1])
+
+ def __getitem__(self, index):
+ return self._cursor[index]
+
+ def __setitem__(self, index, value):
+ self._set = True
+ self._cursor[index] = value
+
+ def __len__(self):
+ return 2
+
+ def __str__(self):
+ return str((self._cursor[0], self._cursor[1]))
+
+
+class IndentUtil(object):
+
+ """Utility class for dealing properly with indentation."""
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ """Gets the spacing properties from Vim."""
+ self.shiftwidth = int(
+ vim.eval("exists('*shiftwidth') ? shiftwidth() : &shiftwidth")
+ )
+ self._expandtab = vim.eval("&expandtab") == "1"
+ self._tabstop = int(vim.eval("&tabstop"))
+
+ def ntabs_to_proper_indent(self, ntabs):
+ """Convert 'ntabs' number of tabs to the proper indent prefix."""
+ line_ind = ntabs * self.shiftwidth * " "
+ line_ind = self.indent_to_spaces(line_ind)
+ line_ind = self.spaces_to_indent(line_ind)
+ return line_ind
+
+ def indent_to_spaces(self, indent):
+ """Converts indentation to spaces respecting Vim settings."""
+ indent = indent.expandtabs(self._tabstop)
+ right = (len(indent) - len(indent.rstrip(" "))) * " "
+ indent = indent.replace(" ", "")
+ indent = indent.replace("\t", " " * self._tabstop)
+ return indent + right
+
+ def spaces_to_indent(self, indent):
+ """Converts spaces to proper indentation respecting Vim settings."""
+ if not self._expandtab:
+ indent = indent.replace(" " * self._tabstop, "\t")
+ return indent
+
+
+class SnippetUtil(object):
+
+ """Provides easy access to indentation, etc.
+
+ This is the 'snip' object in python code.
+
+ """
+
+ def __init__(self, _initial_indent, start, end, context):
+ self._ind = IndentUtil()
+ self._visual = _VisualContent(
+ vim.eval("visualmode()"), vim.eval('get(g:,"coc_selected_text","")')
+ )
+ self._initial_indent = _initial_indent
+ self._reset("")
+ self._start = start
+ self._end = end
+ self._context = context
+
+ def _reset(self, cur):
+ """Gets the snippet ready for another update.
+
+ :cur: the new value for c.
+
+ """
+ self._ind.reset()
+ self._cur = 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.shiftwidth * 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.shiftwidth * amount
+ try:
+ self.indent = self.indent[:by]
+ except IndexError:
+ self.indent = ""
+
+ def mkline(self, line="", indent=""):
+ """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
+
+ """
+ return indent + line
+
+ def reset_indent(self):
+ """Clears the indentation."""
+ self.indent = self._initial_indent
+
+ # Utility methods
+ @property
+ def fn(self): # pylint:disable=no-self-use,invalid-name
+ """The filename."""
+ return vim.eval('expand("%:t")') or ""
+
+ @property
+ def basename(self): # pylint:disable=no-self-use
+ """The filename without extension."""
+ return vim.eval('expand("%:t:r")') or ""
+
+ @property
+ def ft(self): # pylint:disable=invalid-name
+ """The filetype."""
+ return self.opt("&filetype", "")
+
+ @property
+ def rv(self): # pylint:disable=invalid-name
+ """The return value.
+
+ The text to insert at the location of the placeholder.
+
+ """
+ return self._rv
+
+ @rv.setter
+ def rv(self, value): # pylint:disable=invalid-name
+ """See getter."""
+ self._changed = True
+ self._rv = value
+
+ @property
+ def _rv_changed(self):
+ """True if rv has changed."""
+ return self._changed
+
+ @property
+ def c(self): # pylint:disable=invalid-name
+ """The current text of the placeholder."""
+ return ""
+
+ @property
+ def v(self): # pylint:disable=invalid-name
+ """Content of visual expansions."""
+ return self._visual
+
+ @property
+ def p(self):
+ if "coc_last_placeholder" in vim.vars:
+ p = vim.vars["coc_last_placeholder"]
+ start = _Position(p["start"]["line"], p["start"]["col"])
+ end = _Position(p["end"]["line"], p["end"]["col"])
+ return _Placeholder(p["current_text"], start, end)
+ return None
+
+ @property
+ def context(self):
+ return self._context
+
+ def opt(self, option, default=None): # pylint:disable=no-self-use
+ """Gets a Vim variable."""
+ if vim.eval("exists('%s')" % option) == "1":
+ try:
+ return vim.eval(option)
+ except vim.error:
+ pass
+ return default
+
+ def __add__(self, value):
+ """Appends the given line to rv using mkline."""
+ self.rv += "\n" # pylint:disable=invalid-name
+ 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)
+
+ @property
+ def snippet_start(self):
+ """
+ Returns start of the snippet in format (line, column).
+ """
+ return self._start
+
+ @property
+ def snippet_end(self):
+ """
+ Returns end of the snippet in format (line, column).
+ """
+ return self._end
+
+ @property
+ def buffer(self):
+ return vim.buf
+
+
+class ContextSnippet(object):
+ def __init__(self):
+ self.buffer = vim.current.buffer
+ self.window = vim.current.window
+ self.cursor = _SnippetUtilCursor(vim.current.window.cursor)
+ self.line = vim.call("line", ".") - 1
+ self.column = vim.call("col", ".") - 1
+ line = vim.call("getline", ".")
+ self.after = line[self.column :]
+ if "coc_selected_text" in vim.vars:
+ self.visual_mode = vim.eval("visualmode()")
+ self.visual_text = vim.vars["coc_selected_text"]
+ else:
+ self.visual_mode = None
+ self.visual_text = ""
+ if "coc_last_placeholder" in vim.vars:
+ p = vim.vars["coc_last_placeholder"]
+ start = _Position(p["start"]["line"], p["start"]["col"])
+ end = _Position(p["end"]["line"], p["end"]["col"])
+ self.last_placeholder = _Placeholder(p["current_text"], start, end)
+ else:
+ self.last_placeholder = None