1 import re, os, vim, string, random
2 from collections import deque, namedtuple
4 _Placeholder = namedtuple("_FrozenPlaceholder", ["current_text", "start", "end"])
5 _VisualContent = namedtuple("_VisualContent", ["mode", "text"])
6 _Position = namedtuple("_Position", ["line", "col"])
9 class _SnippetUtilCursor(object):
10 def __init__(self, cursor):
11 self._cursor = [cursor[0] - 1, cursor[1]]
16 self._cursor = [vim.buf.cursor[0], vim.buf.cursor[1]]
21 def set(self, line, column):
22 self.__setitem__(0, line)
23 self.__setitem__(1, column)
25 def to_vim_cursor(self):
26 return (self._cursor[0] + 1, self._cursor[1])
28 def __getitem__(self, index):
29 return self._cursor[index]
31 def __setitem__(self, index, value):
33 self._cursor[index] = value
39 return str((self._cursor[0], self._cursor[1]))
42 class IndentUtil(object):
44 """Utility class for dealing properly with indentation."""
50 """Gets the spacing properties from Vim."""
51 self.shiftwidth = int(
52 vim.eval("exists('*shiftwidth') ? shiftwidth() : &shiftwidth")
54 self._expandtab = vim.eval("&expandtab") == "1"
55 self._tabstop = int(vim.eval("&tabstop"))
57 def ntabs_to_proper_indent(self, ntabs):
58 """Convert 'ntabs' number of tabs to the proper indent prefix."""
59 line_ind = ntabs * self.shiftwidth * " "
60 line_ind = self.indent_to_spaces(line_ind)
61 line_ind = self.spaces_to_indent(line_ind)
64 def indent_to_spaces(self, indent):
65 """Converts indentation to spaces respecting Vim settings."""
66 indent = indent.expandtabs(self._tabstop)
67 right = (len(indent) - len(indent.rstrip(" "))) * " "
68 indent = indent.replace(" ", "")
69 indent = indent.replace("\t", " " * self._tabstop)
72 def spaces_to_indent(self, indent):
73 """Converts spaces to proper indentation respecting Vim settings."""
74 if not self._expandtab:
75 indent = indent.replace(" " * self._tabstop, "\t")
79 class SnippetUtil(object):
81 """Provides easy access to indentation, etc.
83 This is the 'snip' object in python code.
87 def __init__(self, _initial_indent, start, end, context):
88 self._ind = IndentUtil()
89 self._visual = _VisualContent(
90 vim.eval("visualmode()"), vim.eval('get(g:,"coc_selected_text","")')
92 self._initial_indent = _initial_indent
96 self._context = context
98 def _reset(self, cur):
99 """Gets the snippet ready for another update.
101 :cur: the new value for c.
107 self._changed = False
110 def shift(self, amount=1):
111 """Shifts the indentation level. Note that this uses the shiftwidth
112 because thats what code formatters use.
114 :amount: the amount by which to shift.
117 self.indent += " " * self._ind.shiftwidth * amount
119 def unshift(self, amount=1):
120 """Unshift the indentation level. Note that this uses the shiftwidth
121 because thats what code formatters use.
123 :amount: the amount by which to unshift.
126 by = -self._ind.shiftwidth * amount
128 self.indent = self.indent[:by]
132 def mkline(self, line="", indent=""):
133 """Creates a properly set up line.
135 :line: the text to add
136 :indent: the indentation to have at the beginning
137 if None, it uses the default amount
142 def reset_indent(self):
143 """Clears the indentation."""
144 self.indent = self._initial_indent
148 def fn(self): # pylint:disable=no-self-use,invalid-name
150 return vim.eval('expand("%:t")') or ""
153 def basename(self): # pylint:disable=no-self-use
154 """The filename without extension."""
155 return vim.eval('expand("%:t:r")') or ""
158 def ft(self): # pylint:disable=invalid-name
160 return self.opt("&filetype", "")
163 def rv(self): # pylint:disable=invalid-name
166 The text to insert at the location of the placeholder.
172 def rv(self, value): # pylint:disable=invalid-name
178 def _rv_changed(self):
179 """True if rv has changed."""
183 def c(self): # pylint:disable=invalid-name
184 """The current text of the placeholder."""
188 def v(self): # pylint:disable=invalid-name
189 """Content of visual expansions."""
194 if "coc_last_placeholder" in vim.vars:
195 p = vim.vars["coc_last_placeholder"]
196 start = _Position(p["start"]["line"], p["start"]["col"])
197 end = _Position(p["end"]["line"], p["end"]["col"])
198 return _Placeholder(p["current_text"], start, end)
205 def opt(self, option, default=None): # pylint:disable=no-self-use
206 """Gets a Vim variable."""
207 if vim.eval("exists('%s')" % option) == "1":
209 return vim.eval(option)
214 def __add__(self, value):
215 """Appends the given line to rv using mkline."""
216 self.rv += "\n" # pylint:disable=invalid-name
217 self.rv += self.mkline(value)
220 def __lshift__(self, other):
221 """Same as unshift."""
224 def __rshift__(self, other):
229 def snippet_start(self):
231 Returns start of the snippet in format (line, column).
236 def snippet_end(self):
238 Returns end of the snippet in format (line, column).
247 class ContextSnippet(object):
249 self.buffer = vim.current.buffer
250 self.window = vim.current.window
251 self.cursor = _SnippetUtilCursor(vim.current.window.cursor)
252 self.line = vim.call("line", ".") - 1
253 self.column = vim.call("col", ".") - 1
254 line = vim.call("getline", ".")
255 self.after = line[self.column :]
256 if "coc_selected_text" in vim.vars:
257 self.visual_mode = vim.eval("visualmode()")
258 self.visual_text = vim.vars["coc_selected_text"]
260 self.visual_mode = None
261 self.visual_text = ""
262 if "coc_last_placeholder" in vim.vars:
263 p = vim.vars["coc_last_placeholder"]
264 start = _Position(p["start"]["line"], p["start"]["col"])
265 end = _Position(p["end"]["line"], p["end"]["col"])
266 self.last_placeholder = _Placeholder(p["current_text"], start, end)
268 self.last_placeholder = None