diff --git a/plugin/UltiSnips/Buffer.py b/plugin/UltiSnips/Buffer.py index 59c4cec..abc1252 100644 --- a/plugin/UltiSnips/Buffer.py +++ b/plugin/UltiSnips/Buffer.py @@ -3,6 +3,7 @@ import vim from UltiSnips.Geometry import Position +from UltiSnips.Compatibility import make_suitable_for_vim __all__ = [ "TextBuffer", "VimBuffer" ] @@ -59,9 +60,9 @@ class VimBuffer(Buffer): return vim.current.buffer[a] def __setitem__(self, a, b): if isinstance(a,slice): - vim.current.buffer[a.start:a.stop] = b + vim.current.buffer[a.start:a.stop] = make_suitable_for_vim(b) else: - vim.current.buffer[a] = b + vim.current.buffer[a] = make_suitable_for_vim(b) # Open any folds this might have created vim.current.window.cursor = a.start + 1, 0 diff --git a/plugin/UltiSnips/Compatibility.py b/plugin/UltiSnips/Compatibility.py index 7fbc43d..359dac2 100644 --- a/plugin/UltiSnips/Compatibility.py +++ b/plugin/UltiSnips/Compatibility.py @@ -8,15 +8,60 @@ as many python versions as possible. import sys -__all__ = ['as_unicode'] +__all__ = ['as_unicode', 'compatible_exec', 'CheapTotalOrdering'] if sys.version_info >= (3,0): + from UltiSnips.Compatibility_py3 import * + + class CheapTotalOrdering: + """Total ordering only appears in python 2.7. We try to stay compatible with + python 2.5 for now, so we define our own""" + + def __lt__(self, other): + return self.__cmp__(other) < 0 + + def __le__(self, other): + return self.__cmp__(other) <= 0 + + def __gt__(self, other): + return self.__cmp__(other) > 0 + + def __ge__(self, other): + return self.__cmp__(other) >= 0 + def as_unicode(s): if isinstance(s, bytes): return s.decode("utf-8") return s + + def make_suitable_for_vim(s): + return s else: + from UltiSnips.Compatibility_py2 import * + + class CheapTotalOrdering(object): + """Total ordering only appears in python 2.7. We try to stay compatible with + python 2.5 for now, so we define our own""" + + def __lt__(self, other): + return self.__cmp__(other) < 0 + + def __le__(self, other): + return self.__cmp__(other) <= 0 + + def __gt__(self, other): + return self.__cmp__(other) > 0 + + def __ge__(self, other): + return self.__cmp__(other) >= 0 + def as_unicode(s): if not isinstance(s, unicode): - return s.decode("uft-8") + return s.decode("utf-8") return s + + def make_suitable_for_vim(s): + if isinstance(s, list): + return [ a.encode("utf-8") for a in s ] + return s.encode("utf-8") + diff --git a/plugin/UltiSnips/Compatibility_py2.py b/plugin/UltiSnips/Compatibility_py2.py new file mode 100755 index 0000000..7e6f5bb --- /dev/null +++ b/plugin/UltiSnips/Compatibility_py2.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +""" +This file contains code that is invalid in python3 and must therefore never be +seen by the interpretor +""" + +def compatible_exec(code, gglobals = None, glocals = None): + if gglobals is not None and glocals is not None: + exec code in gglobals, glocals + elif gglobals is not None: + exec code in gglobals + else: + exec code + + diff --git a/plugin/UltiSnips/Compatibility_py3.py b/plugin/UltiSnips/Compatibility_py3.py new file mode 100644 index 0000000..d0431d4 --- /dev/null +++ b/plugin/UltiSnips/Compatibility_py3.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +# encoding: utf-8 + + +""" +This file contains code that is invalid in python2 and must therefore never be +seen by the interpretor +""" + +compatible_exec = exec diff --git a/plugin/UltiSnips/Geometry.py b/plugin/UltiSnips/Geometry.py index bc3fe59..1e12645 100644 --- a/plugin/UltiSnips/Geometry.py +++ b/plugin/UltiSnips/Geometry.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # encoding: utf-8 -from UltiSnips.Util import CheapTotalOrdering +from UltiSnips.Compatibility import CheapTotalOrdering __all__ = [ "Position", "Span" ] diff --git a/plugin/UltiSnips/TextObjects.py b/plugin/UltiSnips/TextObjects.py index 2c7224f..c5650c7 100644 --- a/plugin/UltiSnips/TextObjects.py +++ b/plugin/UltiSnips/TextObjects.py @@ -7,11 +7,13 @@ import stat import tempfile import vim -from UltiSnips.Util import IndentUtil, CheapTotalOrdering from UltiSnips.Buffer import TextBuffer +from UltiSnips.Compatibility import CheapTotalOrdering +from UltiSnips.Compatibility import compatible_exec from UltiSnips.Geometry import Span, Position from UltiSnips.Lexer import tokenize, EscapeCharToken, TransformationToken, \ TabStopToken, MirrorToken, PythonCodeToken, VimLCodeToken, ShellCodeToken +from UltiSnips.Util import IndentUtil __all__ = [ "Mirror", "Transformation", "SnippetInstance", "StartMarker" ] @@ -146,7 +148,7 @@ class _TOParser(object): for parent, token in all_tokens: if isinstance(token, TransformationToken): if token.no not in seen_ts: - raise RuntimeError("Tabstop %i is not known but is used by a Transformation" % t._ts) + raise RuntimeError("Tabstop %i is not known but is used by a Transformation" % token.no) Transformation(parent, seen_ts[token.no], token) def _do_parse(self, all_tokens, seen_ts): @@ -646,7 +648,7 @@ class PythonCode(TextObject): self._globals = {} globals = snippet.globals.get("!p", []) - exec("\n".join(globals).replace("\r\n", "\n"), self._globals) + compatible_exec("\n".join(globals).replace("\r\n", "\n"), self._globals) # Add Some convenience to the code self._code = "import re, os, vim, string, random\n" + code @@ -674,7 +676,7 @@ class PythonCode(TextObject): }) self._code = self._code.replace("\r\n", "\n") - exec(self._code, self._globals, local_d) + compatible_exec(self._code, self._globals, local_d) if self._snip._rv_changed: self.current_text = self._snip.rv diff --git a/plugin/UltiSnips/Util.py b/plugin/UltiSnips/Util.py index 31f16f1..405a8f7 100644 --- a/plugin/UltiSnips/Util.py +++ b/plugin/UltiSnips/Util.py @@ -7,23 +7,6 @@ import vim from UltiSnips.Compatibility import as_unicode -class CheapTotalOrdering: # TODO: use functools for >= 2.7 - """Total ordering only appears in python 2.7. We try to stay compatible with - python 2.5 for now, so we define our own""" - - def __lt__(self, other): - return self.__cmp__(other) < 0 - - def __le__(self, other): - return self.__cmp__(other) <= 0 - - def __gt__(self, other): - return self.__cmp__(other) > 0 - - def __ge__(self, other): - return self.__cmp__(other) >= 0 - - def vim_string(inp): """ Creates a vim-friendly string from a group of dicts, lists and strings. @@ -36,7 +19,7 @@ def vim_string(inp): "%s:%s" % (conv(key), conv(value)) for key, value in obj.iteritems()]) + '}') else: - rv = as_unicode('"%s"' % str(obj).replace('"', '\\"')) + rv = as_unicode('"%s"') % as_unicode(obj).replace('"', '\\"') return rv return conv(inp) diff --git a/plugin/UltiSnips/__init__.py b/plugin/UltiSnips/__init__.py index 6353f94..06391cc 100644 --- a/plugin/UltiSnips/__init__.py +++ b/plugin/UltiSnips/__init__.py @@ -6,12 +6,12 @@ import glob import hashlib import os import re -import string import traceback import vim from UltiSnips.Geometry import Position +from UltiSnips.Compatibility import make_suitable_for_vim from UltiSnips.TextObjects import * from UltiSnips.Buffer import VimBuffer from UltiSnips.Util import IndentUtil, vim_string, as_unicode @@ -22,7 +22,7 @@ from UltiSnips.Langmap import LangMapTranslator # which is deprecated since 2.5 and will no longer work in 2.7. Let's hope # vim gets this fixed before) import sys -if sys.version_info[:2] >= (2,6): +if (2,6) <= sys.version_info[:2] < (3,0): import warnings warnings.filterwarnings("ignore", category=DeprecationWarning) @@ -322,7 +322,7 @@ class Snippet(object): self._matched = "" # Don't expand on whitespace - if trigger and trigger[-1] in string.whitespace: + if trigger and trigger.rstrip() is not trigger: return False words = self._words_for_line(trigger) @@ -360,7 +360,7 @@ class Snippet(object): self._matched = "" # Don't expand on whitespace - if trigger and trigger[-1] in string.whitespace: + if trigger and trigger.rstrip() is not trigger: return False words = self._words_for_line(trigger) @@ -943,7 +943,7 @@ class SnippetManager(object): line = vim.current.line # Get the word to the left of the current edit position - before, after = line[:col], line[col:] + before, after = as_unicode(line[:col]), as_unicode(line[col:]) return before, after @@ -982,7 +982,7 @@ class SnippetManager(object): try: # let vim_string format it as a vim list - rv = vim.eval("inputlist(%s)" % vim_string(display)) + rv = vim.eval(make_suitable_for_vim(as_unicode("inputlist(%s)") % vim_string(display))) if rv is None or rv == '0': return None rv = int(rv) diff --git a/test.py b/test.py index aa1f52c..9825880 100755 --- a/test.py +++ b/test.py @@ -37,8 +37,6 @@ WIN = platform.system() == "Windows" from textwrap import dedent - - # Some constants for better reading BS = '\x7f' ESC = '\x1b' @@ -131,12 +129,12 @@ def send_win(keys, session): ################ end windows ################ - - - -def send_screen(s,session): +def send_screen(s, session): s = s.replace("'", r"'\''") - os.system(("screen -x %s -X stuff '%s'" % (session, s)).encode("utf-8")) + cmd = "screen -x %s -X stuff '%s'" % (session, s) + if sys.version_info >= (3,0): + cmd = cmd.encode("utf-8") + os.system(cmd) def send(s, session): @@ -149,7 +147,7 @@ def focus(title=None): if WIN: focus_win(title=title) -def type(str, session, sleeptime): +def send_keystrokes(str, session, sleeptime): """ Send the keystrokes to vim via screen. Pause after each char, so vim can handle this @@ -182,8 +180,8 @@ class _VimTest(unittest.TestCase): else: self.send(":py3 << EOF\n%s\nEOF\n" % s) - def type(self,s): - type(s, self.session, self.sleeptime) + def send_keystrokes(self,s): + send_keystrokes(s, self.session, self.sleeptime) def check_output(self): wanted = self.text_before + '\n\n' + self.wanted + \ @@ -269,7 +267,7 @@ class _VimTest(unittest.TestCase): self.send("i") # Execute the command - self.type(self.keys) + self.send_keystrokes(self.keys) self.send(ESC)