All linting done. Project is now lint warning free.

This commit is contained in:
Holger Rapp 2014-02-08 15:52:06 +01:00
parent 873b5b4ad4
commit 773869b1e1
11 changed files with 183 additions and 122 deletions

View File

@ -1,7 +1,5 @@
[MASTER]
msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}"
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
init-hook='import sys; sys.path.append("pythonx/")'
@ -38,13 +36,14 @@ disable=
attribute-defined-outside-init,
fixme,
redefined-builtin,
too-many-arguments,
too-many-instance-attributes,
too-many-public-methods,
too-few-public-methods,
too-many-arguments,
too-many-branches,
too-many-statements,
too-many-instance-attributes,
too-many-locals,
too-many-public-methods,
too-many-return-statements,
too-many-statements,
[REPORTS]
@ -57,6 +56,9 @@ output-format=text
# Tells whether to display a full report or only the messages
reports=yes
msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}"
[BASIC]
@ -99,7 +101,7 @@ class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_
good-names=i,j,a,b,x,y,k,ex,Run,_
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata

View File

@ -145,6 +145,9 @@ Following is the full stack trace:
_vim.new_scratch_buffer(msg)
return wrapper
# TODO(sirver): This class has too many responsibilities - it should not also
# care for the parsing and managing of parsed snippets.
class SnippetManager(object):
"""The main entry point for all UltiSnips functionality. All Vim functions
call methods in this class."""
@ -162,7 +165,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 = defaultdict(lambda: SnippetDictionary())
self._snippets = defaultdict(SnippetDictionary)
self._filetypes = defaultdict(lambda: ['all'])
self._visual_content = VisualContentPreserver()
@ -390,11 +393,8 @@ class SnippetManager(object):
self._current_snippet_is_done()
self._reinit()
###################################
# Private/Protect Functions Below #
###################################
def _error(self, msg):
# TODO(sirver): This is only used by SnippetsFileParser
def report_error(self, msg):
"""Shows 'msg' as error to the user."""
msg = _vim.escape("UltiSnips: " + msg)
if self._test_error:
@ -410,6 +410,9 @@ class SnippetManager(object):
else:
_vim.command("echoerr %s" % msg)
###################################
# Private/Protect Functions Below #
###################################
def _reinit(self):
"""Resets transient state."""
self._ctab = None

View File

