From a2ac54fd578e263506b1bccd41bdf3dcf1b2cb09 Mon Sep 17 00:00:00 2001 From: Raphael Groner Date: Feb 28 2016 11:58:48 +0000 Subject: v2.2.3, now with sources --- diff --git a/.gitignore b/.gitignore index d702570..7f55bcd 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /qutepart-2.2.2.tar.gz +/qutepart-2.2.3.tar.gz diff --git a/qutepart-docs.patch b/qutepart-docs.patch deleted file mode 100644 index 3fb12e5..0000000 --- a/qutepart-docs.patch +++ /dev/null @@ -1,143 +0,0 @@ -From 39dc2a200a11a293611e995429c80c9b0fa545e7 Mon Sep 17 00:00:00 2001 -From: Andrei Kopats -Date: Mon, 19 Oct 2015 23:15:55 +0300 -Subject: [PATCH] docs: build fix - ---- - qutepart/__init__.py | 33 ++++++++++++++++++++------------- - qutepart/rectangularselection.py | 5 ++--- - qutepart/syntaxhlighter.py | 12 +++++++----- - 3 files changed, 29 insertions(+), 21 deletions(-) - -diff --git a/qutepart/__init__.py b/qutepart/__init__.py -index 298b3e5..4f05ab3 100644 ---- a/qutepart/__init__.py -+++ b/qutepart/__init__.py -@@ -2,6 +2,7 @@ - ========================================================= - """ - -+import sys - import os.path - import logging - import platform -@@ -15,7 +16,7 @@ - 'Use next code:\n\timport sip\n\tsip.setapi("QString", 2)\n'\ - 'before importing Qutepart' - --from PyQt4.QtCore import QRect, Qt, QEvent, pyqtSignal -+from PyQt4.QtCore import QRect, Qt, pyqtSignal - from PyQt4.QtGui import QAction, QApplication, QColor, QBrush, \ - QDialog, QFont, \ - QIcon, QKeySequence, QPainter, QPen, QPalette, \ -@@ -24,15 +25,24 @@ - QTextBlock, QTextEdit, QTextFormat - - from qutepart.syntax import SyntaxManager --from qutepart.syntaxhlighter import SyntaxHighlighter --from qutepart.brackethlighter import BracketHighlighter --from qutepart.completer import Completer --from qutepart.lines import Lines --from qutepart.rectangularselection import RectangularSelection --import qutepart.sideareas --from qutepart.indenter import Indenter --import qutepart.vim --import qutepart.bookmarks -+ -+if 'sphinx-build' not in sys.argv[0]: -+ """When building documentation on rtfd.org, Qt is not available and is mocked -+ in conf.py. But mocked Qt doesn't allow to create some global variables. -+ Therefore this code is not executed when building docs -+ """ -+ from qutepart.syntaxhlighter import SyntaxHighlighter -+ from qutepart.brackethlighter import BracketHighlighter -+ from qutepart.completer import Completer -+ from qutepart.lines import Lines -+ from qutepart.rectangularselection import RectangularSelection -+ import qutepart.sideareas -+ from qutepart.indenter import Indenter -+ import qutepart.vim -+ import qutepart.bookmarks -+ -+ def setPositionInBlock(cursor, positionInBlock, anchor=QTextCursor.MoveAnchor): -+ return cursor.setPosition(cursor.block().position() + positionInBlock, anchor) - - - -@@ -65,9 +75,6 @@ def _positionInBlock(cursor): - QTextCursor.positionInBlock = _positionInBlock - - --def setPositionInBlock(cursor, positionInBlock, anchor=QTextCursor.MoveAnchor): -- return cursor.setPosition(cursor.block().position() + positionInBlock, anchor) -- - - class Qutepart(QPlainTextEdit): - '''Qutepart is based on QPlainTextEdit, and you can use QPlainTextEdit methods, -diff --git a/qutepart/rectangularselection.py b/qutepart/rectangularselection.py -index 74d0004..f37c086 100644 ---- a/qutepart/rectangularselection.py -+++ b/qutepart/rectangularselection.py -@@ -1,5 +1,5 @@ - from PyQt4.QtCore import Qt, QMimeData --from PyQt4.QtGui import QApplication, QKeyEvent, QKeySequence, QPalette, QTextCursor, QTextEdit, QWidget -+from PyQt4.QtGui import QApplication, QKeyEvent, QKeySequence, QPalette, QTextCursor, QTextEdit - - - class RectangularSelection: -@@ -10,10 +10,9 @@ class RectangularSelection: - MIME_TYPE = 'text/rectangular-selection' - - # any of this modifiers with mouse select text -- # if hasattr(Qt, 'AltModifier') to make the docs buildable on rtfd.org - MOUSE_MODIFIERS = (Qt.AltModifier | Qt.ControlModifier, - Qt.AltModifier | Qt.ShiftModifier, -- Qt.AltModifier) if hasattr(Qt, 'AltModifier') else None -+ Qt.AltModifier) - - _MAX_SIZE = 256 - -diff --git a/qutepart/syntaxhlighter.py b/qutepart/syntaxhlighter.py -index 7d2f065..6d3b9dc 100644 ---- a/qutepart/syntaxhlighter.py -+++ b/qutepart/syntaxhlighter.py -@@ -4,18 +4,18 @@ - - import time - -- - from PyQt4.QtCore import QObject, QTimer - from PyQt4.QtGui import QApplication, QBrush, QColor, QFont, \ - QTextBlockUserData, QTextCharFormat, QTextLayout - - import qutepart.syntax - --"""PyQt does not define proper comparison for QTextLayout.FormatRange --Define it to check correctly, if formats has changed. --It is important for the performance --""" -+ - def _cmpFormatRanges(a, b): -+ """PyQt does not define proper comparison for QTextLayout.FormatRange -+ Define it to check correctly, if formats has changed. -+ It is important for the performance -+ """ - if a.format == b.format and \ - a.start == b.start and \ - a.length == b.length: -@@ -23,6 +23,7 @@ def _cmpFormatRanges(a, b): - else: - return cmp(id(a), id(b)) - -+ - def _formatRangeListsEqual(a, b): - if len(a) != len(b): - return False -@@ -88,6 +89,7 @@ def _onTimer(self): - """ - _gLastChangeTime = -777. - -+ - class SyntaxHighlighter(QObject): - - # when initially parsing text, it is better, if highlighted text is drawn without flickering diff --git a/qutepart-py3-b4368fd3c0cee1c885fac9382608e1283801064b.patch b/qutepart-py3-b4368fd3c0cee1c885fac9382608e1283801064b.patch new file mode 100644 index 0000000..35ade30 --- /dev/null +++ b/qutepart-py3-b4368fd3c0cee1c885fac9382608e1283801064b.patch @@ -0,0 +1,2130 @@ +From d0766307a3b9fbcb919f323cfe6a5d131920f6ea Mon Sep 17 00:00:00 2001 +From: Andrei Kopats +Date: Sun, 22 Jun 2014 11:42:09 +0300 +Subject: [PATCH 1/4] Use Python 3 + +--- + README.md | 4 +- + debian/control | 7 +- + doc/source/conf.py | 16 ++-- + editor.py | 4 +- + profiling/typing_performance_test.py | 12 +-- + qutepart/__init__.py | 12 +-- + qutepart/indenter/cstyle.py | 2 +- + qutepart/lines.py | 2 +- + qutepart/rectangularselection.py | 13 +-- + qutepart/syntax/__init__.py | 2 +- + qutepart/syntax/data/regenerate-definitions-db.py | 4 +- + qutepart/syntax/loader.py | 28 +++--- + qutepart/syntax/parser.py | 10 +-- + qutepart/vim.py | 8 +- + rpm/python-qutepart.spec | 16 ++-- + setup.py | 29 +++---- + tests/run_all.py | 2 +- + tests/test_actions.py | 2 +- + tests/test_api.py | 100 +++++++++++----------- + tests/test_bookmarks.py | 2 +- + tests/test_bracket_hlighter.py | 2 +- + tests/test_completion.py | 2 +- + tests/test_draw_whitespace.py | 6 +- + tests/test_edit.py | 2 +- + tests/test_indent.py | 2 +- + tests/test_indenter/indenttest.py | 2 +- + tests/test_indenter/test_cstyle.py | 5 +- + tests/test_indenter/test_haskell.py | 6 +- + tests/test_indenter/test_lisp.py | 5 +- + tests/test_indenter/test_normal.py | 5 +- + tests/test_indenter/test_python.py | 5 +- + tests/test_indenter/test_ruby.py | 5 +- + tests/test_indenter/test_scheme.py | 5 +- + tests/test_indenter/test_xmlindent.py | 5 +- + tests/test_rectangular_selection.py | 6 +- + tests/test_syntax/test_dynamic_substitution.py | 2 +- + tests/test_syntax/test_rules.py | 18 ++-- + tests/test_syntax/test_xml_definition_parsing.py | 2 +- + tests/test_vim.py | 4 +- + tools/show-syntax.py | 6 +- + 40 files changed, 197 insertions(+), 173 deletions(-) + +diff --git a/README.md b/README.md +index afcd017..567ace2 100644 +--- a/README.md ++++ b/README.md +@@ -20,7 +20,7 @@ Component has been created for [Enki editor](http://enki-editor.org) as replacem + + Qutepart depends on: + +-* Python 2.7 ++* Python 3 + * PyQt4 (see *Known problems* section) + * pcre + +@@ -29,7 +29,7 @@ On Debian, Ubuntu and other Linuxes install package `libpcreX-dev`, where `X` is + For other OSes - see instructions on pcre website + + #### 2. Install Python development files +-On Debian, Ubuntu and other Linuxes install package `python-dev`, on other systems - see Python website ++On Debian, Ubuntu and other Linuxes install package `python3-dev`, on other systems - see Python website + + #### 3. Install C compiler + It will probably be gcc +diff --git a/debian/control b/debian/control +index 492fbbe..2178580 100644 +--- a/debian/control ++++ b/debian/control +@@ -2,16 +2,15 @@ Source: python-qutepart + Maintainer: Andrei Kopats + Section: python + Priority: optional +-Build-Depends: debhelper (>= 7.4.1), python2.7, libpcre3-dev, python2.7-dev, python-qt4, python-setuptools +-X-Python-Version: >= 2.7 ++Build-Depends: debhelper (>= 7.4.1), python3, libpcre3-dev, python3-dev, python3-pyqt4 + Standards-Version: 3.9.4 + Homepage: https://github.com/hlamer/qutepart + Vcs-Git: https://github.com/hlamer/qutepart + Vcs-Browser: https://github.com/hlamer/qutepart + +-Package: python-qutepart ++Package: python3-qutepart + Architecture: any +-Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, python-qt4, libpcre3 ++Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, python3-pyqt4, libpcre3 + Description: Code editor component + Some of the features: + * Syntax highlighting for 196 languages +diff --git a/doc/source/conf.py b/doc/source/conf.py +index 7f81195..1eff823 100644 +--- a/doc/source/conf.py ++++ b/doc/source/conf.py +@@ -75,8 +75,8 @@ def __getattr__(self, name): + master_doc = 'index' + + # General information about the project. +-project = u'qutepart' +-copyright = u'2013, Andrei Kopats' ++project = 'qutepart' ++copyright = '2013, Andrei Kopats' + + # The version info for the project you're documenting, acts as replacement for + # |version| and |release|, also used in various other places throughout the +@@ -219,8 +219,8 @@ def __getattr__(self, name): + # Grouping the document tree into LaTeX files. List of tuples + # (source start file, target name, title, author, documentclass [howto/manual]). + latex_documents = [ +- ('index', 'qutepart.tex', u'qutepart Documentation', +- u'Andrei Kopats', 'manual'), ++ ('index', 'qutepart.tex', 'qutepart Documentation', ++ 'Andrei Kopats', 'manual'), + ] + + # The name of an image file (relative to this directory) to place at the top of +@@ -251,8 +251,8 @@ def __getattr__(self, name): + # One entry per manual page. List of tuples + # (source start file, name, description, authors, manual section). + man_pages = [ +- ('index', 'qutepart', u'qutepart Documentation', +- [u'Andrei Kopats'], 1) ++ ('index', 'qutepart', 'qutepart Documentation', ++ ['Andrei Kopats'], 1) + ] + + # If true, show URL addresses after external links. +@@ -265,8 +265,8 @@ def __getattr__(self, name): + # (source start file, target name, title, author, + # dir menu entry, description, category) + texinfo_documents = [ +- ('index', 'qutepart', u'qutepart Documentation', +- u'Andrei Kopats', 'qutepart', 'One line description of project.', ++ ('index', 'qutepart', 'qutepart Documentation', ++ 'Andrei Kopats', 'qutepart', 'One line description of project.', + 'Miscellaneous'), + ] + +diff --git a/editor.py b/editor.py +index ba7c411..a4ba78e 100755 +--- a/editor.py ++++ b/editor.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import sys + import os +@@ -38,7 +38,7 @@ def main(): + import qutepart # after correct sys.path has been set + + with open(ns.file) as file: +- text = unicode(file.read(), 'utf8') ++ text = file.read() + + if ns.debug: + logging.getLogger('qutepart').setLevel(logging.DEBUG) +diff --git a/profiling/typing_performance_test.py b/profiling/typing_performance_test.py +index 2812272..427ad52 100755 +--- a/profiling/typing_performance_test.py ++++ b/profiling/typing_performance_test.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import sys + import time +@@ -17,7 +17,7 @@ + + q = qutepart.Qutepart() + q.detectSyntax(sourceFilePath=sys.argv[1]) +-print 'Language:', q.language() ++print('Language:', q.language()) + + q.showMaximized() + +@@ -29,7 +29,7 @@ + def click(key): + clockBefore = time.clock() + +- if isinstance(key, basestring): ++ if isinstance(key, str): + QTest.keyClicks(q, key) + else: + QTest.keyClick(q, key) +@@ -56,11 +56,11 @@ def doTest(): + + clockAfter = time.clock() + typingTime = clockAfter - clockBefore +- print 'Typed {} chars in {} sec. {} ms per character'.format(len(text), typingTime, typingTime * 1000 / len(text)) +- print 'Time per click: count of clicks' ++ print('Typed {} chars in {} sec. {} ms per character'.format(len(text), typingTime, typingTime * 1000 / len(text))) ++ print('Time per click: count of clicks') + clickTimeKeys = sorted(clickTimes.keys()) + for ckt in clickTimeKeys: +- print ' %5dms: %4d' % (ckt, clickTimes[ckt]) ++ print(' %5dms: %4d' % (ckt, clickTimes[ckt])) + + app.quit() + +diff --git a/qutepart/__init__.py b/qutepart/__init__.py +index f29d932..4abde15 100644 +--- a/qutepart/__init__.py ++++ b/qutepart/__init__.py +@@ -233,12 +233,12 @@ class Qutepart(QPlainTextEdit): + **Public methods** + ''' + +- userWarning = pyqtSignal(unicode) +- languageChanged = pyqtSignal(unicode) ++ userWarning = pyqtSignal(str) ++ languageChanged = pyqtSignal(str) + indentWidthChanged = pyqtSignal(int) + indentUseTabsChanged = pyqtSignal(bool) +- eolChanged = pyqtSignal(unicode) +- vimModeIndicationChanged = pyqtSignal(QColor, unicode) ++ eolChanged = pyqtSignal(str) ++ vimModeIndicationChanged = pyqtSignal(QColor, str) + vimModeEnabledChanged = pyqtSignal(bool) + + LINT_ERROR = 'e' +@@ -446,7 +446,7 @@ def lines(self): + @lines.setter + def lines(self, value): + if not isinstance(value, (list, tuple)) or \ +- not all([isinstance(item, basestring) for item in value]): ++ not all([isinstance(item, str) for item in value]): + raise TypeError('Invalid new value of "lines" attribute') + self.setPlainText('\n'.join(value)) + +@@ -479,7 +479,7 @@ def selectedText(self): + text = self.textCursor().selectedText() + + # replace unicode paragraph separator with habitual \n +- text = text.replace(u'\u2029', '\n') ++ text = text.replace('\u2029', '\n') + + return text + +diff --git a/qutepart/indenter/cstyle.py b/qutepart/indenter/cstyle.py +index f1f38fb..a470838 100644 +--- a/qutepart/indenter/cstyle.py ++++ b/qutepart/indenter/cstyle.py +@@ -20,7 +20,7 @@ + + def dbg(*args): + if (DEBUG_MODE): +- print args ++ print(args) + + #global variables and functions + +diff --git a/qutepart/lines.py b/qutepart/lines.py +index 537dd49..f1c1482 100644 +--- a/qutepart/lines.py ++++ b/qutepart/lines.py +@@ -137,7 +137,7 @@ def __init__(self, block): + def __iter__(self): + return self + +- def next(self): ++ def __next__(self): + if self._block.isValid(): + self._block, result = self._block.next(), self._block.text() + return result +diff --git a/qutepart/rectangularselection.py b/qutepart/rectangularselection.py +index f37c086..8f40014 100644 +--- a/qutepart/rectangularselection.py ++++ b/qutepart/rectangularselection.py +@@ -81,7 +81,7 @@ def _visibleCharPositionGenerator(self, text): + if char == '\t': + currentPos += self._qpart.indentWidth + # trim reminder. If width('\t') == 4, width('abc\t') == 4 +- currentPos = currentPos / self._qpart.indentWidth * self._qpart.indentWidth ++ currentPos = currentPos // self._qpart.indentWidth * self._qpart.indentWidth + else: + currentPos += 1 + yield currentPos +@@ -92,8 +92,9 @@ def _realToVisibleColumn(self, text, realColumn): + """ + generator = self._visibleCharPositionGenerator(text) + for i in range(realColumn): +- val = generator.next() +- return generator.next() ++ val = next(generator) ++ val = next(generator) ++ return val + + def _visibleToRealColumn(self, text, visiblePos): + """If \t is used, real position of symbol in block and visible position differs +@@ -202,10 +203,10 @@ def _indentUpTo(self, text, width): + return '' + elif self._qpart.indentUseTabs and \ + all([char == '\t' for char in text]): # if using tabs and only tabs in text +- return '\t' * (diff / self._qpart.indentWidth) + \ ++ return '\t' * (diff // self._qpart.indentWidth) + \ + ' ' * (diff % self._qpart.indentWidth) + else: +- return ' ' * diff ++ return ' ' * int(diff) + + def paste(self, mimeData): + """Paste recrangular selection. +@@ -216,7 +217,7 @@ def paste(self, mimeData): + elif self._qpart.textCursor().hasSelection(): + self._qpart.textCursor().deleteChar() + +- text = str(mimeData.data(self.MIME_TYPE)).decode('utf8') ++ text = bytes(mimeData.data(self.MIME_TYPE)).decode('utf8') + lines = text.splitlines() + cursorLine, cursorCol = self._qpart.cursorPosition + if cursorLine + len(lines) > len(self._qpart.lines): +diff --git a/qutepart/syntax/__init__.py b/qutepart/syntax/__init__.py +index 24b59fd..ca34c40 100644 +--- a/qutepart/syntax/__init__.py ++++ b/qutepart/syntax/__init__.py +@@ -82,7 +82,7 @@ def __str__(self): + res += ' license: %s\n' % self.license + res += ' hidden: %s\n' % self.hidden + res += ' indenter: %s\n' % self.indenter +- res += unicode(self.parser) ++ res += str(self.parser) + + return res + +diff --git a/qutepart/syntax/data/regenerate-definitions-db.py b/qutepart/syntax/data/regenerate-definitions-db.py +index c4cc525..1d649cf 100755 +--- a/qutepart/syntax/data/regenerate-definitions-db.py ++++ b/qutepart/syntax/data/regenerate-definitions-db.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import os.path + import json +@@ -81,7 +81,7 @@ def main(): + with open('syntax_db.json', 'w') as syntaxDbFile: + json.dump(result, syntaxDbFile, sort_keys=True, indent=4) + +- print 'Done. Do not forget to commit the changes' ++ print('Done. Do not forget to commit the changes') + + if __name__ == '__main__': + main() +diff --git a/qutepart/syntax/loader.py b/qutepart/syntax/loader.py +index 5b2b489..63226f5 100644 +--- a/qutepart/syntax/loader.py ++++ b/qutepart/syntax/loader.py +@@ -93,7 +93,7 @@ def _parseBoolAttribute(value): + + def _safeGetRequiredAttribute(xmlElement, name, default): + if name in xmlElement.attrib: +- return unicode(xmlElement.attrib[name]) ++ return str(xmlElement.attrib[name]) + else: + _logger.warning("Required attribute '%s' is not set for element '%s'", name, xmlElement.tag) + return default +@@ -233,7 +233,7 @@ def _loadDetectChar(parentContext, xmlElement, attributeToFormatMap, formatConve + _logger.warning('Too little DetectChar index %d', index) + index = 0 + +- return _parserModule.DetectChar(abstractRuleParams, unicode(char), index) ++ return _parserModule.DetectChar(abstractRuleParams, str(char), index) + + def _loadDetect2Chars(parentContext, xmlElement, attributeToFormatMap, formatConverterFunction): + char = _safeGetRequiredAttribute(xmlElement, 'char', None) +@@ -286,11 +286,11 @@ def _processCraracterCodes(text): + i.e. \0377 is character with code 255 in the unicode table + Convert such notation to unicode text + """ +- text = unicode(text) ++ text = str(text) + def replFunc(matchObj): + matchText = matchObj.group(0) +- charCode = eval(matchText[1:]) +- return chr(charCode).decode('latin1') ++ charCode = eval('0o' + matchText[2:]) ++ return chr(charCode) + return re.sub(r"\\0\d\d\d", replFunc, text) + + insensitive = _parseBoolAttribute(xmlElement.attrib.get('insensitive', 'false')) +@@ -365,8 +365,8 @@ def _loadContexts(highlightingElement, parser, attributeToFormatMap, formatConve + contextList = [] + for xmlElement in xmlElementList: + name = _safeGetRequiredAttribute(xmlElement, +- u'name', +- u'Error: context name is not set!!!') ++ 'name', ++ 'Error: context name is not set!!!') + context = _parserModule.Context(parser, name) + contextList.append(context) + +@@ -448,7 +448,7 @@ def _makeFormat(defaultTheme, defaultStyleName, textType, item=None): + + if item is not None: + caseInsensitiveAttributes = {} +- for key, value in item.attrib.iteritems(): ++ for key, value in item.attrib.items(): + caseInsensitiveAttributes[key.lower()] = value.lower() + + if 'color' in caseInsensitiveAttributes: +@@ -503,7 +503,7 @@ def _loadLists(root, highlightingElement): + lists = {} # list name: list + for listElement in highlightingElement.findall('list'): + # Sometimes item.text is none. Broken xml files +- items = [unicode(item.text.strip()) \ ++ items = [str(item.text.strip()) \ + for item in listElement.findall('item') \ + if item.text is not None] + name = _safeGetRequiredAttribute(listElement, 'name', 'Error: list name is not set!!!') +@@ -520,9 +520,9 @@ def _makeKeywordsLowerCase(listDict): + def _loadSyntaxDescription(root, syntax): + syntax.name = _safeGetRequiredAttribute(root, 'name', 'Error: .parser name is not set!!!') + syntax.section = _safeGetRequiredAttribute(root, 'section', 'Error: Section is not set!!!') +- syntax.extensions = filter(None, _safeGetRequiredAttribute(root, 'extensions', '').split(';')) +- syntax.firstLineGlobs = filter(None, root.attrib.get('firstLineGlobs', '').split(';')) +- syntax.mimetype = filter(None, root.attrib.get('mimetype', '').split(';')) ++ syntax.extensions = [_f for _f in _safeGetRequiredAttribute(root, 'extensions', '').split(';') if _f] ++ syntax.firstLineGlobs = [_f for _f in root.attrib.get('firstLineGlobs', '').split(';') if _f] ++ syntax.mimetype = [_f for _f in root.attrib.get('mimetype', '').split(';') if _f] + syntax.version = root.attrib.get('version', None) + syntax.kateversion = root.attrib.get('kateversion', None) + syntax.priority = int(root.attrib.get('priority', '0')) +@@ -540,7 +540,7 @@ def loadSyntax(syntax, filePath, formatConverterFunction = None): + try: + root = xml.etree.ElementTree.parse(definitionFile).getroot() + except Exception as ex: +- print >> sys.stderr, 'When opening %s:' % filePath ++ print('When opening %s:' % filePath, file=sys.stderr) + raise + + highlightingElement = root.find('highlighting') +@@ -579,7 +579,7 @@ def loadSyntax(syntax, filePath, formatConverterFunction = None): + 'mode' in indentationElement.attrib: + syntax.indenter = indentationElement.attrib['mode'] + +- deliminatorSetAsString = u''.join(list(deliminatorSet)) ++ deliminatorSetAsString = ''.join(list(deliminatorSet)) + debugOutputEnabled = _logger.isEnabledFor(logging.DEBUG) # for cParser + parser = _parserModule.Parser(syntax, deliminatorSetAsString, lists, keywordsCaseSensitive, debugOutputEnabled) + syntax._setParser(parser) +diff --git a/qutepart/syntax/parser.py b/qutepart/syntax/parser.py +index e1ad239..b7de532 100644 +--- a/qutepart/syntax/parser.py ++++ b/qutepart/syntax/parser.py +@@ -819,7 +819,7 @@ def __str__(self): + res += '\t\t%s: %s\n' % ('dynamic', self.dynamic) + + for rule in self.rules: +- res += unicode(rule) ++ res += str(rule) + return res + + def parseBlock(self, contextStack, currentColumnIndex, text): +@@ -917,8 +917,8 @@ def __str__(self): + """Serialize. + For debug logs + """ +- res = u'Parser\n' +- for name, value in vars(self).iteritems(): ++ res = 'Parser\n' ++ for name, value in vars(self).items(): + if not name.startswith('_') and \ + not name in ('defaultContext', 'deliminatorSet', 'contexts', 'lists', 'syntax') and \ + not value is None: +@@ -926,12 +926,12 @@ def __str__(self): + + res += '\tDefault context: %s\n' % self.defaultContext.name + +- for listName, listValue in self.lists.iteritems(): ++ for listName, listValue in self.lists.items(): + res += '\tList %s: %s\n' % (listName, listValue) + + + for context in self.contexts.values(): +- res += unicode(context) ++ res += str(context) + + return res + +diff --git a/qutepart/vim.py b/qutepart/vim.py +index b7f9968..fa74e3f 100644 +--- a/qutepart/vim.py ++++ b/qutepart/vim.py +@@ -76,7 +76,7 @@ class Vim(QObject): + """Vim mode implementation. + Listens events and does actions + """ +- modeIndicationChanged = pyqtSignal(QColor, unicode) ++ modeIndicationChanged = pyqtSignal(QColor, str) + + internalClipboard = '' # delete commands save text to this clipboard + +@@ -266,7 +266,7 @@ def text(self): + + def _reset(self): + self._processCharCoroutine = self._processChar() +- self._processCharCoroutine.next() # run until the first yield ++ next(self._processCharCoroutine) # run until the first yield + self._typedText = '' + + _MOTIONS = (_0, _Home, +@@ -684,7 +684,7 @@ def cmdInternalPaste(self, cmd): + else: + cursor.removeSelectedText() + +- if isinstance(Vim.internalClipboard, basestring): ++ if isinstance(Vim.internalClipboard, str): + self._qpart.textCursor().insertText(Vim.internalClipboard) + elif isinstance(Vim.internalClipboard, list): + currentLineIndex = self._qpart.cursorPosition[0] +@@ -1020,7 +1020,7 @@ def cmdInternalPaste(self, cmd, count): + if not Vim.internalClipboard: + return + +- if isinstance(Vim.internalClipboard, basestring): ++ if isinstance(Vim.internalClipboard, str): + cursor = self._qpart.textCursor() + if cmd == _p: + cursor.movePosition(QTextCursor.Right) +diff --git a/rpm/python-qutepart.spec b/rpm/python-qutepart.spec +index 7613a4b..5175920 100644 +--- a/rpm/python-qutepart.spec ++++ b/rpm/python-qutepart.spec +@@ -1,4 +1,4 @@ +-Name: python-qutepart ++Name: python3-qutepart + Version: 2.2.3 + Release: 1%{?dist} + Summary: Code editor widget for PyQt +@@ -10,18 +10,18 @@ URL: https://github.com/hlamer/qutepart + Source0: https://github.com/hlamer/qutepart/archive/v%{version}.tar.gz#/qutepart-%{version}.tar.gz + + BuildRequires: pcre-devel +-BuildRequires: python-devel +-BuildRequires: python-setuptools +-Requires: python >= 2.7 ++BuildRequires: python3-devel ++BuildRequires: python3-setuptools ++Requires: python3 + Requires: pcre + + + %if 0%{?fedora_version} +-BuildRequires: PyQt4 +-Requires: PyQt4 ++BuildRequires: python3-PyQt4 ++Requires: python3-PyQt4 + %else +-BuildRequires: python-qt4 +-Requires: python-qt4 ++BuildRequires: python3-qt4 ++Requires: python3-qt4 + %endif + + +diff --git a/setup.py b/setup.py +index fbd77dd..d0cdb80 100755 +--- a/setup.py ++++ b/setup.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import sys + import os +@@ -63,19 +63,19 @@ def _checkDependencies(): + There should be better way to check, if C compiler is installed + """ + if not compiler.has_function('rand', includes = ['stdlib.h']): +- print "It seems like C compiler is not installed or not operable." ++ print("It seems like C compiler is not installed or not operable.") + return False + + if not compiler.has_function('rand', + includes = ['stdlib.h', 'Python.h'], + include_dirs=[distutils.sysconfig.get_python_inc()], + library_dirs=[os.path.join(os.path.dirname(sys.executable), 'libs')]): +- print "Failed to find Python headers." +- print "Try to install python-dev package" +- print "If not standard directories are used, pass parameters" +- print "\tpython setup.py install --lib-dir=c://github/pcre-8.32/build/Release --include-dir=c://github/pcre-8.32/build" +- print "\tpython setup.py install --lib-dir=/usr/local/lib --include-dir=/usr/local/include" +- print "--lib-dir= and --include-dir= may be used multiple times" ++ print("Failed to find Python headers.") ++ print("Try to install python-dev package") ++ print("If not standard directories are used, pass parameters") ++ print("\tpython setup.py install --lib-dir=c://github/pcre-8.32/build/Release --include-dir=c://github/pcre-8.32/build") ++ print("\tpython setup.py install --lib-dir=/my/local/lib --include-dir=/my/local/include") ++ print("--lib-dir= and --include-dir= may be used multiple times") + return False + + if not compiler.has_function('pcre_version', +@@ -83,13 +83,12 @@ def _checkDependencies(): + libraries = ['pcre'], + include_dirs=include_dirs, + library_dirs=library_dirs): +- print "Failed to find pcre library." +- print "Try to install libpcre{version}-dev package, or go to http://pcre.org" +- print "If not standard directories are used, pass parameters:" +- print "\tpython setup.py install --lib-dir=c://github/pcre-8.32/build/Release --include-dir=c://github/pcre-8.32/build" +- print "or" +- print "\tpython setup.py install --lib-dir=/my/local/lib --include-dir=/my/local/include" +- print "--lib-dir= and --include-dir= may be used multiple times" ++ print("Failed to find pcre library.") ++ print("Try to install libpcre{version}-dev package, or go to http://pcre.org") ++ print("If not standard directories are used, pass parameters:") ++ print("\tpython setup.py install --lib-dir=c://github/pcre-8.32/build/Release --include-dir=c://github/pcre-8.32/build") ++ print("\tpython setup.py install --lib-dir=/my/local/lib --include-dir=/my/local/include") ++ print("--lib-dir= and --include-dir= may be used multiple times") + return False + + return True +diff --git a/tests/run_all.py b/tests/run_all.py +index 3578404..5c9a4d2 100755 +--- a/tests/run_all.py ++++ b/tests/run_all.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import unittest + import base +diff --git a/tests/test_actions.py b/tests/test_actions.py +index e5b20e2..9b65857 100755 +--- a/tests/test_actions.py ++++ b/tests/test_actions.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import os + import sys +diff --git a/tests/test_api.py b/tests/test_api.py +index 2eff704..09efda6 100755 +--- a/tests/test_api.py ++++ b/tests/test_api.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import os + import sys +@@ -41,50 +41,50 @@ def test_setSelection(self): + + self.qpart.selectedPosition = ((0, 3), (0, 7)) + +- self.assertEquals(self.qpart.selectedText, "f fd") +- self.assertEquals(self.qpart.selectedPosition, ((0, 3), (0, 7))) ++ self.assertEqual(self.qpart.selectedText, "f fd") ++ self.assertEqual(self.qpart.selectedPosition, ((0, 3), (0, 7))) + + def test_selected_multiline_text(self): + self.qpart.text = "a\nb" + self.qpart.selectedPosition = ((0, 0), (1, 1)) +- self.assertEquals(self.qpart.selectedText, "a\nb") ++ self.assertEqual(self.qpart.selectedText, "a\nb") + + class ReplaceText(_BaseTest): + def test_replaceText1(self): + # Basic case + self.qpart.text = '123456789' + self.qpart.replaceText(3, 4, 'xyz') +- self.assertEquals(self.qpart.text, '123xyz89') ++ self.assertEqual(self.qpart.text, '123xyz89') + + def test_replaceText2(self): + # Replace uses (line, col) position + self.qpart.text = '12345\n67890\nabcde' + self.qpart.replaceText((1, 4), 3, 'Z') +- self.assertEquals(self.qpart.text, '12345\n6789Zbcde') ++ self.assertEqual(self.qpart.text, '12345\n6789Zbcde') + + def test_replaceText3(self): + # Edge cases + self.qpart.text = '12345\n67890\nabcde' + self.qpart.replaceText((0, 0), 3, 'Z') +- self.assertEquals(self.qpart.text, 'Z45\n67890\nabcde') ++ self.assertEqual(self.qpart.text, 'Z45\n67890\nabcde') + + self.qpart.text = '12345\n67890\nabcde' + self.qpart.replaceText((2, 4), 1, 'Z') +- self.assertEquals(self.qpart.text, '12345\n67890\nabcdZ') ++ self.assertEqual(self.qpart.text, '12345\n67890\nabcdZ') + + self.qpart.text = '12345\n67890\nabcde' + self.qpart.replaceText((0, 0), 0, 'Z') +- self.assertEquals(self.qpart.text, 'Z12345\n67890\nabcde') ++ self.assertEqual(self.qpart.text, 'Z12345\n67890\nabcde') + + self.qpart.text = '12345\n67890\nabcde' + self.qpart.replaceText((2, 5), 0, 'Z') +- self.assertEquals(self.qpart.text, '12345\n67890\nabcdeZ') ++ self.assertEqual(self.qpart.text, '12345\n67890\nabcdeZ') + + def test_replaceText4(self): + # Replace nothing with something + self.qpart.text = '12345\n67890\nabcde' + self.qpart.replaceText(2, 0, 'XYZ') +- self.assertEquals(self.qpart.text, '12XYZ345\n67890\nabcde') ++ self.assertEqual(self.qpart.text, '12XYZ345\n67890\nabcde') + + def test_replaceText5(self): + # Make sure exceptions are raised for invalid params +@@ -101,23 +101,23 @@ def test_1(self): + # Basic case + self.qpart.text = '123456789' + self.qpart.insertText(3, 'xyz') +- self.assertEquals(self.qpart.text, '123xyz456789') ++ self.assertEqual(self.qpart.text, '123xyz456789') + + def test_2(self): + # (line, col) position + self.qpart.text = '12345\n67890\nabcde' + self.qpart.insertText((1, 4), 'Z') +- self.assertEquals(self.qpart.text, '12345\n6789Z0\nabcde') ++ self.assertEqual(self.qpart.text, '12345\n6789Z0\nabcde') + + def test_3(self): + # Edge cases + self.qpart.text = '12345\n67890\nabcde' + self.qpart.insertText((0, 0), 'Z') +- self.assertEquals(self.qpart.text, 'Z12345\n67890\nabcde') ++ self.assertEqual(self.qpart.text, 'Z12345\n67890\nabcde') + + self.qpart.text = '12345\n67890\nabcde' + self.qpart.insertText((2, 5), 'Z') +- self.assertEquals(self.qpart.text, '12345\n67890\nabcdeZ') ++ self.assertEqual(self.qpart.text, '12345\n67890\nabcdeZ') + + + class IsCodeOrComment(_BaseTest): +@@ -129,10 +129,10 @@ def test_1(self): + self.qpart.text = 'a + b # comment' + self.qpart.detectSyntax(language = 'Python') + self._wait_highlighting_finished() +- self.assertEquals([self.qpart.isCode(0, i) for i in range(len(self.qpart.text))], ++ self.assertEqual([self.qpart.isCode(0, i) for i in range(len(self.qpart.text))], + [True, True, True, True, True, True, False, False, False, False, \ + False, False, False, False, False]) +- self.assertEquals([self.qpart.isComment(0, i) for i in range(len(self.qpart.text))], ++ self.assertEqual([self.qpart.isComment(0, i) for i in range(len(self.qpart.text))], + [False, False, False, False, False, False, True, True, True, True, \ + True, True, True, True, True]) + +@@ -168,19 +168,19 @@ def test_here_doc(self): + class DetectSyntax(_BaseTest): + def test_1(self): + self.qpart.detectSyntax(xmlFileName='ada.xml') +- self.assertEquals(self.qpart.language(), 'Ada') ++ self.assertEqual(self.qpart.language(), 'Ada') + + self.qpart.detectSyntax(mimeType='text/x-cgsrc') +- self.assertEquals(self.qpart.language(), 'Cg') ++ self.assertEqual(self.qpart.language(), 'Cg') + + self.qpart.detectSyntax(language='CSS') +- self.assertEquals(self.qpart.language(), 'CSS') ++ self.assertEqual(self.qpart.language(), 'CSS') + + self.qpart.detectSyntax(sourceFilePath='/tmp/file.feh') +- self.assertEquals(self.qpart.language(), 'ferite') ++ self.assertEqual(self.qpart.language(), 'ferite') + + self.qpart.detectSyntax(firstLine='') +- self.assertEquals(self.qpart.language(), 'PHP (HTML)') ++ self.assertEqual(self.qpart.language(), 'PHP (HTML)') + + + class Signals(_BaseTest): +@@ -191,7 +191,7 @@ def setNeVal(val): + self.qpart.languageChanged.connect(setNeVal) + + self.qpart.detectSyntax(language='Python') +- self.assertEquals(newValue[0], 'Python') ++ self.assertEqual(newValue[0], 'Python') + + def test_indent_width_changed(self): + newValue = [None] +@@ -200,7 +200,7 @@ def setNeVal(val): + self.qpart.indentWidthChanged.connect(setNeVal) + + self.qpart.indentWidth = 7 +- self.assertEquals(newValue[0], 7) ++ self.assertEqual(newValue[0], 7) + + def test_use_tabs_changed(self): + newValue = [None] +@@ -210,7 +210,7 @@ def setNeVal(val): + self.qpart.indentUseTabsChanged.connect(setNeVal) + + self.qpart.indentUseTabs = True +- self.assertEquals(newValue[0], True) ++ self.assertEqual(newValue[0], True) + + def test_eol_changed(self): + newValue = [None] +@@ -220,7 +220,7 @@ def setNeVal(val): + self.qpart.eolChanged.connect(setNeVal) + + self.qpart.eol = '\r\n' +- self.assertEquals(newValue[0], '\r\n') ++ self.assertEqual(newValue[0], '\r\n') + + + class Completion(_BaseTest): +@@ -268,27 +268,27 @@ def setUp(self): + self.qpart.text = 'abcd\nefgh\nklmn\nopqr' + + def test_accessByIndex(self): +- self.assertEquals(self.qpart.lines[0], 'abcd') +- self.assertEquals(self.qpart.lines[1], 'efgh') +- self.assertEquals(self.qpart.lines[-1], 'opqr') ++ self.assertEqual(self.qpart.lines[0], 'abcd') ++ self.assertEqual(self.qpart.lines[1], 'efgh') ++ self.assertEqual(self.qpart.lines[-1], 'opqr') + + def test_modifyByIndex(self): + self.qpart.lines[2] = 'new text' +- self.assertEquals(self.qpart.text, 'abcd\nefgh\nnew text\nopqr') ++ self.assertEqual(self.qpart.text, 'abcd\nefgh\nnew text\nopqr') + + def test_getSlice(self): +- self.assertEquals(self.qpart.lines[0], 'abcd') +- self.assertEquals(self.qpart.lines[1], 'efgh') +- self.assertEquals(self.qpart.lines[3], 'opqr') +- self.assertEquals(self.qpart.lines[-4], 'abcd') +- self.assertEquals(self.qpart.lines[1:4], ['efgh', 'klmn', 'opqr']) +- self.assertEquals(self.qpart.lines[1:7], ['efgh', 'klmn', 'opqr']) # Python list behaves this way +- self.assertEquals(self.qpart.lines[0:0], []) +- self.assertEquals(self.qpart.lines[0:1], ['abcd']) +- self.assertEquals(self.qpart.lines[:2], ['abcd', 'efgh']) +- self.assertEquals(self.qpart.lines[0:-2], ['abcd', 'efgh']) +- self.assertEquals(self.qpart.lines[-2:], ['klmn', 'opqr']) +- self.assertEquals(self.qpart.lines[-4:-2], ['abcd', 'efgh']) ++ self.assertEqual(self.qpart.lines[0], 'abcd') ++ self.assertEqual(self.qpart.lines[1], 'efgh') ++ self.assertEqual(self.qpart.lines[3], 'opqr') ++ self.assertEqual(self.qpart.lines[-4], 'abcd') ++ self.assertEqual(self.qpart.lines[1:4], ['efgh', 'klmn', 'opqr']) ++ self.assertEqual(self.qpart.lines[1:7], ['efgh', 'klmn', 'opqr']) # Python list behaves this way ++ self.assertEqual(self.qpart.lines[0:0], []) ++ self.assertEqual(self.qpart.lines[0:1], ['abcd']) ++ self.assertEqual(self.qpart.lines[:2], ['abcd', 'efgh']) ++ self.assertEqual(self.qpart.lines[0:-2], ['abcd', 'efgh']) ++ self.assertEqual(self.qpart.lines[-2:], ['klmn', 'opqr']) ++ self.assertEqual(self.qpart.lines[-4:-2], ['abcd', 'efgh']) + + with self.assertRaises(IndexError): + self.qpart.lines[4] +@@ -297,27 +297,27 @@ def test_getSlice(self): + + def test_setSlice_1(self): + self.qpart.lines[0] = 'xyz' +- self.assertEquals(self.qpart.text, 'xyz\nefgh\nklmn\nopqr') ++ self.assertEqual(self.qpart.text, 'xyz\nefgh\nklmn\nopqr') + + def test_setSlice_2(self): + self.qpart.lines[1] = 'xyz' +- self.assertEquals(self.qpart.text, 'abcd\nxyz\nklmn\nopqr') ++ self.assertEqual(self.qpart.text, 'abcd\nxyz\nklmn\nopqr') + + def test_setSlice_3(self): + self.qpart.lines[-4] = 'xyz' +- self.assertEquals(self.qpart.text, 'xyz\nefgh\nklmn\nopqr') ++ self.assertEqual(self.qpart.text, 'xyz\nefgh\nklmn\nopqr') + + def test_setSlice_4(self): + self.qpart.lines[0:4] = ['st', 'uv', 'wx', 'z'] +- self.assertEquals(self.qpart.text, 'st\nuv\nwx\nz') ++ self.assertEqual(self.qpart.text, 'st\nuv\nwx\nz') + + def test_setSlice_5(self): + self.qpart.lines[0:47] = ['st', 'uv', 'wx', 'z'] +- self.assertEquals(self.qpart.text, 'st\nuv\nwx\nz') ++ self.assertEqual(self.qpart.text, 'st\nuv\nwx\nz') + + def test_setSlice_6(self): + self.qpart.lines[1:3] = ['st', 'uv'] +- self.assertEquals(self.qpart.text, 'abcd\nst\nuv\nopqr') ++ self.assertEqual(self.qpart.text, 'abcd\nst\nuv\nopqr') + + def test_setSlice_61(self): + with self.assertRaises(ValueError): +@@ -325,11 +325,11 @@ def test_setSlice_61(self): + + def test_setSlice_7(self): + self.qpart.lines[-3:3] = ['st', 'uv'] +- self.assertEquals(self.qpart.text, 'abcd\nst\nuv\nopqr') ++ self.assertEqual(self.qpart.text, 'abcd\nst\nuv\nopqr') + + def test_setSlice_8(self): + self.qpart.lines[-3:-1] = ['st', 'uv'] +- self.assertEquals(self.qpart.text, 'abcd\nst\nuv\nopqr') ++ self.assertEqual(self.qpart.text, 'abcd\nst\nuv\nopqr') + + def test_setSlice_9(self): + with self.assertRaises(IndexError): +diff --git a/tests/test_bookmarks.py b/tests/test_bookmarks.py +index 2bdc034..795f4fe 100755 +--- a/tests/test_bookmarks.py ++++ b/tests/test_bookmarks.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import os + import sys +diff --git a/tests/test_bracket_hlighter.py b/tests/test_bracket_hlighter.py +index 3c19748..8dead38 100755 +--- a/tests/test_bracket_hlighter.py ++++ b/tests/test_bracket_hlighter.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import os + import sys +diff --git a/tests/test_completion.py b/tests/test_completion.py +index 848e55c..d8ea3ad 100755 +--- a/tests/test_completion.py ++++ b/tests/test_completion.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import os + import sys +diff --git a/tests/test_draw_whitespace.py b/tests/test_draw_whitespace.py +index e3cbbed..fa484e9 100755 +--- a/tests/test_draw_whitespace.py ++++ b/tests/test_draw_whitespace.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + # encoding: utf8 + + +@@ -48,11 +48,11 @@ def _ws_test(self, + try: + self._verify(text, expectedResult) + except: +- print "Failed params:\n\tany {}\n\tincorrect {}\n\ttabs {}\n\twidth {}".format( ++ print("Failed params:\n\tany {}\n\tincorrect {}\n\ttabs {}\n\twidth {}".format( + self.qpart.drawAnyWhitespace, + self.qpart.drawIncorrectIndentation, + self.qpart.indentUseTabs, +- self.qpart.indentWidth) ++ self.qpart.indentWidth)) + raise + + def _verify(self, text, expectedResult): +diff --git a/tests/test_edit.py b/tests/test_edit.py +index 7dda435..0f91f9c 100755 +--- a/tests/test_edit.py ++++ b/tests/test_edit.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import sys + import unittest +diff --git a/tests/test_indent.py b/tests/test_indent.py +index bc9483d..2ce5d57 100755 +--- a/tests/test_indent.py ++++ b/tests/test_indent.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import sys + import unittest +diff --git a/tests/test_indenter/indenttest.py b/tests/test_indenter/indenttest.py +index e4f9a77..a358980 100644 +--- a/tests/test_indenter/indenttest.py ++++ b/tests/test_indenter/indenttest.py +@@ -25,7 +25,7 @@ def setOrigin(self, text): + + def verifyExpected(self, text): + lines = self.qpart.text.split('\n') +- self.assertEquals(text, map(str, lines)) ++ self.assertEqual(text, [l for l in lines]) + + def setCursorPosition(self, line, col): + self.qpart.cursorPosition = line, col +diff --git a/tests/test_indenter/test_cstyle.py b/tests/test_indenter/test_cstyle.py +index 91cf219..3901ce9 100755 +--- a/tests/test_indenter/test_cstyle.py ++++ b/tests/test_indenter/test_cstyle.py +@@ -1,7 +1,10 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import unittest + ++import os.path ++import sys ++sys.path.append(os.path.abspath(os.path.join(__file__, '..'))) + from indenttest import IndentTest + + class BaseTestClass(IndentTest): +diff --git a/tests/test_indenter/test_haskell.py b/tests/test_indenter/test_haskell.py +index a4ad710..b98ef37 100755 +--- a/tests/test_indenter/test_haskell.py ++++ b/tests/test_indenter/test_haskell.py +@@ -1,7 +1,11 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import unittest + ++import os.path ++import sys ++sys.path.append(os.path.abspath(os.path.join(__file__, '..'))) ++ + from indenttest import IndentTest + + +diff --git a/tests/test_indenter/test_lisp.py b/tests/test_indenter/test_lisp.py +index 14598ba..84cde3a 100755 +--- a/tests/test_indenter/test_lisp.py ++++ b/tests/test_indenter/test_lisp.py +@@ -1,7 +1,10 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import unittest + ++import os.path ++import sys ++sys.path.append(os.path.abspath(os.path.join(__file__, '..'))) + from indenttest import IndentTest + + +diff --git a/tests/test_indenter/test_normal.py b/tests/test_indenter/test_normal.py +index 49e464a..5e1cd92 100755 +--- a/tests/test_indenter/test_normal.py ++++ b/tests/test_indenter/test_normal.py +@@ -1,7 +1,10 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import unittest + ++import os.path ++import sys ++sys.path.append(os.path.abspath(os.path.join(__file__, '..'))) + from indenttest import IndentTest + + +diff --git a/tests/test_indenter/test_python.py b/tests/test_indenter/test_python.py +index 48f83f9..40714b8 100755 +--- a/tests/test_indenter/test_python.py ++++ b/tests/test_indenter/test_python.py +@@ -1,7 +1,10 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import unittest + ++import os.path ++import sys ++sys.path.append(os.path.abspath(os.path.join(__file__, '..'))) + from indenttest import IndentTest + + +diff --git a/tests/test_indenter/test_ruby.py b/tests/test_indenter/test_ruby.py +index 34e1ec5..f810d90 100755 +--- a/tests/test_indenter/test_ruby.py ++++ b/tests/test_indenter/test_ruby.py +@@ -1,7 +1,10 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import unittest + ++import os.path ++import sys ++sys.path.append(os.path.abspath(os.path.join(__file__, '..'))) + from indenttest import IndentTest + + class BaseTestClass(IndentTest): +diff --git a/tests/test_indenter/test_scheme.py b/tests/test_indenter/test_scheme.py +index 435eb3d..13324a2 100755 +--- a/tests/test_indenter/test_scheme.py ++++ b/tests/test_indenter/test_scheme.py +@@ -1,7 +1,10 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import unittest + ++import os.path ++import sys ++sys.path.append(os.path.abspath(os.path.join(__file__, '..'))) + from indenttest import IndentTest + + class BaseTestClass(IndentTest): +diff --git a/tests/test_indenter/test_xmlindent.py b/tests/test_indenter/test_xmlindent.py +index dd66255..4fc6707 100755 +--- a/tests/test_indenter/test_xmlindent.py ++++ b/tests/test_indenter/test_xmlindent.py +@@ -1,7 +1,10 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import unittest + ++import os.path ++import sys ++sys.path.append(os.path.abspath(os.path.join(__file__, '..'))) + from indenttest import IndentTest + + class BaseTestClass(IndentTest): +diff --git a/tests/test_rectangular_selection.py b/tests/test_rectangular_selection.py +index 5083e84..c814ec0 100755 +--- a/tests/test_rectangular_selection.py ++++ b/tests/test_rectangular_selection.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + # encoding: utf8 + + +@@ -145,7 +145,7 @@ def test_copy_paste(self): + + def test_copy_paste_utf8(self): + self.qpart.show() +- self.qpart.text = u'фыва' ++ self.qpart.text = 'фыва' + for i in range(3): + QTest.keyClick(self.qpart, Qt.Key_Right, Qt.AltModifier | Qt.ShiftModifier) + QTest.keyClick(self.qpart, Qt.Key_C, Qt.ControlModifier) +@@ -155,7 +155,7 @@ def test_copy_paste_utf8(self): + QTest.keyClick(self.qpart, Qt.Key_V, Qt.ControlModifier) + + self.assertEqual(self.qpart.text, +- u'фыва фыв') ++ 'фыва фыв') + + def test_paste_replace_selection(self): + self.qpart.show() +diff --git a/tests/test_syntax/test_dynamic_substitution.py b/tests/test_syntax/test_dynamic_substitution.py +index 39b445a..45045cd 100755 +--- a/tests/test_syntax/test_dynamic_substitution.py ++++ b/tests/test_syntax/test_dynamic_substitution.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import unittest + import sys +diff --git a/tests/test_syntax/test_rules.py b/tests/test_syntax/test_rules.py +index 75f2958..859afff 100755 +--- a/tests/test_syntax/test_rules.py ++++ b/tests/test_syntax/test_rules.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import unittest + +@@ -24,7 +24,7 @@ def tryMatch(rule, column, text): + return tryMatchWithData(rule, None, column, text) + + def tryMatchWithData(rule, contextData, column, text): +- textToMatchObject = parser.TextToMatchObject(column, unicode(text), _currentSyntax.parser.deliminatorSet, contextData) ++ textToMatchObject = parser.TextToMatchObject(column, str(text), _currentSyntax.parser.deliminatorSet, contextData) + ruleTryMatchResult = rule.tryMatch(textToMatchObject) + if ruleTryMatchResult is not None: + return ruleTryMatchResult.length +@@ -50,17 +50,17 @@ def test_DetectChar_dynamic(self): + rule = self._getRule("perl.xml", "ip_string_6", 1) + text = "a" + +- self.assertEqual(tryMatchWithData(rule, (u'a', u'b', u'c'), 0, text), 1) +- self.assertEqual(tryMatchWithData(rule, (u'x', u'y', u'z'), 0, text), None) ++ self.assertEqual(tryMatchWithData(rule, ('a', 'b', 'c'), 0, text), 1) ++ self.assertEqual(tryMatchWithData(rule, ('x', 'y', 'z'), 0, text), None) + + def test_DetectChar_dynamic2(self): + rule = self._getRule("perl.xml", "string_6", 3) + text = "abcdXefg" + +- count = tryMatchWithData(rule, (u'X', u'Y', u'Z',), 0, text) ++ count = tryMatchWithData(rule, ('X', 'Y', 'Z',), 0, text) + self.assertEqual(count, None) + +- count = tryMatchWithData(rule, (u'X', u'Y', u'Z',), 4, text) ++ count = tryMatchWithData(rule, ('X', 'Y', 'Z',), 4, text) + self.assertEqual(count, 1) + + def test_Detect2Chars(self): +@@ -156,7 +156,7 @@ def test_RegExpr(self): + + rule = self._getRule('fsharp.xml', 'ModuleEnv2', 0) + if hasattr(rule, 'regExp'): # only on Python version +- self.assertEqual(rule.regExp.pattern, u"[A-Z][A-Za-z\xc0-\xd6\xd8-\xf6\xf8-\xff0-9_']*") ++ self.assertEqual(rule.regExp.pattern, "[A-Z][A-Za-z\xc0-\xd6\xd8-\xf6\xf8-\xff0-9_']*") + + def test_RegExpr_slashB(self): + rule = self._getRule('fortran.xml', 'find_numbers', 3) +@@ -291,7 +291,7 @@ def test_dynamic_reg_exp(self): + """ + rule = self._getRule("ruby.xml", "gdl_dq_string_5", 2) # "\s*%1" + text = '%|a| x' +- count = tryMatchWithData(rule, (u'blabla|', u'|', ), 3, text) ++ count = tryMatchWithData(rule, ('blabla|', '|', ), 3, text) + self.assertEqual(count, 1) + + def test_dynamic_string_detect(self): +@@ -300,7 +300,7 @@ def test_dynamic_string_detect(self): + rule = self._getRule("php.xml", "phpsource", 34) # heredoc + text = "<<': + QTest.keyClick(self.qpart, key, Qt.ShiftModifier) +diff --git a/tools/show-syntax.py b/tools/show-syntax.py +index 500d548..0008df8 100755 +--- a/tools/show-syntax.py ++++ b/tools/show-syntax.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + import sys + sys.path.insert(0, '.') +@@ -11,7 +11,7 @@ + + if __name__ == '__main__': + if len(sys.argv) != 2: +- print 'Usage:\n\t%s SYNTAX_FILE_NAME' % sys.argv[0] ++ print('Usage:\n\t%s SYNTAX_FILE_NAME' % sys.argv[0]) + else: + syntax = SyntaxManager().getSyntax(xmlFileName = sys.argv[1]) +- print unicode(syntax) ++ print(str(syntax)) + +From 14a0f8cae1919c646ec57f36035ce4bb4766b9fa Mon Sep 17 00:00:00 2001 +From: Andrei Kopats +Date: Thu, 19 Nov 2015 10:23:52 +0300 +Subject: [PATCH 2/4] tests: fix a crash on py3 + +--- + tests/test_actions.py | 2 +- + tests/test_api.py | 2 +- + tests/test_bookmarks.py | 2 +- + tests/test_bracket_hlighter.py | 2 +- + tests/test_completion.py | 2 +- + tests/test_draw_whitespace.py | 2 +- + tests/test_edit.py | 2 +- + tests/test_indent.py | 2 +- + tests/test_rectangular_selection.py | 2 +- + tests/test_vim.py | 2 +- + 10 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/tests/test_actions.py b/tests/test_actions.py +index 9b65857..7e2f7df 100755 +--- a/tests/test_actions.py ++++ b/tests/test_actions.py +@@ -20,7 +20,7 @@ def setUp(self): + self.qpart = Qutepart() + + def tearDown(self): +- del self.qpart ++ self.qpart.terminate() + + + class Print(_BaseTest): +diff --git a/tests/test_api.py b/tests/test_api.py +index 09efda6..358089f 100755 +--- a/tests/test_api.py ++++ b/tests/test_api.py +@@ -23,7 +23,7 @@ def setUp(self): + self.qpart = Qutepart() + + def tearDown(self): +- del self.qpart ++ self.qpart.terminate() + + + class Selection(_BaseTest): +diff --git a/tests/test_bookmarks.py b/tests/test_bookmarks.py +index 795f4fe..fdd3f32 100755 +--- a/tests/test_bookmarks.py ++++ b/tests/test_bookmarks.py +@@ -23,7 +23,7 @@ def setUp(self): + self.qpart = Qutepart() + + def tearDown(self): +- del self.qpart ++ self.qpart.terminate() + + def _markedBlocks(self): + bookMarksObject = self.qpart._bookmarks +diff --git a/tests/test_bracket_hlighter.py b/tests/test_bracket_hlighter.py +index 8dead38..ce683f9 100755 +--- a/tests/test_bracket_hlighter.py ++++ b/tests/test_bracket_hlighter.py +@@ -22,7 +22,7 @@ def setUp(self): + self.qpart = Qutepart() + + def tearDown(self): +- del self.qpart ++ self.qpart.terminate() + + def _verify(self, actual, expected): + converted = [] +diff --git a/tests/test_completion.py b/tests/test_completion.py +index d8ea3ad..4c61cbc 100755 +--- a/tests/test_completion.py ++++ b/tests/test_completion.py +@@ -27,7 +27,7 @@ def setUp(self): + self._window.menuBar().addAction(self.qpart.invokeCompletionAction) + + def tearDown(self): +- del self.qpart ++ self.qpart.terminate() + + def test_down_selects_first(self): + self.qpart.text = 'aaaa\nbbbb\ncccX\ndddd\ncccY' +diff --git a/tests/test_draw_whitespace.py b/tests/test_draw_whitespace.py +index fa484e9..5b92bdd 100755 +--- a/tests/test_draw_whitespace.py ++++ b/tests/test_draw_whitespace.py +@@ -25,7 +25,7 @@ def setUp(self): + self.qpart = Qutepart() + + def tearDown(self): +- del self.qpart ++ self.qpart.terminate() + + def _ws_test(self, + text, +diff --git a/tests/test_edit.py b/tests/test_edit.py +index 0f91f9c..edd4df6 100755 +--- a/tests/test_edit.py ++++ b/tests/test_edit.py +@@ -20,7 +20,7 @@ def setUp(self): + self.qpart = Qutepart() + + def tearDown(self): +- del self.qpart ++ self.qpart.terminate() + + def test_overwrite_edit(self): + self.qpart.show() +diff --git a/tests/test_indent.py b/tests/test_indent.py +index 2ce5d57..e01d463 100755 +--- a/tests/test_indent.py ++++ b/tests/test_indent.py +@@ -20,7 +20,7 @@ def setUp(self): + self.qpart = Qutepart() + + def tearDown(self): +- del self.qpart ++ self.qpart.terminate() + + def test_1(self): + # Indent with Tab +diff --git a/tests/test_rectangular_selection.py b/tests/test_rectangular_selection.py +index c814ec0..0b28179 100755 +--- a/tests/test_rectangular_selection.py ++++ b/tests/test_rectangular_selection.py +@@ -24,7 +24,7 @@ def setUp(self): + + def tearDown(self): + self.qpart.hide() +- del self.qpart ++ self.qpart.terminate() + + def test_real_to_visible(self): + self.qpart.text = 'abcdfg' +diff --git a/tests/test_vim.py b/tests/test_vim.py +index def74b0..d00dfe7 100755 +--- a/tests/test_vim.py ++++ b/tests/test_vim.py +@@ -30,7 +30,7 @@ def setUp(self): + + def tearDown(self): + self.qpart.hide() +- del self.qpart ++ self.qpart.terminate() + + def _onVimModeChanged(self, color, mode): + self.vimMode = mode + +From 7fc479d4bbf0dcf8aa729633f9af72450a527431 Mon Sep 17 00:00:00 2001 +From: Andrei Kopats +Date: Thu, 19 Nov 2015 23:45:01 +0300 +Subject: [PATCH 3/4] vim: fix strage crash in the tests on PySide + +--- + qutepart/vim.py | 71 +++++++++++++++++++++++++++++-------------------------- + tests/test_vim.py | 11 +++++---- + 2 files changed, 44 insertions(+), 38 deletions(-) + +diff --git a/qutepart/vim.py b/qutepart/vim.py +index fa74e3f..d847af5 100644 +--- a/qutepart/vim.py ++++ b/qutepart/vim.py +@@ -72,14 +72,19 @@ def isChar(ev): + REPLACE_CHAR: QColor('#ff3300')} + + ++class _GlobalClipboard: ++ def __init__(self): ++ self.value = '' ++ ++_globalClipboard = _GlobalClipboard() ++ ++ + class Vim(QObject): + """Vim mode implementation. + Listens events and does actions + """ + modeIndicationChanged = pyqtSignal(QColor, str) + +- internalClipboard = '' # delete commands save text to this clipboard +- + def __init__(self, qpart): + QObject.__init__(self) + self._qpart = qpart +@@ -607,10 +612,10 @@ def cmdDelete(self, cmd, repeatLineCount=None): + if self._selectLines: + start, end = self._selectedLinesRange() + self._saveLastEditLinesCmd(cmd, end - start + 1) +- Vim.internalClipboard = self._qpart.lines[start:end + 1] ++ _globalClipboard.value = self._qpart.lines[start:end + 1] + del self._qpart.lines[start:end + 1] + else: +- Vim.internalClipboard = cursor.selectedText() ++ _globalClipboard.value = cursor.selectedText() + cursor.removeSelectedText() + + def cmdDeleteLines(self, cmd, repeatLineCount=None): +@@ -620,7 +625,7 @@ def cmdDeleteLines(self, cmd, repeatLineCount=None): + start, end = self._selectedLinesRange() + self._saveLastEditLinesCmd(cmd, end - start + 1) + +- Vim.internalClipboard = self._qpart.lines[start:end + 1] ++ _globalClipboard.value = self._qpart.lines[start:end + 1] + del self._qpart.lines[start:end + 1] + + def cmdInsertMode(self, cmd): +@@ -660,7 +665,7 @@ def cmdAppendAfterChar(self, cmd): + + def cmdReplaceSelectedLines(self, cmd): + start, end = self._selectedLinesRange() +- Vim.internalClipboard = self._qpart.lines[start:end + 1] ++ _globalClipboard.value = self._qpart.lines[start:end + 1] + + lastLineLen = len(self._qpart.lines[end]) + self._qpart.selectedPosition = ((start, 0), (end, lastLineLen)) +@@ -672,7 +677,7 @@ def cmdResetSelection(self, cmd): + self._qpart.cursorPosition = self._qpart.selectedPosition[0] + + def cmdInternalPaste(self, cmd): +- if not Vim.internalClipboard: ++ if not _globalClipboard.value: + return + + with self._qpart: +@@ -684,11 +689,11 @@ def cmdInternalPaste(self, cmd): + else: + cursor.removeSelectedText() + +- if isinstance(Vim.internalClipboard, str): +- self._qpart.textCursor().insertText(Vim.internalClipboard) +- elif isinstance(Vim.internalClipboard, list): ++ if isinstance(_globalClipboard.value, str): ++ self._qpart.textCursor().insertText(_globalClipboard.value) ++ elif isinstance(_globalClipboard.value, list): + currentLineIndex = self._qpart.cursorPosition[0] +- text = '\n'.join(Vim.internalClipboard) ++ text = '\n'.join(_globalClipboard.value) + index = currentLineIndex if self._selectLines else currentLineIndex + 1 + self._qpart.lines.insert(index, text) + +@@ -709,9 +714,9 @@ def cmdVisualLinesMode(self, cmd): + def cmdYank(self, cmd): + if self._selectLines: + start, end = self._selectedLinesRange() +- Vim.internalClipboard = self._qpart.lines[start:end + 1] ++ _globalClipboard.value = self._qpart.lines[start:end + 1] + else: +- Vim.internalClipboard = self._qpart.selectedText ++ _globalClipboard.value = self._qpart.selectedText + + self._qpart.copy() + +@@ -719,9 +724,9 @@ def cmdChange(self, cmd): + cursor = self._qpart.textCursor() + if cursor.selectedText(): + if self._selectLines: +- Vim.internalClipboard = cursor.selectedText().splitlines() ++ _globalClipboard.value = cursor.selectedText().splitlines() + else: +- Vim.internalClipboard = cursor.selectedText() ++ _globalClipboard.value = cursor.selectedText() + cursor.removeSelectedText() + self.switchMode(Insert) + +@@ -1017,27 +1022,27 @@ def insert(): + self.switchMode(Insert) + + def cmdInternalPaste(self, cmd, count): +- if not Vim.internalClipboard: ++ if not _globalClipboard.value: + return + +- if isinstance(Vim.internalClipboard, str): ++ if isinstance(_globalClipboard.value, str): + cursor = self._qpart.textCursor() + if cmd == _p: + cursor.movePosition(QTextCursor.Right) + self._qpart.setTextCursor(cursor) + + self._repeat(count, +- lambda: cursor.insertText(Vim.internalClipboard)) ++ lambda: cursor.insertText(_globalClipboard.value)) + cursor.movePosition(QTextCursor.Left) + self._qpart.setTextCursor(cursor) + +- elif isinstance(Vim.internalClipboard, list): ++ elif isinstance(_globalClipboard.value, list): + index = self._qpart.cursorPosition[0] + if cmd == _p: + index += 1 + + self._repeat(count, +- lambda: self._qpart.lines.insert(index, '\n'.join(Vim.internalClipboard))) ++ lambda: self._qpart.lines.insert(index, '\n'.join(_globalClipboard.value))) + + self._saveLastEditSimpleCmd(cmd, count) + +@@ -1049,7 +1054,7 @@ def cmdSubstitute(self, cmd, count): + cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor) + + if cursor.selectedText(): +- Vim.internalClipboard = cursor.selectedText() ++ _globalClipboard.value = cursor.selectedText() + cursor.removeSelectedText() + + self._saveLastEditSimpleCmd(cmd, count) +@@ -1062,7 +1067,7 @@ def cmdSubstituteLines(self, cmd, count): + availableCount = len(self._qpart.lines) - lineIndex + effectiveCount = min(availableCount, count) + +- Vim.internalClipboard = self._qpart.lines[lineIndex:lineIndex + effectiveCount] ++ _globalClipboard.value = self._qpart.lines[lineIndex:lineIndex + effectiveCount] + with self._qpart: + del self._qpart.lines[lineIndex:lineIndex + effectiveCount] + self._qpart.lines.insert(lineIndex, '') +@@ -1090,7 +1095,7 @@ def cmdDelete(self, cmd, count): + cursor.movePosition(direction, QTextCursor.KeepAnchor) + + if cursor.selectedText(): +- Vim.internalClipboard = cursor.selectedText() ++ _globalClipboard.value = cursor.selectedText() + cursor.removeSelectedText() + + self._saveLastEditSimpleCmd(cmd, count) +@@ -1102,7 +1107,7 @@ def cmdDeleteUntilEndOfBlock(self, cmd, count): + for _ in range(count - 1): + cursor.movePosition(QTextCursor.Down, QTextCursor.KeepAnchor) + cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) +- Vim.internalClipboard = cursor.selectedText() ++ _globalClipboard.value = cursor.selectedText() + cursor.removeSelectedText() + if cmd == _C: + self.switchMode(Insert) +@@ -1113,7 +1118,7 @@ def cmdYankUntilEndOfLine(self, cmd, count): + oldCursor = self._qpart.textCursor() + cursor = self._qpart.textCursor() + cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) +- Vim.internalClipboard = cursor.selectedText() ++ _globalClipboard.value = cursor.selectedText() + self._qpart.setTextCursor(cursor) + self._qpart.copy() + self._qpart.setTextCursor(oldCursor) +@@ -1156,7 +1161,7 @@ def cmdCompositeDelete(self, cmd, motion, searchChar, count): + + effectiveCount = min(availableCount, count) + +- Vim.internalClipboard = self._qpart.lines[lineIndex:lineIndex + effectiveCount + 1] ++ _globalClipboard.value = self._qpart.lines[lineIndex:lineIndex + effectiveCount + 1] + del self._qpart.lines[lineIndex:lineIndex + effectiveCount + 1] + elif motion in (_k, _Up): + lineIndex = self._qpart.cursorPosition[0] +@@ -1165,7 +1170,7 @@ def cmdCompositeDelete(self, cmd, motion, searchChar, count): + + effectiveCount = min(lineIndex, count) + +- Vim.internalClipboard = self._qpart.lines[lineIndex - effectiveCount:lineIndex + 1] ++ _globalClipboard.value = self._qpart.lines[lineIndex - effectiveCount:lineIndex + 1] + del self._qpart.lines[lineIndex - effectiveCount:lineIndex + 1] + elif motion == _d: # delete whole line + lineIndex = self._qpart.cursorPosition[0] +@@ -1173,22 +1178,22 @@ def cmdCompositeDelete(self, cmd, motion, searchChar, count): + + effectiveCount = min(availableCount, count) + +- Vim.internalClipboard = self._qpart.lines[lineIndex:lineIndex + effectiveCount] ++ _globalClipboard.value = self._qpart.lines[lineIndex:lineIndex + effectiveCount] + del self._qpart.lines[lineIndex:lineIndex + effectiveCount] + elif motion == _G: + currentLineIndex = self._qpart.cursorPosition[0] +- Vim.internalClipboard = self._qpart.lines[currentLineIndex:] ++ _globalClipboard.value = self._qpart.lines[currentLineIndex:] + del self._qpart.lines[currentLineIndex:] + elif motion == 'gg': + currentLineIndex = self._qpart.cursorPosition[0] +- Vim.internalClipboard = self._qpart.lines[:currentLineIndex + 1] ++ _globalClipboard.value = self._qpart.lines[:currentLineIndex + 1] + del self._qpart.lines[:currentLineIndex + 1] + else: + self._moveCursor(motion, count, select=True, searchChar=searchChar) + + selText = self._qpart.textCursor().selectedText() + if selText: +- Vim.internalClipboard = selText ++ _globalClipboard.value = selText + self._qpart.textCursor().removeSelectedText() + + self._saveLastEditCompositeCmd(cmd, motion, searchChar, count) +@@ -1207,10 +1212,10 @@ def cmdCompositeYank(self, cmd, motion, searchChar, count): + cursor.movePosition(QTextCursor.Down, QTextCursor.KeepAnchor) + cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) + self._qpart.setTextCursor(cursor) +- Vim.internalClipboard = [self._qpart.selectedText] ++ _globalClipboard.value = [self._qpart.selectedText] + else: + self._moveCursor(motion, count, select=True, searchChar=searchChar) +- Vim.internalClipboard = self._qpart.selectedText ++ _globalClipboard.value = self._qpart.selectedText + + self._qpart.copy() + self._qpart.setTextCursor(oldCursor) +diff --git a/tests/test_vim.py b/tests/test_vim.py +index d00dfe7..0aa19ae 100755 +--- a/tests/test_vim.py ++++ b/tests/test_vim.py +@@ -10,6 +10,7 @@ + from PyQt4.QtTest import QTest + + from qutepart import Qutepart ++from qutepart.vim import _globalClipboard + + + class _Test(unittest.TestCase): +@@ -353,7 +354,7 @@ def test_01a(self): + + self.assertEqual(self.qpart.lines[0], + 'The brown fox') +- self.assertEqual(self.qpart._vim.internalClipboard, 'k') ++ self.assertEqual(_globalClipboard.value, 'k') + + def test_01b(self): + """Delete with x. Use count +@@ -363,7 +364,7 @@ def test_01b(self): + + self.assertEqual(self.qpart.lines[0], + 'The brown fox') +- self.assertEqual(self.qpart._vim.internalClipboard, 'quick') ++ self.assertEqual(_globalClipboard.value, 'quick') + + def test_02(self): + """Composite delete with d. Left and right +@@ -399,7 +400,7 @@ def test_03(self): + self.click('dj') + self.assertEqual(self.qpart.lines[:], + ['']) +- self.assertEqual(self.qpart._vim.internalClipboard, ++ self.assertEqual(_globalClipboard.value, + ['lazy dog', + 'back']) + +@@ -415,7 +416,7 @@ def test_04(self): + self.assertEqual(self.qpart.lines[:], + ['The quick brown fox', + 'back']) +- self.assertEqual(self.qpart._vim.internalClipboard, ++ self.assertEqual(_globalClipboard.value, + ['jumps over the', + 'lazy dog']) + +@@ -426,7 +427,7 @@ def test_05(self): + """ + self.click('3dw') + self.assertEqual(self.qpart.lines[0], 'fox') +- self.assertEqual(self.qpart._vim.internalClipboard, ++ self.assertEqual(_globalClipboard.value, + 'The quick brown ') + + def test_06(self): + +From b4368fd3c0cee1c885fac9382608e1283801064b Mon Sep 17 00:00:00 2001 +From: Andrei Kopats +Date: Sat, 28 Jun 2014 18:37:44 +0300 +Subject: [PATCH 4/4] Port cParser to Python3 + +--- + editor.py | 6 ++- + qutepart/syntax/cParser.c | 112 ++++++++++++++++++++++++++-------------------- + 2 files changed, 67 insertions(+), 51 deletions(-) + +diff --git a/editor.py b/editor.py +index a4ba78e..ff8c1e4 100755 +--- a/editor.py ++++ b/editor.py +@@ -27,8 +27,10 @@ def _fixSysPath(binaryQutepart): + qutepartDir = os.path.dirname(executablePath) + sys.path.insert(0, qutepartDir) # do not import installed modules + if binaryQutepart: +- sys.path.insert(0, qutepartDir + '/build/lib.linux-i686-2.7/') # use built modules +- sys.path.insert(0, qutepartDir + '/build/lib.linux-x86_64-2.7/') # use built modules ++ sys.path.insert(0, qutepartDir + '/build/lib.linux-i686-3.3/') # use built modules ++ sys.path.insert(0, qutepartDir + '/build/lib.linux-i686-3.4/') # use built modules ++ sys.path.insert(0, qutepartDir + '/build/lib.linux-x86_64-3.3/') # use built modules ++ sys.path.insert(0, qutepartDir + '/build/lib.linux-x86_64-3.4/') # use built modules + + + def main(): +diff --git a/qutepart/syntax/cParser.c b/qutepart/syntax/cParser.c +index 40b6615..4708fb5 100644 +--- a/qutepart/syntax/cParser.c ++++ b/qutepart/syntax/cParser.c +@@ -108,8 +108,7 @@ + + #define _DECLARE_TYPE(TYPE_NAME, CONSTRUCTOR, METHODS, MEMBERS, COMMENT) \ + static PyTypeObject TYPE_NAME##Type = { \ +- PyObject_HEAD_INIT(NULL)\ +- 0,\ ++ PyVarObject_HEAD_INIT(NULL, 0)\ + "qutepart.syntax.cParser." #TYPE_NAME,\ + sizeof(TYPE_NAME),\ + 0,\ +@@ -164,7 +163,7 @@ + #define REGISTER_TYPE(TYPE_NAME) \ + TYPE_NAME##Type.tp_new = PyType_GenericNew; \ + if (PyType_Ready(&TYPE_NAME##Type) < 0) \ +- return; \ ++ return NULL; \ + Py_INCREF(&TYPE_NAME##Type); \ + PyModule_AddObject(m, #TYPE_NAME, (PyObject *)&TYPE_NAME##Type); + +@@ -175,7 +174,7 @@ + { \ + Py_XDECREF(self->abstractRuleParams); \ + RULE_TYPE_NAME##_dealloc_fields(self); \ +- self->ob_type->tp_free((PyObject*)self); \ ++ Py_TYPE(self)->tp_free((PyObject*)self); \ + }; \ + \ + static PyMethodDef RULE_TYPE_NAME##_methods[] = { \ +@@ -458,7 +457,7 @@ AbstractRuleParams_dealloc(AbstractRuleParams* self) + Py_XDECREF(self->attribute); + Py_XDECREF(self->context); + +- self->ob_type->tp_free((PyObject*)self); ++ Py_TYPE(self)->tp_free((PyObject*)self); + } + + static int +@@ -489,7 +488,7 @@ AbstractRuleParams_init(AbstractRuleParams *self, PyObject *args, PyObject *kwds + ASSIGN_PYOBJECT_FIELD(format); + + if (Py_None != textType) +- self->textType = PyString_AsString(textType)[0]; ++ self->textType = PyUnicode_AsUnicode(textType)[0]; + else + self->textType = 0; + +@@ -516,7 +515,7 @@ RuleTryMatchResult_dealloc(RuleTryMatchResult* self) + Py_XDECREF(self->rule); + Py_XDECREF(self->data); + +- self->ob_type->tp_free((PyObject*)self); ++ Py_TYPE(self)->tp_free((PyObject*)self); + } + + static PyMemberDef RuleTryMatchResult_members[] = { +@@ -581,8 +580,8 @@ TextToMatchObject_internal_make(int column, PyObject* unicodeText, _RegExpMatchG + textToMatchObject.wholeLineUnicodeTextLower = PyObject_CallMethod(unicodeText, "lower", ""); + textToMatchObject.wholeLineUtf8Text = PyUnicode_AsUTF8String(unicodeText); + textToMatchObject.wholeLineUtf8TextLower = PyUnicode_AsUTF8String(textToMatchObject.wholeLineUnicodeTextLower); +- textToMatchObject.utf8Text = PyString_AS_STRING(textToMatchObject.wholeLineUtf8Text); +- textToMatchObject.utf8TextLower = PyString_AS_STRING(textToMatchObject.wholeLineUtf8TextLower); ++ textToMatchObject.utf8Text = PyBytes_AsString(textToMatchObject.wholeLineUtf8Text); ++ textToMatchObject.utf8TextLower = PyBytes_AsString(textToMatchObject.wholeLineUtf8TextLower); + + // text and textLen is updated in the loop + textToMatchObject.textLen = textToMatchObject.wholeLineLen; +@@ -686,16 +685,20 @@ TextToMatchObject_internal_update(TextToMatchObject_internal* self, + _StringHash* pMask = (_StringHash*)maskForDifferenceChar[self->utf8WordLength - 1]; + _StringHash mask = *pMask; + +- *(_StringHash*)self->utf8Word = *(_StringHash*)self->utf8Text & mask; +- *(_StringHash*)self->utf8WordLower = *(_StringHash*)self->utf8TextLower & mask; ++ _StringHash* pUtf8Word = (_StringHash*)self->utf8Word; ++ _StringHash* pUtf8WordLower = (_StringHash*)self->utf8WordLower; ++ _StringHash* pUtf8Text = (_StringHash*)self->utf8Text; ++ _StringHash* pUtf8TextLower = (_StringHash*)self->utf8TextLower; ++ *pUtf8Word = *pUtf8Text & mask; ++ *pUtf8WordLower = *pUtf8TextLower & mask; + } + else + { +- *(_StringHash*)self->utf8Word = 0; ++ memset(self->utf8Word, 0, sizeof(_StringHash)); + strncpy(self->utf8Word, self->utf8Text, self->utf8WordLength); // without \0 + self->utf8Word[self->utf8WordLength] = '\0'; + +- *(_StringHash*)self->utf8WordLower = 0; ++ memset(self->utf8WordLower, 0, sizeof(_StringHash)); + strncpy(self->utf8WordLower, self->utf8TextLower, self->utf8WordLength); // without \0 + self->utf8WordLower[self->utf8WordLength] = '\0'; + } +@@ -714,7 +717,7 @@ TextToMatchObject_dealloc(TextToMatchObject* self) + Py_XDECREF(self->internal.wholeLineUnicodeText); + Py_XDECREF(self->internal.contextData); + TextToMatchObject_internal_free(&self->internal); +- self->ob_type->tp_free((PyObject*)self); ++ Py_TYPE(self)->tp_free((PyObject*)self); + } + + static int +@@ -756,7 +759,7 @@ TextToMatchObject_init(TextToMatchObject*self, PyObject *args, PyObject *kwds) + return -1; + } + utf8String = PyUnicode_AsUTF8String(unicodeString); +- memsize += PyString_GET_SIZE(utf8String) + 1; // + null char ++ memsize += PyBytes_Size(utf8String) + 1; // + null char + Py_XDECREF(utf8String); + } + data = pcre_malloc(memsize); +@@ -769,8 +772,8 @@ TextToMatchObject_init(TextToMatchObject*self, PyObject *args, PyObject *kwds) + int printedSize; + PyObject* unicodeString = PyTuple_GET_ITEM(contextDataTuple, i); + PyObject* utf8String = PyUnicode_AsUTF8String(unicodeString); +- strcpy(freeSpaceForString, PyString_AS_STRING(utf8String)); +- printedSize = PyString_GET_SIZE(utf8String) + 1; ++ strcpy(freeSpaceForString, PyBytes_AsString(utf8String)); ++ printedSize = PyBytes_Size(utf8String) + 1; + charPointers[i] = freeSpaceForString; + freeSpaceForString += printedSize; + Py_XDECREF(utf8String); +@@ -1064,7 +1067,7 @@ DetectChar_init(DetectChar *self, PyObject *args, PyObject *kwds) + ASSIGN_FIELD(AbstractRuleParams, abstractRuleParams); + + utf8Text = PyUnicode_AsUTF8String(char_); +- strncpy(self->utf8Char, PyString_AS_STRING(utf8Text), sizeof self->utf8Char); ++ strncpy(self->utf8Char, PyBytes_AsString(utf8Text), sizeof self->utf8Char); + Py_XDECREF(utf8Text); + + return 0; +@@ -1246,9 +1249,9 @@ StringDetect_init(StringDetect *self, PyObject *args, PyObject *kwds) + ASSIGN_FIELD(AbstractRuleParams, abstractRuleParams); + + utf8String = PyUnicode_AsUTF8String(string); +- self->stringLen = PyString_GET_SIZE(utf8String); ++ self->stringLen = PyBytes_Size(utf8String); + self->utf8String = PyMem_Malloc(self->stringLen + 1); +- strncpy(self->utf8String, PyString_AS_STRING(utf8String), self->stringLen + 1); ++ strncpy(self->utf8String, PyBytes_AsString(utf8String), self->stringLen + 1); + Py_DECREF(utf8String); + + return 0; +@@ -1322,7 +1325,7 @@ WordDetect_init(WordDetect *self, PyObject *args, PyObject *kwds) + ASSIGN_BOOL_FIELD(insensitive); + + utf8Word = PyUnicode_AsUTF8String(word); +- self->utf8Word = strdup(PyString_AS_STRING(utf8Word)); ++ self->utf8Word = strdup(PyBytes_AsString(utf8Word)); + Py_XDECREF(utf8Word); + + self->utf8WordLength = strlen(self->utf8Word); +@@ -1374,12 +1377,12 @@ _WordTree_init(_WordTree* self, PyObject* listOfUnicodeStrings) + + PyObject* unicodeWord = PyList_GetItem(listOfUnicodeStrings, i); + PyObject* utf8Word = PyUnicode_AsUTF8String(unicodeWord); +- wordLength = PyString_GET_SIZE(utf8Word); ++ wordLength = PyBytes_Size(utf8Word); + + if (wordLength <= QUTEPART_MAX_WORD_LENGTH) + self->wordCount[wordLength]++; + else +- fprintf(stderr, "Too long word '%s'\n", PyString_AS_STRING(utf8Word)); ++ fprintf(stderr, "Too long word '%s'\n", PyBytes_AsString(utf8Word)); + + Py_XDECREF(utf8Word); + } +@@ -1410,13 +1413,13 @@ _WordTree_init(_WordTree* self, PyObject* listOfUnicodeStrings) + + PyObject* unicodeWord = PyList_GetItem(listOfUnicodeStrings, i); + PyObject* utf8Word = PyUnicode_AsUTF8String(unicodeWord); +- wordLength = PyString_GET_SIZE(utf8Word); ++ wordLength = PyBytes_Size(utf8Word); + + wordIndex = currentWordIndex[wordLength]; + wordOffset = _WordTree_wordBufferSize(wordLength) * wordIndex; + wordPointer = self->words[wordLength] + wordOffset; + memset(wordPointer, 0, _WordTree_wordBufferSize(wordLength)); +- strncpy(wordPointer, PyString_AS_STRING(utf8Word), wordLength); // copy without zero ++ strncpy(wordPointer, PyBytes_AsString(utf8Word), wordLength); // copy without zero + wordPointer += _WordTree_wordBufferSize(wordLength); + currentWordIndex[wordLength]++; + +@@ -1695,13 +1698,13 @@ RegExpr_init(RegExpr *self, PyObject *args, PyObject *kwds) + utf8String = PyUnicode_AsUTF8String(string); + if (self->abstractRuleParams->dynamic) + { +- self->stringLen = PyString_GET_SIZE(utf8String); ++ self->stringLen = PyBytes_Size(utf8String); + self->utf8String = PyMem_Malloc(self->stringLen + 1); +- strcpy(self->utf8String, PyString_AS_STRING(utf8String)); ++ strcpy(self->utf8String, PyBytes_AsString(utf8String)); + } + else + { +- self->regExp = _compileRegExp(PyString_AS_STRING(utf8String), self->insensitive, &(self->extra)); ++ self->regExp = _compileRegExp(PyBytes_AsString(utf8String), self->insensitive, &(self->extra)); + } + Py_DECREF(utf8String); + +@@ -2513,7 +2516,7 @@ ContextStack_dealloc(ContextStack* self) + for (i = 0; i < self->_size; i++) + _RegExpMatchGroups_release(self->_data[i]); + +- self->ob_type->tp_free((PyObject*)self); ++ Py_TYPE(self)->tp_free((PyObject*)self); + } + + DECLARE_TYPE_WITHOUT_CONSTRUCTOR(ContextStack, NULL, "Context stack"); +@@ -2555,7 +2558,7 @@ ContextSwitcher_dealloc(ContextSwitcher* self) + { + Py_XDECREF(self->_contextToSwitch); + +- self->ob_type->tp_free((PyObject*)self); ++ Py_TYPE(self)->tp_free((PyObject*)self); + } + + static int +@@ -2657,7 +2660,7 @@ Context_dealloc(Context* self) + + PyMem_Free(self->rulesC); + +- self->ob_type->tp_free((PyObject*)self); ++ Py_TYPE(self)->tp_free((PyObject*)self); + } + + static int +@@ -2711,7 +2714,7 @@ Context_setValues(Context *self, PyObject *args) + ASSIGN_FIELD(ContextSwitcher, fallthroughContext); + ASSIGN_BOOL_FIELD(dynamic); + ASSIGN_PYOBJECT_FIELD(textTypePython); +- self->textType = PyString_AsString(textTypePython)[0]; ++ self->textType = PyUnicode_AsUnicode(textTypePython)[0]; + + Py_RETURN_NONE; + } +@@ -2753,11 +2756,12 @@ Context_appendSegment(PyObject* segmentList, int count, PyObject* format) + } + + static void +-Context_appendTextType(int fromIndex, int count, char* textTypeMapData, char textType) ++Context_appendTextType(int fromIndex, int count, PyObject* textTypeMap, char textType) + { ++ return; + int i; + for (i = fromIndex; i < fromIndex + count; i++) +- textTypeMapData[i] = textType; ++ PyUnicode_WriteChar(textTypeMap, i, textType); + } + + +@@ -2766,7 +2770,7 @@ Context_parseBlock(Context* self, + int currentColumnIndex, + PyObject* unicodeText, + PyObject* segmentList, +- char* textTypeMapData, ++ PyObject* textTypeMap, + ContextStack** pContextStack, + bool* pLineContinue) + { +@@ -2818,7 +2822,7 @@ Context_parseBlock(Context* self, + { + Context_appendSegment(segmentList, countOfNotMatchedSymbols, self->format); + Context_appendTextType(currentColumnIndex - countOfNotMatchedSymbols, countOfNotMatchedSymbols, +- textTypeMapData, self->textType); ++ textTypeMap, self->textType); + countOfNotMatchedSymbols = 0; + } + +@@ -2836,7 +2840,7 @@ Context_parseBlock(Context* self, + result.length, + format); + Context_appendTextType(currentColumnIndex, result.length, +- textTypeMapData, ++ textTypeMap, + textType); + currentColumnIndex += result.length; + +@@ -2889,7 +2893,7 @@ Context_parseBlock(Context* self, + { + Context_appendSegment(segmentList, countOfNotMatchedSymbols, self->format); + Context_appendTextType(currentColumnIndex - countOfNotMatchedSymbols, countOfNotMatchedSymbols, +- textTypeMapData, self->textType); ++ textTypeMap, self->textType); + + countOfNotMatchedSymbols = 0; + } +@@ -2923,7 +2927,7 @@ Parser_dealloc(Parser* self) + Py_XDECREF(self->defaultContext); + Py_XDECREF(self->defaultContextStack); + +- self->ob_type->tp_free((PyObject*)self); ++ Py_TYPE(self)->tp_free((PyObject*)self); + } + + static int +@@ -3004,7 +3008,6 @@ Parser_parseBlock_internal(Parser *self, PyObject *args, bool returnSegments) + int currentColumnIndex = 0; + int textLen; + PyObject* textTypeMap; +- char* textTypeMapData; + ContextStack* contextStack; + + if (! PyArg_ParseTuple(args, "|OO", +@@ -3037,10 +3040,9 @@ Parser_parseBlock_internal(Parser *self, PyObject *args, bool returnSegments) + } + + textLen = PyUnicode_GET_SIZE(unicodeText); +- textTypeMap = PyString_FromStringAndSize(NULL, textLen + 1); +- textTypeMapData = PyString_AS_STRING(textTypeMap); +- memset(textTypeMapData, ' ', textLen); +- textTypeMapData[textLen + 1] = 0; ++ textTypeMap = PyUnicode_New(textLen, 65535); ++ if (textLen > 0) ++ PyUnicode_Fill(textTypeMap, 0, textLen, ' '); + + while (currentColumnIndex < textLen) + { +@@ -3057,7 +3059,7 @@ Parser_parseBlock_internal(Parser *self, PyObject *args, bool returnSegments) + currentColumnIndex, + unicodeText, + segmentList, +- textTypeMapData, ++ textTypeMap, + &contextStack, + &lineContinue); + currentColumnIndex += length; +@@ -3161,19 +3163,29 @@ static PyMethodDef cParser_methods[] = { + {NULL} /* Sentinel */ + }; + ++static struct PyModuleDef moduledef = { ++ PyModuleDef_HEAD_INIT, ++ "cParser", /* m_name */ ++ "Accelerated code parser for Qutepart highlighter.", /* m_doc */ ++ -1, /* m_size */ ++ cParser_methods, /* m_methods */ ++ NULL, /* m_reload */ ++ NULL, /* m_traverse */ ++ NULL, /* m_clear */ ++ NULL, /* m_free */ ++ }; ++ + #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ + #define PyMODINIT_FUNC void + #endif + PyMODINIT_FUNC +-initcParser(void) ++PyInit_cParser(void) + { + PyObject* m; + + _utf8CharacterLengthTable_init(); + +- m = Py_InitModule3("cParser", cParser_methods, +- "Example module that creates an extension type."); +- ++ m = PyModule_Create(&moduledef); + + REGISTER_TYPE(AbstractRuleParams) + +@@ -3203,4 +3215,6 @@ initcParser(void) + REGISTER_TYPE(Context) + REGISTER_TYPE(ContextSwitcher) + REGISTER_TYPE(Parser) ++ ++ return m; + } diff --git a/sources b/sources index d230852..7574e9e 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -d6c32ee9b2a873c718c0d23a11e937bc qutepart-2.2.2.tar.gz +bfeee66e09cd1191e5f5f2d9c93cf3c7 qutepart-2.2.3.tar.gz