More linting and refactorings mainly in _transformation.py.
This commit is contained in:
parent
63021206cd
commit
c67a59f579
2
pylintrc
2
pylintrc
@ -55,7 +55,7 @@ disable=
|
||||
output-format=text
|
||||
|
||||
# Tells whether to display a full report or only the messages
|
||||
reports=no
|
||||
reports=yes
|
||||
|
||||
|
||||
[BASIC]
|
||||
|
@ -12,7 +12,7 @@ import traceback
|
||||
|
||||
from UltiSnips._diff import diff, guess_edit
|
||||
from UltiSnips.compatibility import as_unicode
|
||||
from UltiSnips.geometry import Position
|
||||
from UltiSnips.position import Position
|
||||
from UltiSnips.snippet import Snippet
|
||||
from UltiSnips.snippet_dictionary import SnippetDictionary
|
||||
from UltiSnips.snippets_file_parser import SnippetsFileParser
|
||||
@ -155,7 +155,6 @@ class SnippetManager(object):
|
||||
self.backward_trigger = backward_trigger
|
||||
self._supertab_keys = None
|
||||
self._csnippets = []
|
||||
|
||||
self.reset()
|
||||
|
||||
@err_to_scratch_buffer
|
||||
@ -163,7 +162,7 @@ class SnippetManager(object):
|
||||
"""Reset the class to the state it had directly after creation."""
|
||||
self._vstate = VimState()
|
||||
self._test_error = test_error
|
||||
self._snippets = {}
|
||||
self._snippets = defaultdict(lambda: SnippetDictionary())
|
||||
self._filetypes = defaultdict(lambda: ['all'])
|
||||
self._visual_content = VisualContentPreserver()
|
||||
|
||||
@ -277,27 +276,14 @@ class SnippetManager(object):
|
||||
"""
|
||||
self._visual_content.conserve()
|
||||
|
||||
# TODO(sirver): replace through defaultdict
|
||||
def snippet_dict(self, ft):
|
||||
"""Makes sure that ft is in self._snippets."""
|
||||
if ft not in self._snippets:
|
||||
self._snippets[ft] = SnippetDictionary()
|
||||
return self._snippets[ft]
|
||||
|
||||
@err_to_scratch_buffer
|
||||
def add_snippet(self, trigger, value, description,
|
||||
options, ft="all", globals=None, fn=None):
|
||||
"""Add a snippet to the list of known snippets of the given 'ft'."""
|
||||
self.snippet_dict(ft).add_snippet(
|
||||
self._snippets[ft].add_snippet(
|
||||
Snippet(trigger, value, description, options, globals or {}), fn
|
||||
)
|
||||
|
||||
@err_to_scratch_buffer
|
||||
def add_snippet_file(self, ft, path):
|
||||
"""Add a file to be watched for changes to the 'ft' snippet dict."""
|
||||
sd = self.snippet_dict(ft)
|
||||
sd.addfile(path)
|
||||
|
||||
@err_to_scratch_buffer
|
||||
def expand_anon(self, value, trigger="", description="",
|
||||
options="", globals=None):
|
||||
@ -326,7 +312,7 @@ class SnippetManager(object):
|
||||
@err_to_scratch_buffer
|
||||
def add_extending_info(self, ft, parents):
|
||||
"""Add the list of 'parents' as being extended by the 'ft'."""
|
||||
sd = self.snippet_dict(ft)
|
||||
sd = self._snippets[ft]
|
||||
for parent in parents:
|
||||
if parent in sd.extends:
|
||||
continue
|
||||
@ -616,8 +602,9 @@ class SnippetManager(object):
|
||||
return self._csnippets[-1]
|
||||
|
||||
def _parse_snippets(self, ft, filename, file_data=None):
|
||||
"""Parse the file 'filename' for the given 'ft'."""
|
||||
self.add_snippet_file(ft, filename)
|
||||
"""Parse the file 'filename' for the given 'ft' and watch it for
|
||||
changes in the future."""
|
||||
self._snippets[ft].addfile(filename)
|
||||
SnippetsFileParser(ft, filename, self, file_data).parse()
|
||||
|
||||
@property
|
||||
@ -667,7 +654,7 @@ class SnippetManager(object):
|
||||
|
||||
def _load_snippets_for(self, ft):
|
||||
"""Load all snippets for the given 'ft'."""
|
||||
self.snippet_dict(ft).reset()
|
||||
del self._snippets[ft]
|
||||
for fn in _base_snippet_files_for(ft):
|
||||
self._parse_snippets(ft, fn)
|
||||
# Now load for the parents
|
||||
@ -683,11 +670,11 @@ class SnippetManager(object):
|
||||
|
||||
if ft not in self._snippets:
|
||||
return True
|
||||
elif do_hash and self.snippet_dict(ft).needs_update():
|
||||
elif do_hash and self._snippets[ft].has_any_file_changed():
|
||||
return True
|
||||
elif do_hash:
|
||||
cur_snips = set(_base_snippet_files_for(ft))
|
||||
old_snips = set(self.snippet_dict(ft).files)
|
||||
old_snips = set(self._snippets[ft].files)
|
||||
if cur_snips - old_snips:
|
||||
return True
|
||||
return False
|
||||
@ -705,7 +692,7 @@ class SnippetManager(object):
|
||||
if self._needs_update(ft):
|
||||
self._load_snippets_for(ft)
|
||||
|
||||
for parent in self.snippet_dict(ft).extends:
|
||||
for parent in self._snippets[ft].extends:
|
||||
self._ensure_loaded(parent, checked)
|
||||
|
||||
def _ensure_all_loaded(self):
|
||||
@ -746,12 +733,12 @@ class SnippetManager(object):
|
||||
if not snips:
|
||||
return []
|
||||
if not seen:
|
||||
seen = []
|
||||
seen.append(ft)
|
||||
seen = set()
|
||||
seen.add(ft)
|
||||
parent_results = []
|
||||
for parent_ft in snips.extends:
|
||||
if parent_ft not in seen:
|
||||
seen.append(parent_ft)
|
||||
seen.add(parent_ft)
|
||||
parent_results += self._find_snippets(parent_ft, trigger,
|
||||
potentially, seen)
|
||||
return parent_results + snips.get_matching_snippets(
|
||||
|
@ -5,7 +5,7 @@ from collections import defaultdict
|
||||
import sys
|
||||
|
||||
from UltiSnips import _vim
|
||||
from UltiSnips.geometry import Position
|
||||
from UltiSnips.position import Position
|
||||
|
||||
def is_complete_edit(initial_line, a, b, cmds):
|
||||
buf = a[:]
|
||||
@ -171,4 +171,3 @@ def diff(a, b, sline = 0):
|
||||
(("D",line, col, a[x]),) )
|
||||
)
|
||||
cost += 1
|
||||
|
||||
|
@ -8,9 +8,9 @@ import re
|
||||
import vim # pylint:disable=import-error
|
||||
from vim import error # pylint:disable=import-error,unused-import
|
||||
|
||||
from UltiSnips.geometry import Position
|
||||
from UltiSnips.compatibility import col2byte, byte2col, \
|
||||
as_unicode, as_vimencoding
|
||||
from UltiSnips.position import Position
|
||||
|
||||
class VimBuffer(object):
|
||||
"""Wrapper around the current Vim buffer."""
|
||||
|
@ -1,36 +1,43 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
"""Convenience methods that help with debugging. They should never be used in
|
||||
production code."""
|
||||
|
||||
import sys
|
||||
|
||||
from UltiSnips.compatibility import as_unicode
|
||||
|
||||
dump_filename = "/tmp/file.txt" if not sys.platform.lower().startswith("win") \
|
||||
DUMP_FILENAME = "/tmp/file.txt" if not sys.platform.lower().startswith("win") \
|
||||
else "C:/windows/temp/ultisnips.txt"
|
||||
with open(dump_filename, "w") as dump_file:
|
||||
with open(DUMP_FILENAME, "w"):
|
||||
pass # clears the file
|
||||
|
||||
def echo_to_hierarchy(to):
|
||||
par = to
|
||||
while par._parent: par = par._parent
|
||||
|
||||
def _do_print(to, indent=""):
|
||||
debug(indent + as_unicode(to))
|
||||
def echo_to_hierarchy(text_object):
|
||||
"""Outputs the given 'text_object' and its childs hierarchically."""
|
||||
# pylint:disable=protected-access
|
||||
parent = text_object
|
||||
while parent._parent:
|
||||
parent = parent._parent
|
||||
|
||||
def _do_print(text_object, indent=""):
|
||||
"""prints recursively."""
|
||||
debug(indent + as_unicode(text_object))
|
||||
try:
|
||||
for c in to._childs:
|
||||
_do_print(c, indent=indent + " ")
|
||||
for child in text_object._childs:
|
||||
_do_print(child, indent=indent + " ")
|
||||
except AttributeError:
|
||||
pass
|
||||
_do_print(parent)
|
||||
|
||||
_do_print(par)
|
||||
|
||||
def debug(s):
|
||||
s = as_unicode(s)
|
||||
with open(dump_filename, "ab") as dump_file:
|
||||
dump_file.write((s + '\n').encode("utf-8"))
|
||||
def debug(msg):
|
||||
"""Dumb 'msg' into the debug file."""
|
||||
msg = as_unicode(msg)
|
||||
with open(DUMP_FILENAME, "ab") as dump_file:
|
||||
dump_file.write((msg + '\n').encode("utf-8"))
|
||||
|
||||
def print_stack():
|
||||
"""Dump a stack trace into the debug file."""
|
||||
import traceback
|
||||
with open(dump_filename, "ab") as dump_file:
|
||||
with open(DUMP_FILENAME, "ab") as dump_file:
|
||||
traceback.print_stack(file=dump_file)
|
||||
|
@ -1,79 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
class Position(object):
|
||||
def __init__(self, line, col):
|
||||
self.line = line
|
||||
self.col = col
|
||||
|
||||
def col():
|
||||
def fget(self):
|
||||
return self._col
|
||||
def fset(self, value):
|
||||
self._col = value
|
||||
return locals()
|
||||
col = property(**col())
|
||||
|
||||
def line():
|
||||
doc = "Zero base line numbers"
|
||||
def fget(self):
|
||||
return self._line
|
||||
def fset(self, value):
|
||||
self._line = value
|
||||
return locals()
|
||||
line = property(**line())
|
||||
|
||||
def move(self, pivot, diff):
|
||||
"""pivot is the position of the first changed
|
||||
character, diff is how text after it moved"""
|
||||
if self < pivot: return
|
||||
if diff.line == 0:
|
||||
if self.line == pivot.line:
|
||||
self.col += diff.col
|
||||
elif diff.line > 0:
|
||||
if self.line == pivot.line:
|
||||
self.col += diff.col - pivot.col
|
||||
self.line += diff.line
|
||||
else:
|
||||
self.line += diff.line
|
||||
if self.line == pivot.line:
|
||||
self.col += - diff.col + pivot.col
|
||||
|
||||
|
||||
def __add__(self,pos):
|
||||
if not isinstance(pos,Position):
|
||||
raise TypeError("unsupported operand type(s) for +: " \
|
||||
"'Position' and %s" % type(pos))
|
||||
|
||||
return Position(self.line + pos.line, self.col + pos.col)
|
||||
|
||||
def __sub__(self,pos):
|
||||
if not isinstance(pos,Position):
|
||||
raise TypeError("unsupported operand type(s) for +: " \
|
||||
"'Position' and %s" % type(pos))
|
||||
return Position(self.line - pos.line, self.col - pos.col)
|
||||
|
||||
def diff(self,pos):
|
||||
if not isinstance(pos,Position):
|
||||
raise TypeError("unsupported operand type(s) for +: " \
|
||||
"'Position' and %s" % type(pos))
|
||||
if self.line == pos.line:
|
||||
return Position(0, self.col - pos.col)
|
||||
else:
|
||||
if self > pos:
|
||||
return Position(self.line - pos.line, self.col)
|
||||
else:
|
||||
return Position(self.line - pos.line, pos.col)
|
||||
return Position(self.line - pos.line, self.col - pos.col)
|
||||
|
||||
def __eq__(self, other):
|
||||
return (self._line, self._col) == (other._line, other._col)
|
||||
def __ne__(self, other):
|
||||
return (self._line, self._col) != (other._line, other._col)
|
||||
def __lt__(self, other):
|
||||
return (self._line, self._col) < (other._line, other._col)
|
||||
def __le__(self, other):
|
||||
return (self._line, self._col) <= (other._line, other._col)
|
||||
|
||||
def __repr__(self):
|
||||
return "(%i,%i)" % (self._line, self._col)
|
65
pythonx/UltiSnips/position.py
Normal file
65
pythonx/UltiSnips/position.py
Normal file
@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
"""Represents a Position in a text file: (0 based line index, 0 based column
|
||||
index) and provides methods for moving them around."""
|
||||
|
||||
class Position(object):
|
||||
"""See module docstring."""
|
||||
|
||||
def __init__(self, line, col):
|
||||
self.line = line
|
||||
self.col = col
|
||||
|
||||
def move(self, pivot, delta):
|
||||
"""'pivot' is the position of the first changed character, 'delta' is
|
||||
how text after it moved"""
|
||||
if self < pivot:
|
||||
return
|
||||
if delta.line == 0:
|
||||
if self.line == pivot.line:
|
||||
self.col += delta.col
|
||||
elif delta.line > 0:
|
||||
if self.line == pivot.line:
|
||||
self.col += delta.col - pivot.col
|
||||
self.line += delta.line
|
||||
else:
|
||||
self.line += delta.line
|
||||
if self.line == pivot.line:
|
||||
self.col += - delta.col + pivot.col
|
||||
|
||||
def delta(self, pos):
|
||||
"""Returns the difference that the cursor must move to come from 'pos'
|
||||
to us."""
|
||||
assert isinstance(pos, Position)
|
||||
if self.line == pos.line:
|
||||
return Position(0, self.col - pos.col)
|
||||
else:
|
||||
if self > pos:
|
||||
return Position(self.line - pos.line, self.col)
|
||||
else:
|
||||
return Position(self.line - pos.line, pos.col)
|
||||
return Position(self.line - pos.line, self.col - pos.col)
|
||||
|
||||
def __add__(self, pos):
|
||||
assert isinstance(pos, Position)
|
||||
return Position(self.line + pos.line, self.col + pos.col)
|
||||
|
||||
def __sub__(self, pos):
|
||||
assert isinstance(pos, Position)
|
||||
return Position(self.line - pos.line, self.col - pos.col)
|
||||
|
||||
def __eq__(self, other):
|
||||
return (self.line, self.col) == (other.line, other.col)
|
||||
|
||||
def __ne__(self, other):
|
||||
return (self.line, self.col) != (other.line, other.col)
|
||||
|
||||
def __lt__(self, other):
|
||||
return (self.line, self.col) < (other.line, other.col)
|
||||
|
||||
def __le__(self, other):
|
||||
return (self.line, self.col) <= (other.line, other.col)
|
||||
|
||||
def __repr__(self):
|
||||
return "(%i,%i)" % (self.line, self.col)
|
@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
"""Implements a container for parsed snippets."""
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
@ -13,67 +15,71 @@ def _hash_file(path):
|
||||
|
||||
|
||||
class SnippetDictionary(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._added = []
|
||||
self.reset()
|
||||
"""See module docstring."""
|
||||
|
||||
def reset(self):
|
||||
self._snippets = []
|
||||
def __init__(self):
|
||||
self._added = []
|
||||
self._extends = []
|
||||
self._files = {}
|
||||
self._snippets = []
|
||||
|
||||
def add_snippet(self, s, fn=None):
|
||||
if fn:
|
||||
self._snippets.append(s)
|
||||
|
||||
if fn not in self.files:
|
||||
self.addfile(fn)
|
||||
def add_snippet(self, snippet, filename):
|
||||
"""Add 'snippet' to this dictionary. If 'filename' is given, also watch
|
||||
the original file for changes."""
|
||||
if filename:
|
||||
self._snippets.append(snippet)
|
||||
if filename not in self.files:
|
||||
self.addfile(filename)
|
||||
else:
|
||||
self._added.append(s)
|
||||
self._added.append(snippet)
|
||||
|
||||
def get_matching_snippets(self, trigger, potentially):
|
||||
"""Returns all snippets matching the given trigger."""
|
||||
"""Returns all snippets matching the given trigger. If 'potentially' is
|
||||
true, returns all that could_match()."""
|
||||
if not potentially:
|
||||
return [ s for s in self.snippets if s.matches(trigger) ]
|
||||
return [s for s in self.snippets if s.matches(trigger)]
|
||||
else:
|
||||
return [ s for s in self.snippets if s.could_match(trigger) ]
|
||||
return [s for s in self.snippets if s.could_match(trigger)]
|
||||
|
||||
@property
|
||||
def snippets(self):
|
||||
"""Returns all snippets in this dictionary."""
|
||||
return self._added + self._snippets
|
||||
|
||||
def clear_snippets(self, triggers=[]):
|
||||
"""Remove all snippets that match each trigger in triggers.
|
||||
When triggers is empty, removes all snippets.
|
||||
"""
|
||||
def clear_snippets(self, triggers=None):
|
||||
"""Remove all snippets that match each trigger in 'triggers'. When
|
||||
'triggers' is None, empties this dictionary completely."""
|
||||
if triggers is None:
|
||||
triggers = []
|
||||
if triggers:
|
||||
for t in triggers:
|
||||
for s in self.get_matching_snippets(t, potentially=False):
|
||||
if s in self._snippets:
|
||||
self._snippets.remove(s)
|
||||
if s in self._added:
|
||||
self._added.remove(s)
|
||||
for trigger in triggers:
|
||||
for snippet in self.get_matching_snippets(trigger, False):
|
||||
if snippet in self._snippets:
|
||||
self._snippets.remove(snippet)
|
||||
if snippet in self._added:
|
||||
self._added.remove(snippet)
|
||||
else:
|
||||
self._snippets = []
|
||||
self._added = []
|
||||
|
||||
@property
|
||||
def files(self):
|
||||
return self._files
|
||||
|
||||
def addfile(self, path):
|
||||
"""Add this file to the files we read triggers from."""
|
||||
self.files[path] = _hash_file(path)
|
||||
|
||||
def needs_update(self):
|
||||
def has_any_file_changed(self):
|
||||
"""Returns True if any of our watched files has changed since we read
|
||||
it last."""
|
||||
for path, hash in self.files.items():
|
||||
if not hash or hash != _hash_file(path):
|
||||
return True
|
||||
return False
|
||||
|
||||
def extends():
|
||||
def fget(self):
|
||||
return self._extends
|
||||
def fset(self, value):
|
||||
self._extends = value
|
||||
return locals()
|
||||
extends = property(**extends())
|
||||
@property
|
||||
def files(self):
|
||||
"""All files we have read snippets from."""
|
||||
return self._files
|
||||
|
||||
@property
|
||||
def extends(self):
|
||||
"""The list of filetypes this filetype extends."""
|
||||
return self._extends
|
||||
|
@ -2,15 +2,14 @@
|
||||
# encoding: utf-8
|
||||
|
||||
import unittest
|
||||
import os.path as p, sys; sys.path.append(p.join(p.dirname(__file__), ".."))
|
||||
|
||||
from geometry import Position
|
||||
from position import Position
|
||||
|
||||
class _MPBase(object):
|
||||
def runTest(self):
|
||||
obj = Position(*self.obj)
|
||||
for pivot, diff, wanted in self.steps:
|
||||
obj.move(Position(*pivot), Position(*diff))
|
||||
for pivot, delta, wanted in self.steps:
|
||||
obj.move(Position(*pivot), Position(*delta))
|
||||
self.assertEqual(Position(*wanted), obj)
|
||||
|
||||
class MovePosition_DelSameLine(_MPBase, unittest.TestCase):
|
@ -6,7 +6,7 @@ import unittest
|
||||
import os.path as p, sys; sys.path.append(p.join(p.dirname(__file__), ".."))
|
||||
|
||||
from _diff import diff, guess_edit
|
||||
from geometry import Position
|
||||
from position import Position
|
||||
|
||||
|
||||
def transform(a, cmds):
|
||||
@ -184,7 +184,3 @@ if __name__ == '__main__':
|
||||
unittest.main()
|
||||
# k = TestEditScript()
|
||||
# unittest.TextTestRunner().run(k)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
"""Base classes for all text objects."""
|
||||
|
||||
import UltiSnips._vim as _vim
|
||||
from UltiSnips.geometry import Position
|
||||
from UltiSnips.position import Position
|
||||
|
||||
def _calc_end(text, start):
|
||||
"""Calculate the end position of the 'text' starting at 'start."""
|
||||
@ -126,7 +126,7 @@ class TextObject(object):
|
||||
if self._parent:
|
||||
self._parent._child_has_moved(
|
||||
self._parent._childs.index(self), min(old_end, self._end),
|
||||
self._end.diff(old_end)
|
||||
self._end.delta(old_end)
|
||||
)
|
||||
|
||||
def _update(self, done):
|
||||
|
@ -9,8 +9,9 @@ definitions into logical units called Tokens.
|
||||
import string
|
||||
import re
|
||||
|
||||
from UltiSnips.geometry import Position
|
||||
from UltiSnips.compatibility import as_unicode
|
||||
from UltiSnips.position import Position
|
||||
from UltiSnips.escaping import unescape
|
||||
|
||||
class _TextIterator(object):
|
||||
"""Helper class to make iterating over text easier."""
|
||||
@ -55,19 +56,6 @@ class _TextIterator(object):
|
||||
"""Current position in the text."""
|
||||
return Position(self._line, self._col)
|
||||
|
||||
def _unescape(text):
|
||||
"""Removes escaping from 'text'."""
|
||||
rv = ""
|
||||
i = 0
|
||||
while i < len(text):
|
||||
if i+1 < len(text) and text[i] == '\\':
|
||||
rv += text[i+1]
|
||||
i += 1
|
||||
else:
|
||||
rv += text[i]
|
||||
i += 1
|
||||
return rv
|
||||
|
||||
def _parse_number(stream):
|
||||
"""
|
||||
Expects the stream to contain a number next, returns the number
|
||||
@ -178,7 +166,7 @@ class VisualToken(Token):
|
||||
if stream.peek() == ":":
|
||||
stream.next()
|
||||
self.alternative_text, char = _parse_till_unescaped_char(stream, '/}')
|
||||
self.alternative_text = _unescape(self.alternative_text)
|
||||
self.alternative_text = unescape(self.alternative_text)
|
||||
|
||||
if char == '/': # Transformation going on
|
||||
try:
|
||||
|
@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
from UltiSnips.geometry import Position
|
||||
from UltiSnips.text_objects._lexer import tokenize, EscapeCharToken, VisualToken, \
|
||||
TransformationToken, TabStopToken, MirrorToken, PythonCodeToken, \
|
||||
VimLCodeToken, ShellCodeToken
|
||||
from UltiSnips.position import Position
|
||||
from UltiSnips.text_objects._escaped_char import EscapedChar
|
||||
from UltiSnips.text_objects._mirror import Mirror
|
||||
from UltiSnips.text_objects._python_code import PythonCode
|
||||
|
@ -1,54 +1,52 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
"""Implements `!p ` interpolation."""
|
||||
|
||||
import os
|
||||
from collections import namedtuple
|
||||
|
||||
import UltiSnips._vim as _vim
|
||||
from UltiSnips.compatibility import as_unicode
|
||||
from UltiSnips.indent_util import IndentUtil
|
||||
|
||||
from UltiSnips.text_objects._base import NoneditableTextObject
|
||||
import UltiSnips._vim as _vim
|
||||
|
||||
|
||||
class _Tabs(object):
|
||||
"""Allows access to tabstop content via t[] inside of python code."""
|
||||
def __init__(self, to):
|
||||
self._to = to
|
||||
|
||||
def __getitem__(self, no):
|
||||
ts = self._to._get_tabstop(self._to, int(no))
|
||||
ts = self._to._get_tabstop(self._to, int(no)) # pylint:disable=protected-access
|
||||
if ts is None:
|
||||
return ""
|
||||
return ts.current_text
|
||||
|
||||
_VisualContent = namedtuple('_VisualContent', ['mode', 'text'])
|
||||
|
||||
|
||||
class SnippetUtil(object):
|
||||
""" Provides easy access to indentation, etc.
|
||||
"""
|
||||
"""Provides easy access to indentation, etc. This is the 'snip' object in
|
||||
python code."""
|
||||
|
||||
def __init__(self, initial_indent, vmode, vtext):
|
||||
self._ind = IndentUtil()
|
||||
self._visual = _VisualContent(vmode, vtext)
|
||||
|
||||
self._initial_indent = self._ind.indent_to_spaces(initial_indent)
|
||||
|
||||
self._reset("")
|
||||
|
||||
def _reset(self, cur):
|
||||
""" Gets the snippet ready for another update.
|
||||
|
||||
"""Gets the snippet ready for another update.
|
||||
:cur: the new value for c.
|
||||
"""
|
||||
self._ind.reset()
|
||||
self._c = cur
|
||||
self._cur = cur
|
||||
self._rv = ""
|
||||
self._changed = False
|
||||
self.reset_indent()
|
||||
|
||||
def shift(self, amount=1):
|
||||
""" Shifts the indentation level.
|
||||
"""Shifts the indentation level.
|
||||
Note that this uses the shiftwidth because thats what code
|
||||
formatters use.
|
||||
|
||||
@ -57,7 +55,7 @@ class SnippetUtil(object):
|
||||
self.indent += " " * self._ind.shiftwidth * amount
|
||||
|
||||
def unshift(self, amount=1):
|
||||
""" Unshift the indentation level.
|
||||
"""Unshift the indentation level.
|
||||
Note that this uses the shiftwidth because thats what code
|
||||
formatters use.
|
||||
|
||||
@ -70,7 +68,7 @@ class SnippetUtil(object):
|
||||
self.indent = ""
|
||||
|
||||
def mkline(self, line="", indent=None):
|
||||
""" Creates a properly set up line.
|
||||
"""Creates a properly set up line.
|
||||
|
||||
:line: the text to add
|
||||
:indent: the indentation to have at the beginning
|
||||
@ -90,62 +88,54 @@ class SnippetUtil(object):
|
||||
return indent + line
|
||||
|
||||
def reset_indent(self):
|
||||
""" Clears the indentation. """
|
||||
"""Clears the indentation."""
|
||||
self.indent = self._initial_indent
|
||||
|
||||
# Utility methods
|
||||
@property
|
||||
def fn(self):
|
||||
""" The filename. """
|
||||
def fn(self): # pylint:disable=no-self-use,invalid-name
|
||||
"""The filename."""
|
||||
return _vim.eval('expand("%:t")') or ""
|
||||
|
||||
@property
|
||||
def basename(self):
|
||||
""" The filename without extension. """
|
||||
def basename(self): # pylint:disable=no-self-use
|
||||
"""The filename without extension."""
|
||||
return _vim.eval('expand("%:t:r")') or ""
|
||||
|
||||
@property
|
||||
def ft(self):
|
||||
""" The filetype. """
|
||||
def ft(self): # pylint:disable=invalid-name
|
||||
"""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.
|
||||
@property
|
||||
def rv(self): # pylint:disable=invalid-name
|
||||
"""The return value. The text to insert at the location of the
|
||||
placeholder."""
|
||||
return self._rv
|
||||
|
||||
Deprecates res.
|
||||
"""
|
||||
def fget(self):
|
||||
return self._rv
|
||||
|
||||
def fset(self, value):
|
||||
self._changed = True
|
||||
self._rv = value
|
||||
return locals()
|
||||
rv = property(**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. """
|
||||
"""True if rv has changed."""
|
||||
return self._changed
|
||||
|
||||
@property
|
||||
def c(self):
|
||||
""" The current text of the placeholder.
|
||||
|
||||
Deprecates cur.
|
||||
"""
|
||||
return self._c
|
||||
def c(self): # pylint:disable=invalid-name
|
||||
"""The current text of the placeholder."""
|
||||
return self._cur
|
||||
|
||||
@property
|
||||
def v(self):
|
||||
def v(self): # pylint:disable=invalid-name
|
||||
"""Content of visual expansions"""
|
||||
return self._visual
|
||||
|
||||
def opt(self, option, default=None):
|
||||
""" Gets a Vim variable. """
|
||||
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)
|
||||
@ -153,23 +143,24 @@ class SnippetUtil(object):
|
||||
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
|
||||
"""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. """
|
||||
"""Same as unshift."""
|
||||
self.unshift(other)
|
||||
|
||||
def __rshift__(self, other):
|
||||
""" Same as shift. """
|
||||
"""Same as shift."""
|
||||
self.shift(other)
|
||||
|
||||
|
||||
class PythonCode(NoneditableTextObject):
|
||||
"""See module docstring."""
|
||||
|
||||
def __init__(self, parent, token):
|
||||
code = token.code.replace("\\`", "`")
|
||||
|
||||
@ -178,16 +169,16 @@ class PythonCode(NoneditableTextObject):
|
||||
while snippet:
|
||||
try:
|
||||
self._locals = snippet.locals
|
||||
t = snippet.visual_content.text
|
||||
m = snippet.visual_content.mode
|
||||
text = snippet.visual_content.text
|
||||
mode = snippet.visual_content.mode
|
||||
break
|
||||
except AttributeError:
|
||||
snippet = snippet._parent
|
||||
self._snip = SnippetUtil(token.indent, m, t)
|
||||
snippet = snippet._parent # pylint:disable=protected-access
|
||||
self._snip = SnippetUtil(token.indent, mode, text)
|
||||
|
||||
self._globals = {}
|
||||
globals = snippet.globals.get("!p", [])
|
||||
exec("\n".join(globals).replace("\r\n", "\n"), self._globals)
|
||||
exec("\n".join(globals).replace("\r\n", "\n"), self._globals) # pylint:disable=exec-used
|
||||
|
||||
# Add Some convenience to the code
|
||||
self._code = "import re, os, vim, string, random\n" + code
|
||||
@ -201,7 +192,7 @@ class PythonCode(NoneditableTextObject):
|
||||
fn = os.path.basename(path)
|
||||
|
||||
ct = self.current_text
|
||||
self._snip._reset(ct)
|
||||
self._snip._reset(ct) # pylint:disable=protected-access
|
||||
local_d = self._locals
|
||||
|
||||
local_d.update({
|
||||
@ -213,10 +204,10 @@ class PythonCode(NoneditableTextObject):
|
||||
'snip': self._snip,
|
||||
})
|
||||
|
||||
exec(self._code, self._globals, local_d)
|
||||
exec(self._code, self._globals, local_d) # pylint:disable=exec-used
|
||||
|
||||
rv = as_unicode(
|
||||
self._snip.rv if self._snip._rv_changed
|
||||
self._snip.rv if self._snip._rv_changed # pylint:disable=protected-access
|
||||
else as_unicode(local_d['res'])
|
||||
)
|
||||
|
||||
|
@ -5,9 +5,8 @@
|
||||
user expands a snippet, a SnippetInstance is created to keep track of the
|
||||
corresponding TextObjects. The Snippet itself is also a TextObject. """
|
||||
|
||||
from UltiSnips.geometry import Position
|
||||
from UltiSnips.position import Position
|
||||
import UltiSnips._vim as _vim
|
||||
|
||||
from UltiSnips.text_objects._base import EditableTextObject, \
|
||||
NoneditableTextObject
|
||||
from UltiSnips.text_objects._parser import TOParser
|
||||
|
@ -1,109 +1,105 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
"""Implements TabStop transformations."""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from UltiSnips.text_objects._mirror import Mirror
|
||||
from UltiSnips.escaping import unescape, fill_in_whitespace
|
||||
|
||||
def _find_closing_brace(string, start_pos):
|
||||
"""Finds the corresponding closing brace after start_pos."""
|
||||
bracks_open = 1
|
||||
for idx, char in enumerate(string[start_pos:]):
|
||||
if char == '(':
|
||||
if string[idx+start_pos-1] != '\\':
|
||||
bracks_open += 1
|
||||
elif char == ')':
|
||||
if string[idx+start_pos-1] != '\\':
|
||||
bracks_open -= 1
|
||||
if not bracks_open:
|
||||
return start_pos+idx+1
|
||||
|
||||
def _split_conditional(string):
|
||||
"""Split the given conditional 'string' into its arguments."""
|
||||
bracks_open = 0
|
||||
args = []
|
||||
carg = ""
|
||||
for idx, char in enumerate(string):
|
||||
if char == '(':
|
||||
if string[idx-1] != '\\':
|
||||
bracks_open += 1
|
||||
elif char == ')':
|
||||
if string[idx-1] != '\\':
|
||||
bracks_open -= 1
|
||||
elif char == ':' and not bracks_open and not string[idx-1] == '\\':
|
||||
args.append(carg)
|
||||
carg = ""
|
||||
continue
|
||||
carg += char
|
||||
args.append(carg)
|
||||
return args
|
||||
|
||||
def _replace_conditional(match, string):
|
||||
"""Replaces a conditional match in a transformation."""
|
||||
conditional_match = _CONDITIONAL.search(string)
|
||||
while conditional_match:
|
||||
start = conditional_match.start()
|
||||
end = _find_closing_brace(string, start+4)
|
||||
args = _split_conditional(string[start+4:end-1])
|
||||
rv = ""
|
||||
if match.group(int(conditional_match.group(1))):
|
||||
rv = unescape(_replace_conditional(match, args[0]))
|
||||
elif len(args) > 1:
|
||||
rv = unescape(_replace_conditional(match, args[1]))
|
||||
string = string[:start] + rv + string[end:]
|
||||
conditional_match = _CONDITIONAL.search(string)
|
||||
return string
|
||||
|
||||
_ONE_CHAR_CASE_SWITCH = re.compile(r"\\([ul].)", re.DOTALL)
|
||||
_LONG_CASEFOLDINGS = re.compile(r"\\([UL].*?)\\E", re.DOTALL)
|
||||
_DOLLAR = re.compile(r"\$(\d+)", re.DOTALL)
|
||||
_CONDITIONAL = re.compile(r"\(\?(\d+):", re.DOTALL)
|
||||
class _CleverReplace(object):
|
||||
"""Mimics TextMates replace syntax."""
|
||||
|
||||
def __init__(self, expression):
|
||||
self._expression = expression
|
||||
|
||||
def replace(self, match):
|
||||
"""Replaces 'match' through the correct replacement string."""
|
||||
transformed = self._expression
|
||||
# Replace all $? with capture groups
|
||||
transformed = _DOLLAR.subn(
|
||||
lambda m: match.group(int(m.group(1))), transformed)[0]
|
||||
|
||||
# Replace Case switches
|
||||
def _one_char_case_change(match):
|
||||
"""Replaces one character case changes."""
|
||||
if match.group(1)[0] == 'u':
|
||||
return match.group(1)[-1].upper()
|
||||
else:
|
||||
return match.group(1)[-1].lower()
|
||||
transformed = _ONE_CHAR_CASE_SWITCH.subn(
|
||||
_one_char_case_change, transformed)[0]
|
||||
|
||||
def _multi_char_case_change(match):
|
||||
"""Replaces multi character case changes."""
|
||||
if match.group(1)[0] == 'U':
|
||||
return match.group(1)[1:].upper()
|
||||
else:
|
||||
return match.group(1)[1:].lower()
|
||||
transformed = _LONG_CASEFOLDINGS.subn(
|
||||
_multi_char_case_change, transformed)[0]
|
||||
transformed = _replace_conditional(match, transformed)
|
||||
return unescape(fill_in_whitespace(transformed))
|
||||
|
||||
# flag used to display only one time the lack of unidecode
|
||||
UNIDECODE_ALERT_RAISED = False
|
||||
|
||||
class _CleverReplace(object):
|
||||
"""
|
||||
This class mimics TextMates replace syntax
|
||||
"""
|
||||
_DOLLAR = re.compile(r"\$(\d+)", re.DOTALL)
|
||||
_SIMPLE_CASEFOLDINGS = re.compile(r"\\([ul].)", re.DOTALL)
|
||||
_LONG_CASEFOLDINGS = re.compile(r"\\([UL].*?)\\E", re.DOTALL)
|
||||
_CONDITIONAL = re.compile(r"\(\?(\d+):", re.DOTALL)
|
||||
|
||||
_UNESCAPE = re.compile(r'\\[^ntrab]')
|
||||
_SCHARS_ESCPAE = re.compile(r'\\[ntrab]')
|
||||
|
||||
def __init__(self, s):
|
||||
self._s = s
|
||||
|
||||
def _scase_folding(self, m):
|
||||
if m.group(1)[0] == 'u':
|
||||
return m.group(1)[-1].upper()
|
||||
else:
|
||||
return m.group(1)[-1].lower()
|
||||
def _lcase_folding(self, m):
|
||||
if m.group(1)[0] == 'U':
|
||||
return m.group(1)[1:].upper()
|
||||
else:
|
||||
return m.group(1)[1:].lower()
|
||||
|
||||
def _replace_conditional(self, match, v):
|
||||
def _find_closingbrace(v,start_pos):
|
||||
bracks_open = 1
|
||||
for idx, c in enumerate(v[start_pos:]):
|
||||
if c == '(':
|
||||
if v[idx+start_pos-1] != '\\':
|
||||
bracks_open += 1
|
||||
elif c == ')':
|
||||
if v[idx+start_pos-1] != '\\':
|
||||
bracks_open -= 1
|
||||
if not bracks_open:
|
||||
return start_pos+idx+1
|
||||
m = self._CONDITIONAL.search(v)
|
||||
|
||||
def _part_conditional(v):
|
||||
bracks_open = 0
|
||||
args = []
|
||||
carg = ""
|
||||
for idx, c in enumerate(v):
|
||||
if c == '(':
|
||||
if v[idx-1] != '\\':
|
||||
bracks_open += 1
|
||||
elif c == ')':
|
||||
if v[idx-1] != '\\':
|
||||
bracks_open -= 1
|
||||
elif c == ':' and not bracks_open and not v[idx-1] == '\\':
|
||||
args.append(carg)
|
||||
carg = ""
|
||||
continue
|
||||
carg += c
|
||||
args.append(carg)
|
||||
return args
|
||||
|
||||
while m:
|
||||
start = m.start()
|
||||
end = _find_closingbrace(v,start+4)
|
||||
args = _part_conditional(v[start+4:end-1])
|
||||
|
||||
rv = ""
|
||||
if match.group(int(m.group(1))):
|
||||
rv = self._unescape(self._replace_conditional(match,args[0]))
|
||||
elif len(args) > 1:
|
||||
rv = self._unescape(self._replace_conditional(match,args[1]))
|
||||
|
||||
v = v[:start] + rv + v[end:]
|
||||
|
||||
m = self._CONDITIONAL.search(v)
|
||||
return v
|
||||
|
||||
def _unescape(self, v):
|
||||
return self._UNESCAPE.subn(lambda m: m.group(0)[-1], v)[0]
|
||||
def _schar_escape(self, v):
|
||||
return self._SCHARS_ESCPAE.subn(lambda m: eval(r"'\%s'" % m.group(0)[-1]), v)[0]
|
||||
|
||||
def replace(self, match):
|
||||
start, end = match.span()
|
||||
|
||||
tv = self._s
|
||||
|
||||
# Replace all $? with capture groups
|
||||
tv = self._DOLLAR.subn(lambda m: match.group(int(m.group(1))), tv)[0]
|
||||
|
||||
# Replace CaseFoldings
|
||||
tv = self._SIMPLE_CASEFOLDINGS.subn(self._scase_folding, tv)[0]
|
||||
tv = self._LONG_CASEFOLDINGS.subn(self._lcase_folding, tv)[0]
|
||||
tv = self._replace_conditional(match, tv)
|
||||
|
||||
return self._unescape(self._schar_escape(tv))
|
||||
|
||||
class TextObjectTransformation(object):
|
||||
"""Base class for Transformations and ${VISUAL}."""
|
||||
|
||||
def __init__(self, token):
|
||||
self._convert_to_ascii = False
|
||||
|
||||
@ -125,20 +121,26 @@ class TextObjectTransformation(object):
|
||||
self._replace = _CleverReplace(token.replace)
|
||||
|
||||
def _transform(self, text):
|
||||
global UNIDECODE_ALERT_RAISED
|
||||
"""Do the actual transform on the given text."""
|
||||
global UNIDECODE_ALERT_RAISED # pylint:disable=global-statement
|
||||
if self._convert_to_ascii:
|
||||
try:
|
||||
import unidecode
|
||||
text = unidecode.unidecode(text)
|
||||
except Exception as e:
|
||||
except Exception: # pylint:disable=broad-except
|
||||
if UNIDECODE_ALERT_RAISED == False:
|
||||
UNIDECODE_ALERT_RAISED = True
|
||||
sys.stderr.write("Please install unidecode python package in order to be able to make ascii conversions.\n")
|
||||
sys.stderr.write(
|
||||
"Please install unidecode python package in order to "
|
||||
"be able to make ascii conversions.\n")
|
||||
if self._find is None:
|
||||
return text
|
||||
return self._find.subn(self._replace.replace, text, self._match_this_many)[0]
|
||||
return self._find.subn(
|
||||
self._replace.replace, text, self._match_this_many)[0]
|
||||
|
||||
class Transformation(Mirror, TextObjectTransformation):
|
||||
"""See module docstring."""
|
||||
|
||||
def __init__(self, parent, ts, token):
|
||||
Mirror.__init__(self, parent, ts, token)
|
||||
TextObjectTransformation.__init__(self, token)
|
||||
|
@ -6,7 +6,7 @@
|
||||
from collections import deque
|
||||
|
||||
from UltiSnips.compatibility import as_unicode, byte2col
|
||||
from UltiSnips.geometry import Position
|
||||
from UltiSnips.position import Position
|
||||
import UltiSnips._vim as _vim
|
||||
|
||||
class VimPosition(Position):
|
||||
|
Loading…
x
Reference in New Issue
Block a user