@ -1,14 +1,19 @@
#!/usr/bin/env python
# encoding: utf-8
"""Commands to compare text objects and to guess how to transform from one to
another."""
from collections import defaultdict
import sys
from UltiSnips import _vim
from UltiSnips.position import Position
def is_complete_edit(initial_line, a, b, cmds):
buf = a[:]
def is_complete_edit(initial_line, original, wanted, cmds):
"""Returns true if 'original' is changed to 'wanted' with the edit commands
in 'cmds'. Initial line is to change the line numbers in 'cmds'."""
buf = original[:]
for cmd in cmds:
ctype, line, col, char = cmd
line -= initial_line
@ -24,95 +29,133 @@ def is_complete_edit(initial_line, a, b, cmds):
elif ctype == "I":
buf[line] = buf[line][:col] + char + buf[line][col:]
buf = '\n'.join(buf).split('\n')
return len(buf) == len(b) and all(j==k for j,k in zip(buf, b))
return (len(buf) == len(wanted) and
all(j == k for j, k in zip(buf, wanted)))
def guess_edit(initial_line, lt, ct, vs):
def guess_edit(initial_line, last_text, current_text, vim_state):
"""
Try to guess what the user might have done by heuristically looking at cursor movement
number of changed lines and if they got longer or shorter. This will detect most simple
movements like insertion, deletion of a line or carriage return.
Try to guess what the user might have done by heuristically looking at
cursor movement, number of changed lines and if they got longer or shorter.
This will detect most simple movements like insertion, deletion of a line
or carriage return. 'initial_text' is the index of where the comparison
starts, 'last_text' is the last text of the snippet, 'current_text' is the
current text of the snippet and 'vim_state' is the cached vim state.
Returns (True, edit_cmds) when the edit could be guessed, (False, None)
otherwise.
"""
if not len(lt) and not len(ct): return True, ()
pos = vs.pos
ppos = vs.ppos
if len(lt) and (not ct or (len(ct) == 1 and not ct[0])): # All text deleted?
if not len(last_text) and not len(current_text):
return True, ()
pos = vim_state.pos
ppos = vim_state.ppos
# All text deleted?
if (len(last_text) and
(not current_text or
(len(current_text) == 1 and not current_text[0]))
):
es = []
if not ct: ct = ['']
for i in lt:
if not current_text:
current_text = ['']
for i in last_text:
es.append(("D", initial_line, 0, i))
es.append(("D", initial_line, 0, "\n"))
es.pop() # Remove final \n because it is not really removed
if is_complete_edit(initial_line, lt, ct, es): return True, es
if is_complete_edit(initial_line, last_text, current_text, es):
return True, es
if ppos.mode == 'v': # Maybe selectmode?
sv = list(map(int, _vim.eval("""getpos("'<")"""))); sv = Position(sv[1]-1,sv[2]-1)
ev = list(map(int, _vim.eval("""getpos("'>")"""))); ev = Position(ev[1]-1,ev[2]-1)
sv = list(map(int, _vim.eval("""getpos("'<")""")))
sv = Position(sv[1]-1, sv[2]-1)
ev = list(map(int, _vim.eval("""getpos("'>")""")))
ev = Position(ev[1]-1, ev[2]-1)
if "exclusive" in _vim.eval("&selection"):
ppos.col -= 1 # We want to be inclusive, sorry.
ev.col -= 1
es = []
if sv.line == ev.line:
es.append(("D", sv.line, sv.col, lt[sv.line - initial_line][sv.col:ev.col+1]))
es.append(("D", sv.line, sv.col,
last_text[sv.line - initial_line][sv.col:ev.col+1]))
if sv != pos and sv.line == pos.line:
es.append(("I", sv.line, sv.col, ct[sv.line - initial_line][sv.col:pos.col+1]))
if is_complete_edit(initial_line, lt, ct, es): return True, es
es.append(("I", sv.line, sv.col,
current_text[sv.line - initial_line][sv.col:pos.col+1]))
if is_complete_edit(initial_line, last_text, current_text, es):
return True, es
if pos.line == ppos.line:
if len(lt) == len(ct): # Movement only in one line
llen = len(lt[ppos.line - initial_line])
clen = len(ct[pos.line - initial_line])
if ppos < pos and clen > llen: # Likely that only characters have been added
if len(last_text) == len(current_text): # Movement only in one line
llen = len(last_text[ppos.line - initial_line])
clen = len(current_text[pos.line - initial_line])
if ppos < pos and clen > llen: # maybe only chars have been added
es = (
("I", ppos.line, ppos.col, ct[ppos.line - initial_line][ppos.col:pos.col]),
("I", ppos.line, ppos.col,
current_text[ppos.line - initial_line]
[ppos.col:pos.col]),
)
if is_complete_edit(initial_line, lt, ct, es): return True, es
if is_complete_edit(initial_line, last_text, current_text, es):
return True, es
if clen < llen:
if ppos == pos: # 'x' or DEL or dt or something
es = (
("D", pos.line, pos.col, lt[ppos.line - initial_line][ppos.col:ppos.col + (llen - clen)]),
("D", pos.line, pos.col,
last_text[ppos.line - initial_line]
[ppos.col:ppos.col + (llen - clen)]),
)
if is_complete_edit(initial_line, lt, ct, es): return True, es
if is_complete_edit(initial_line, last_text,
current_text, es):
return True, es
if pos < ppos: # Backspacing or dT dF?
es = (
("D", pos.line, pos.col, lt[pos.line - initial_line][pos.col:pos.col + llen - clen]),
("D", pos.line, pos.col,
last_text[pos.line - initial_line]
[pos.col:pos.col + llen - clen]),
)
if is_complete_edit(initial_line, lt, ct, es): return True, es
elif len(ct) < len(lt): # Maybe some lines were deleted? (dd or so)
if is_complete_edit(initial_line, last_text,
current_text, es):
return True, es
elif len(current_text) < len(last_text):
# where some lines deleted? (dd or so)
es = []
for i in range(len(lt)-len(ct)):
es.append( ("D", pos.line, 0, lt[pos.line - initial_line + i]))
es.append( ("D", pos.line, 0, '\n'))
if is_complete_edit(initial_line, lt, ct, es): return True, es
else: # Movement in more than one line
for i in range(len(last_text)-len(current_text)):
es.append(("D", pos.line, 0,
last_text[pos.line - initial_line + i]))
es.append(("D", pos.line, 0, '\n'))
if is_complete_edit(initial_line, last_text,
current_text, es):
return True, es
else:
# Movement in more than one line
if ppos.line + 1 == pos.line and pos.col == 0: # Carriage return?
es = (("I", ppos.line, ppos.col, "\n"),)
if is_complete_edit(initial_line, lt, ct, es): return True, es
if is_complete_edit(initial_line, last_text,
current_text, es):
return True, es
return False, None
def diff(a, b, sline = 0):
def diff(a, b, sline=0):
"""
Return a list of deletions and insertions that will turn a into b. This is
done by traversing an implicit edit graph and searching for the shortest
Return a list of deletions and insertions that will turn 'a' into 'b'. This
is done by traversing an implicit edit graph and searching for the shortest
route. The basic idea is as follows:
- Matching a character is free as long as there was no deletion/insertion
before. Then, matching will be seen as delete + insert [1].
- Matching a character is free as long as there was no
deletion/insertion before. Then, matching will be seen as delete +
insert [1].
- Deleting one character has the same cost everywhere. Each additional
character costs only have of the first deletion.
- Insertion is cheaper the earlier it happes. The first character is more
expensive that any later [2].
- Insertion is cheaper the earlier it happens. The first character is
more expensive that any later [2].
[1] This is that world -> aolsa will be "D" world + "I" aolsa instead of
"D" w , "D" rld, "I" a, "I" lsa
[2] This is that "hello\n\n" -> "hello\n\n\n" will insert a newline after hello
and not after \n
[2] This is that "hello\n\n" -> "hello\n\n\n" will insert a newline after
hello and not after \n
"""
d = defaultdict(list)
d = defaultdict(list) # pylint:disable=invalid-name
seen = defaultdict(lambda: sys.maxsize)
d[0] = [ (0,0,sline, 0, ()) ]
d[0] = [(0, 0, sline, 0, ())]
cost = 0
D_COST = len(a)+len(b)
I_COST = len(a)+len(b)
deletion_cost = len(a)+len(b)
insertion_cost = len(a)+len(b)
while True:
while len(d[cost]):
x, y, line, col, what = d[cost].pop()
@ -125,17 +168,16 @@ def diff(a, b, sline = 0):
nline = line
if a[x] == '\n':
ncol = 0
nline +=1
nline += 1
lcost = cost + 1
if (what and what[-1][0] == "D" and what[-1][1] == line and
what[-1][2] == col and a[x] != '\n'):
# Matching directly after a deletion should be as costly as
# DELETE + INSERT + a bit
lcost = (D_COST + I_COST)*1.5
if seen[x+1,y+1] > lcost:
d[lcost].append((x+1,y+1, nline, ncol, what))
seen[x+1,y+1] = lcost
lcost = (deletion_cost + insertion_cost)*1.5
if seen[x+1, y+1] > lcost:
d[lcost].append((x+1, y+1, nline, ncol, what))
seen[x+1, y+1] = lcost
if y < len(b): # INSERT
ncol = col + 1
nline = line
@ -144,30 +186,34 @@ def diff(a, b, sline = 0):
nline += 1
if (what and what[-1][0] == "I" and what[-1][1] == nline and
what[-1][2]+len(what[-1][-1]) == col and b[y] != '\n' and
seen[x,y+1] > cost + (I_COST + ncol) // 2
seen[x, y+1] > cost + (insertion_cost + ncol) // 2
):
seen[x,y+1] = cost + (I_COST + ncol) // 2
d[cost + (I_COST + ncol) // 2].append(
(x,y+1, line, ncol, what[:-1] + (
("I", what[-1][1], what[-1][2], what[-1][-1] + b[y]),) )
seen[x, y+1] = cost + (insertion_cost + ncol) // 2
d[cost + (insertion_cost + ncol) // 2].append(
(x, y+1, line, ncol, what[:-1] + (
("I", what[-1][1], what[-1][2],
what[-1][-1] + b[y]),)
)
)
elif seen[x,y+1] > cost + I_COST + ncol:
seen[x,y+1] = cost + I_COST + ncol
d[cost + ncol + I_COST].append((x,y+1, nline, ncol,
what + (("I", line, col,b[y]),))
elif seen[x, y+1] > cost + insertion_cost + ncol:
seen[x, y+1] = cost + insertion_cost + ncol
d[cost + ncol + insertion_cost].append((x, y+1, nline, ncol,
what + (("I", line, col, b[y]),))
)
if x < len(a): # DELETE
if (what and what[-1][0] == "D" and what[-1][1] == line and
what[-1][2] == col and a[x] != '\n' and what[-1][-1] != '\n' and
seen[x+1,y] > cost + D_COST // 2
what[-1][2] == col and a[x] != '\n' and
what[-1][-1] != '\n' and
seen[x+1, y] > cost + deletion_cost // 2
):
seen[x+1,y] = cost + D_COST // 2
d[cost + D_COST // 2].append((x+1,y, line, col, what[:-1] +
(("D",line, col, what[-1][-1] + a[x]),) )
seen[x+1, y] = cost + deletion_cost // 2
d[cost + deletion_cost // 2].append(
(x+1, y, line, col, what[:-1] + (
("D", line, col, what[-1][-1] + a[x]),))
)
elif seen[x+1,y] > cost + D_COST:
seen[x+1,y] = cost + D_COST
d[cost + D_COST].append((x+1,y, line, col, what +
(("D",line, col, a[x]),) )
elif seen[x+1, y] > cost + deletion_cost:
seen[x+1, y] = cost + deletion_cost
d[cost + deletion_cost].append((x+1, y, line, col, what +
(("D", line, col, a[x]),))
)
cost += 1

View File

@ -1,11 +1,17 @@
#!/usr/bin/env python
# encoding: utf-8
"""Parsing of snippet files."""
import re
import UltiSnips._vim as _vim
# TODO(sirver): This could just as well be a function.
# TODO(sirver): This could just as well be a function. Also the
# interface should change to a stream of events - so that it does
# not need knowledge of SnippetManager.
class SnippetsFileParser(object):
"""Does the actual parsing."""
def __init__(self, ft, fn, snip_manager, file_data=None):
"""Parser 'fn' as filetype 'ft'."""
self._sm = snip_manager
@ -19,33 +25,27 @@ class SnippetsFileParser(object):
self._idx = 0
def _error(self, msg):
"""Reports 'msg' as an error."""
fn = _vim.eval("""fnamemodify(%s, ":~:.")""" % _vim.escape(self._fn))
self._sm._error("%s in %s(%d)" % (msg, fn, self._idx + 1))
self._sm.report_error("%s in %s(%d)" % (msg, fn, self._idx + 1))
def _line(self):
if self._idx < len(self._lines):
line = self._lines[self._idx]
else:
line = ""
return line
"""The current line or the empty string."""
return self._lines[self._idx] if self._idx < len(self._lines) else ""
def _line_head_tail(self):
"""Returns (first word, rest) of the current line."""
parts = re.split(r"\s+", self._line().rstrip(), maxsplit=1)
parts.append('')
return parts[:2]
def _line_head(self):
return self._line_head_tail()[0]
def _line_tail(self):
return self._line_head_tail()[1]
def _goto_next_line(self):
"""Advances to and returns the next line."""
self._idx += 1
return self._line()
def _parse_first(self, line):
""" Parses the first line of the snippet definition. Returns the
"""Parses the first line of the snippet definition. Returns the
snippet type, trigger, description, and options in a tuple in that
order.
"""
@ -80,10 +80,10 @@ class SnippetsFileParser(object):
cs = ""
else:
cs = cs[1:-1]
return (snip, cs, cdescr, coptions)
def _parse_snippet(self):
"""Parses the snippet that begins at the current line."""
line = self._line()
(snip, trig, desc, opts) = self._parse_first(line)
@ -109,17 +109,19 @@ class SnippetsFileParser(object):
self._globals[trig] = []
self._globals[trig].append(cv)
elif snip == "snippet":
self._sm.add_snippet(trig, cv, desc, opts, self._ft, self._globals, fn=self._fn)
self._sm.add_snippet(
trig, cv, desc, opts, self._ft, self._globals, fn=self._fn)
else:
self._error("Invalid snippet type: '%s'" % snip)
def parse(self):
"""Parses the given file."""
while self._line():
head, tail = self._line_head_tail()
if head == "extends":
if tail:
self._sm.add_extending_info(self._ft,
[ p.strip() for p in tail.split(',') ])
[p.strip() for p in tail.split(',')])
else:
self._error("'extends' without file types")
elif head in ("snippet", "global"):

View File

@ -1,9 +1,9 @@
#!/usr/bin/env python
# encoding: utf-8
import unittest
# pylint: skip-file
import os.path as p, sys; sys.path.append(p.join(p.dirname(__file__), ".."))
import unittest
from _diff import diff, guess_edit
from position import Position

View File

@ -1,6 +1,8 @@
#!/usr/bin/env python
# encoding: utf-8
# pylint: skip-file
import unittest
from position import Position

View File

@ -1,14 +1,15 @@
#!/usr/bin/env python
# encoding: utf-8
"""See module comment."""
from UltiSnips.text_objects._base import NoneditableTextObject
class EscapedChar(NoneditableTextObject):
"""
r"""
This class is a escape char like \$. It is handled in a text object to make
sure that siblings are correctly moved after replacing the text.
This is a base class without functionality just to mark it in the code.
"""
pass

View File

@ -1,12 +1,13 @@
#!/usr/bin/env python
# encoding: utf-8
"""A Mirror object contains the same text as its related tabstop."""
from UltiSnips.text_objects._base import NoneditableTextObject
class Mirror(NoneditableTextObject):
"""
A Mirror object mirrors a TabStop that is, text is repeated here
"""
"""See module docstring."""
def __init__(self, parent, tabstop, token):
NoneditableTextObject.__init__(self, parent, token)
@ -15,7 +16,7 @@ class Mirror(NoneditableTextObject):
def _update(self, done):
if self._ts.is_killed:
self.overwrite("")
self._parent._del_child(self)
self._parent._del_child(self) # pylint:disable=protected-access
return True
if self._ts not in done:
@ -25,4 +26,5 @@ class Mirror(NoneditableTextObject):
return True
def _get_text(self):
"""Returns the text used for mirroring. Overwritten by base classes."""
return self._ts.current_text

View File

@ -13,6 +13,7 @@ from UltiSnips.text_objects._parser import parse_text_object
class SnippetInstance(EditableTextObject):
"""See module docstring."""
# pylint:disable=protected-access
def __init__(self, snippet, parent, indent, initial_text,
start, end, visual_content, last_re, globals):

View File

@ -1,11 +1,13 @@
#!/usr/bin/env python
# encoding: utf-8
"""Implements `!v ` VimL interpolation."""
import UltiSnips._vim as _vim
from UltiSnips.text_objects._base import NoneditableTextObject
class VimLCode(NoneditableTextObject):
"""See module docstring."""
def __init__(self, parent, token):
self._code = token.code.replace("\\`", "`").strip()

View File

@ -1,6 +1,10 @@
#!/usr/bin/env python
# encoding: utf-8
"""A ${VISUAL} placeholder that will use the text that was last visually
selected and insert it here. If there was no text visually selected, this will
be the empty string. """
import re
import UltiSnips._vim as _vim
@ -8,13 +12,9 @@ from UltiSnips.indent_util import IndentUtil
from UltiSnips.text_objects._transformation import TextObjectTransformation
from UltiSnips.text_objects._base import NoneditableTextObject
class Visual(NoneditableTextObject,TextObjectTransformation):
"""
A ${VISUAL} placeholder that will use the text that was last visually
selected and insert it here. If there was no text visually selected,
this will be the empty string
"""
__REPLACE_NON_WS = re.compile(r"[^ \t]")
_REPLACE_NON_WS = re.compile(r"[^ \t]")
class Visual(NoneditableTextObject, TextObjectTransformation):
"""See module docstring."""
def __init__(self, parent, token):
# Find our containing snippet for visual_content
@ -25,7 +25,7 @@ class Visual(NoneditableTextObject,TextObjectTransformation):
self._mode = snippet.visual_content.mode
break
except AttributeError:
snippet = snippet._parent
snippet = snippet._parent # pylint:disable=protected-access
if not self._text:
self._text = token.alternative_text
self._mode = "v"
@ -37,7 +37,7 @@ class Visual(NoneditableTextObject,TextObjectTransformation):
if self._mode != "v":
# Keep the indent for Line/Block Selection
text_before = _vim.buf[self.start.line][:self.start.col]
indent = self.__REPLACE_NON_WS.sub(" ", text_before)
indent = _REPLACE_NON_WS.sub(" ", text_before)
iu = IndentUtil()
indent = iu.indent_to_spaces(indent)
indent = iu.spaces_to_indent(indent)
@ -52,6 +52,6 @@ class Visual(NoneditableTextObject,TextObjectTransformation):
text = self._transform(text)
self.overwrite(text)
self._parent._del_child(self)
self._parent._del_child(self) # pylint:disable=protected-access
return True