eric6/QScintilla/Editor.py

branch
maintenance
changeset 7286
7eb04391adf7
parent 7214
f434af227a41
parent 7278
1820a0344b62
child 7322
cd8ee889589f
--- a/eric6/QScintilla/Editor.py	Mon Sep 09 18:52:08 2019 +0200
+++ b/eric6/QScintilla/Editor.py	Thu Oct 03 11:12:10 2019 +0200
@@ -6,22 +6,20 @@
 """
 Module implementing the editor component of the eric6 IDE.
 """
-from __future__ import unicode_literals
-try:
-    str = unicode
-    chr = unichr
-except NameError:
-    pass
+
 
 import os
 import re
 import difflib
 
-from PyQt5.QtCore import QDir, QTimer, QModelIndex, QFileInfo, pyqtSignal, \
-    pyqtSlot, QCryptographicHash, QEvent, QDateTime, QRegExp, Qt, QPoint
+from PyQt5.QtCore import (
+    QDir, QTimer, QModelIndex, QFileInfo, pyqtSignal, pyqtSlot,
+    QCryptographicHash, QEvent, QDateTime, QRegExp, Qt, QPoint
+)
 from PyQt5.QtGui import QCursor, QPalette, QFont, QPixmap, QPainter
-from PyQt5.QtWidgets import QLineEdit, QActionGroup, QDialog, QInputDialog, \
-    QApplication, QMenu
+from PyQt5.QtWidgets import (
+    QLineEdit, QActionGroup, QDialog, QInputDialog, QApplication, QMenu
+)
 from PyQt5.QtPrintSupport import QPrinter, QPrintDialog, QAbstractPrintDialog
 from PyQt5.Qsci import QsciScintilla, QsciMacro, QsciStyledText
 
@@ -35,7 +33,6 @@
 import Preferences
 import Utilities
 from Utilities import MouseUtilities
-from Globals import qVersionTuple
 
 import UI.PixmapCache
 
@@ -315,14 +312,18 @@
             self.errorline = self.markerDefine(
                 UI.PixmapCache.getPixmap("errorLineMarker.png"))
         
-        self.breakpointMask = (1 << self.breakpoint) | \
-                              (1 << self.cbreakpoint) | \
-                              (1 << self.tbreakpoint) | \
-                              (1 << self.tcbreakpoint) | \
-                              (1 << self.dbreakpoint)
-        
-        self.changeMarkersMask = (1 << self.__changeMarkerSaved) | \
-                                 (1 << self.__changeMarkerUnsaved)
+        self.breakpointMask = (
+            (1 << self.breakpoint) |
+            (1 << self.cbreakpoint) |
+            (1 << self.tbreakpoint) |
+            (1 << self.tcbreakpoint) |
+            (1 << self.dbreakpoint)
+        )
+        
+        self.changeMarkersMask = (
+            (1 << self.__changeMarkerSaved) |
+            (1 << self.__changeMarkerUnsaved)
+        )
         
         self.__markerMap = EditorMarkerMap(self)
         
@@ -352,8 +353,10 @@
         self.isResourcesFile = False
         if editor is None:
             if self.fileName:
-                if (QFileInfo(self.fileName).size() // 1024) > \
-                   Preferences.getEditor("WarnFilesize"):
+                if (
+                    (QFileInfo(self.fileName).size() // 1024) >
+                        Preferences.getEditor("WarnFilesize")
+                ):
                     res = E5MessageBox.yesNo(
                         self,
                         self.tr("Open File"),
@@ -371,6 +374,8 @@
                 self.checkSyntax()
                 self.isResourcesFile = self.fileName.endswith(".qrc")
                 
+                self.__convertTabs()
+                
                 self.recolor()
         else:
             # clone the given editor
@@ -510,9 +515,11 @@
         # initialize the online change trace timer
         self.__initOnlineChangeTrace()
         
-        if self.fileName and \
-           self.project.isOpen() and \
-           self.project.isProjectSource(self.fileName):
+        if (
+            self.fileName and
+            self.project.isOpen() and
+            self.project.isProjectSource(self.fileName)
+        ):
             self.project.projectPropertiesChanged.connect(
                 self.__projectPropertiesChanged)
         
@@ -1017,8 +1024,9 @@
         languages = sorted(list(supportedLanguages.keys()))
         for language in languages:
             if language != "Guessed":
-                self.supportedLanguages[language] = \
+                self.supportedLanguages[language] = (
                     supportedLanguages[language][:2]
+                )
                 act = menu.addAction(
                     UI.PixmapCache.getIcon(supportedLanguages[language][2]),
                     self.supportedLanguages[language][0])
@@ -1174,10 +1182,11 @@
             self.__menuToggleBreakpointEnabled)
         self.marginMenuActs["NextBreakpoint"] = self.bpMarginMenu.addAction(
             self.tr('Next breakpoint'), self.menuNextBreakpoint)
-        self.marginMenuActs["PreviousBreakpoint"] = \
+        self.marginMenuActs["PreviousBreakpoint"] = (
             self.bpMarginMenu.addAction(
                 self.tr('Previous breakpoint'),
                 self.menuPreviousBreakpoint)
+        )
         self.marginMenuActs["ClearBreakpoint"] = self.bpMarginMenu.addAction(
             self.tr('Clear all breakpoints'), self.__menuClearBreakpoints)
         
@@ -1187,32 +1196,38 @@
         # fold margin
         self.foldMarginMenu = QMenu()
         
-        self.marginMenuActs["ToggleAllFolds"] = \
+        self.marginMenuActs["ToggleAllFolds"] = (
             self.foldMarginMenu.addAction(
                 self.tr("Toggle all folds"),
                 self.foldAll)
-        self.marginMenuActs["ToggleAllFoldsAndChildren"] = \
+        )
+        self.marginMenuActs["ToggleAllFoldsAndChildren"] = (
             self.foldMarginMenu.addAction(
                 self.tr("Toggle all folds (including children)"),
                 lambda: self.foldAll(True))
-        self.marginMenuActs["ToggleCurrentFold"] = \
+        )
+        self.marginMenuActs["ToggleCurrentFold"] = (
             self.foldMarginMenu.addAction(
                 self.tr("Toggle current fold"),
                 self.toggleCurrentFold)
+        )
         self.foldMarginMenu.addSeparator()
-        self.marginMenuActs["ExpandChildren"] = \
+        self.marginMenuActs["ExpandChildren"] = (
             self.foldMarginMenu.addAction(
                 self.tr("Expand (including children)"),
                 self.__contextMenuExpandFoldWithChildren)
-        self.marginMenuActs["CollapseChildren"] = \
+        )
+        self.marginMenuActs["CollapseChildren"] = (
             self.foldMarginMenu.addAction(
                 self.tr("Collapse (including children)"),
                 self.__contextMenuCollapseFoldWithChildren)
+        )
         self.foldMarginMenu.addSeparator()
-        self.marginMenuActs["ClearAllFolds"] = \
+        self.marginMenuActs["ClearAllFolds"] = (
             self.foldMarginMenu.addAction(
                 self.tr("Clear all folds"),
                 self.clearFolds)
+        )
         
         self.foldMarginMenu.aboutToShow.connect(
             lambda: self.__showContextMenuMargin(self.foldMarginMenu))
@@ -1220,53 +1235,67 @@
         # indicator margin
         self.indicMarginMenu = QMenu()
         
-        self.marginMenuActs["GotoSyntaxError"] = \
+        self.marginMenuActs["GotoSyntaxError"] = (
             self.indicMarginMenu.addAction(
                 self.tr('Goto syntax error'), self.gotoSyntaxError)
-        self.marginMenuActs["ShowSyntaxError"] = \
+        )
+        self.marginMenuActs["ShowSyntaxError"] = (
             self.indicMarginMenu.addAction(
                 self.tr('Show syntax error message'),
                 self.__showSyntaxError)
-        self.marginMenuActs["ClearSyntaxError"] = \
+        )
+        self.marginMenuActs["ClearSyntaxError"] = (
             self.indicMarginMenu.addAction(
                 self.tr('Clear syntax error'), self.clearSyntaxError)
+        )
         self.indicMarginMenu.addSeparator()
-        self.marginMenuActs["NextWarningMarker"] = \
+        self.marginMenuActs["NextWarningMarker"] = (
             self.indicMarginMenu.addAction(
                 self.tr("Next warning"), self.nextWarning)
-        self.marginMenuActs["PreviousWarningMarker"] = \
+        )
+        self.marginMenuActs["PreviousWarningMarker"] = (
             self.indicMarginMenu.addAction(
                 self.tr("Previous warning"), self.previousWarning)
-        self.marginMenuActs["ShowWarning"] = \
+        )
+        self.marginMenuActs["ShowWarning"] = (
             self.indicMarginMenu.addAction(
                 self.tr('Show warning message'), self.__showWarning)
-        self.marginMenuActs["ClearWarnings"] = \
+        )
+        self.marginMenuActs["ClearWarnings"] = (
             self.indicMarginMenu.addAction(
                 self.tr('Clear warnings'), self.clearWarnings)
+        )
         self.indicMarginMenu.addSeparator()
-        self.marginMenuActs["NextCoverageMarker"] = \
+        self.marginMenuActs["NextCoverageMarker"] = (
             self.indicMarginMenu.addAction(
                 self.tr('Next uncovered line'), self.nextUncovered)
-        self.marginMenuActs["PreviousCoverageMarker"] = \
+        )
+        self.marginMenuActs["PreviousCoverageMarker"] = (
             self.indicMarginMenu.addAction(
                 self.tr('Previous uncovered line'), self.previousUncovered)
+        )
         self.indicMarginMenu.addSeparator()
-        self.marginMenuActs["NextTaskMarker"] = \
+        self.marginMenuActs["NextTaskMarker"] = (
             self.indicMarginMenu.addAction(
                 self.tr('Next task'), self.nextTask)
-        self.marginMenuActs["PreviousTaskMarker"] = \
+        )
+        self.marginMenuActs["PreviousTaskMarker"] = (
             self.indicMarginMenu.addAction(
                 self.tr('Previous task'), self.previousTask)
+        )
         self.indicMarginMenu.addSeparator()
-        self.marginMenuActs["NextChangeMarker"] = \
+        self.marginMenuActs["NextChangeMarker"] = (
             self.indicMarginMenu.addAction(
                 self.tr('Next change'), self.nextChange)
-        self.marginMenuActs["PreviousChangeMarker"] = \
+        )
+        self.marginMenuActs["PreviousChangeMarker"] = (
             self.indicMarginMenu.addAction(
                 self.tr('Previous change'), self.previousChange)
-        self.marginMenuActs["ClearChangeMarkers"] = \
+        )
+        self.marginMenuActs["ClearChangeMarkers"] = (
             self.indicMarginMenu.addAction(
                 self.tr('Clear changes'), self.__reinitOnlineChangeTrace)
+        )
         
         self.indicMarginMenu.aboutToShow.connect(
             lambda: self.__showContextMenuMargin(self.indicMarginMenu))
@@ -1392,8 +1421,11 @@
         
         @keyparam propagate flag indicating to propagate the change (boolean)
         """
-        if self.lexer_ is not None and \
-           (self.lexer_.lexer() == "container" or self.lexer_.lexer() is None):
+        if (
+            self.lexer_ is not None and
+            (self.lexer_.lexer() == "container" or
+             self.lexer_.lexer() is None)
+        ):
             self.SCN_STYLENEEDED.disconnect(self.__styleNeeded)
         
         self.apiLanguage = ""
@@ -1493,8 +1525,8 @@
         Private method to check the selected encoding of the encodings submenu.
         """
         try:
-            self.supportedEncodings[self.__normalizedEncoding()]\
-                .setChecked(True)
+            (self.supportedEncodings[self.__normalizedEncoding()]
+             .setChecked(True))
         except (AttributeError, KeyError):
             pass
         
@@ -1522,9 +1554,12 @@
         """
         if not encoding:
             encoding = self.encoding
-        return encoding.replace("-default", "")\
-                       .replace("-guessed", "")\
-                       .replace("-selected", "")
+        return (
+            encoding
+            .replace("-default", "")
+            .replace("-guessed", "")
+            .replace("-selected", "")
+        )
         
     def __showContextMenuEol(self):
         """
@@ -1571,16 +1606,21 @@
             language (string)
         @keyparam pyname name of the pygments lexer to use (string)
         """
-        if self.lexer_ is not None and \
-           (self.lexer_.lexer() == "container" or self.lexer_.lexer() is None):
+        if (
+            self.lexer_ is not None and
+            (self.lexer_.lexer() == "container" or
+             self.lexer_.lexer() is None)
+        ):
             self.SCN_STYLENEEDED.disconnect(self.__styleNeeded)
         
         language = ""
         if not self.filetype:
             if filename:
                 basename = os.path.basename(filename)
-                if self.project.isOpen() and \
-                        self.project.isProjectFile(filename):
+                if (
+                    self.project.isOpen() and
+                    self.project.isProjectFile(filename)
+                ):
                     language = self.project.getEditorLexerAssoc(basename)
                 if not language:
                     language = Preferences.getEditorLexerAssoc(basename)
@@ -1688,8 +1728,10 @@
             names for Pygments (boolean)
         @return language of the editor (string)
         """
-        if self.apiLanguage == "Guessed" or \
-                self.apiLanguage.startswith("Pygments|"):
+        if (
+            self.apiLanguage == "Guessed" or
+            self.apiLanguage.startswith("Pygments|")
+        ):
             lang = self.lexer_.name()
             if normalized:
                 # adjust some Pygments lexer names
@@ -1937,14 +1979,18 @@
         
         if self.filetype == "":
             line0 = self.text(0)
-            if line0.startswith("#!") and \
-               "ruby" in line0:
+            if (
+                line0.startswith("#!") and
+                "ruby" in line0
+            ):
                 self.filetype = "Ruby"
                 return True
             
-            if bool(self.fileName) and \
-               os.path.splitext(self.fileName)[1] in \
-                    self.dbs.getExtensions('Ruby'):
+            if (
+                bool(self.fileName) and
+                os.path.splitext(self.fileName)[1] in
+                    self.dbs.getExtensions('Ruby')
+            ):
                 self.filetype = "Ruby"
                 return True
         
@@ -1960,8 +2006,10 @@
             return True
         
         if self.filetype == "":
-            if self.fileName and \
-               os.path.splitext(self.fileName)[1] == ".js":
+            if (
+                self.fileName and
+                os.path.splitext(self.fileName)[1] == ".js"
+            ):
                 self.filetype = "JavaScript"
                 return True
         
@@ -2038,12 +2086,15 @@
         @param annotationLinesAdded number of added/deleted annotation lines
             (integer)
         """
-        if mtype & (self.SC_MOD_INSERTTEXT | self.SC_MOD_DELETETEXT) and \
-           linesAdded != 0:
+        if (
+            mtype & (self.SC_MOD_INSERTTEXT | self.SC_MOD_DELETETEXT) and
+            linesAdded != 0
+        ):
             if self.breaks:
                 bps = []    # list of breakpoints
-                for handle, (ln, cond, temp, enabled, ignorecount) in \
-                        self.breaks.items():
+                for handle, (ln, cond, temp, enabled, ignorecount) in (
+                    self.breaks.items()
+                ):
                     line = self.markerLine(handle) + 1
                     if ln != line:
                         bps.append((ln, line))
@@ -2115,8 +2166,9 @@
         """
         for row in range(start, end + 1):
             index = self.breakpointModel.index(row, 0, parentIndex)
-            fn, line, cond, temp, enabled, ignorecount = \
+            fn, line, cond, temp, enabled, ignorecount = (
                 self.breakpointModel.getBreakPointByIndex(index)[:6]
+            )
             if fn == self.fileName:
                 self.newBreakpointWithProperties(
                     line, (cond, temp, enabled, ignorecount))
@@ -2177,9 +2229,11 @@
                 # delete breakpoint or toggle it to the next state
                 index = self.breakpointModel.getBreakPointIndex(
                     self.fileName, line)
-                if Preferences.getDebugger("ThreeStateBreakPoints") and \
-                        not self.breakpointModel.isBreakPointTemporaryByIndex(
-                            index):
+                if (
+                    Preferences.getDebugger("ThreeStateBreakPoints") and
+                    not self.breakpointModel.isBreakPointTemporaryByIndex(
+                        index)
+                ):
                     self.breakpointModel.deleteBreakPointByIndex(index)
                     self.__addBreakPoint(line, True)
                 else:
@@ -2953,23 +3007,16 @@
         
         modified = False
         
-        if (not self.__getEditorConfig("TabForIndentation")) and \
-                Preferences.getEditor("ConvertTabsOnLoad") and \
-                not (self.lexer_ and
-                     self.lexer_.alwaysKeepTabs()):
-            txtExpanded = txt.expandtabs(self.__getEditorConfig("TabWidth"))
-            if txtExpanded != txt:
-                modified = True
-            txt = txtExpanded
-        
         self.setText(txt)
         
         # get eric specific flags
         self.__processFlags()
         
         # perform automatic EOL conversion
-        if self.__getEditorConfig("EOLMode", nodefault=True) or \
-           Preferences.getEditor("AutomaticEOLConversion"):
+        if (
+            self.__getEditorConfig("EOLMode", nodefault=True) or
+            Preferences.getEditor("AutomaticEOLConversion")
+        ):
             self.convertEols(self.eolMode())
         else:
             fileEol = self.detectEolString(txt)
@@ -2981,7 +3028,26 @@
         
         self.setModified(modified)
         self.lastModified = QFileInfo(self.fileName).lastModified()
-        
+    
+    def __convertTabs(self):
+        """
+        Private slot to convert tabulators to spaces.
+        """
+        if (
+            (not self.__getEditorConfig("TabForIndentation")) and
+            Preferences.getEditor("ConvertTabsOnLoad") and
+            not (self.lexer_ and
+                 self.lexer_.alwaysKeepTabs())
+        ):
+            txt = self.text()
+            txtExpanded = txt.expandtabs(self.__getEditorConfig("TabWidth"))
+            if txtExpanded != txt:
+                self.beginUndoAction
+                self.setText(txt)
+                self.endUndoAction()
+                
+                self.setModified(True)
+    
     def __removeTrailingWhitespace(self):
         """
         Private method to remove trailing whitespace.
@@ -3073,8 +3139,10 @@
         """
         # save to project, if a project is loaded
         if self.project.isOpen():
-            if self.fileName and \
-               self.project.startswithProjectPath(self.fileName):
+            if (
+                self.fileName and
+                self.project.startswithProjectPath(self.fileName)
+            ):
                 path = os.path.dirname(self.fileName)
             else:
                 path = self.project.getProjectPath()
@@ -3082,8 +3150,10 @@
         if not path and self.fileName:
             path = os.path.dirname(self.fileName)
         if not path:
-            path = Preferences.getMultiProject("Workspace") or \
+            path = (
+                Preferences.getMultiProject("Workspace") or
                 Utilities.getHomeDir()
+            )
         
         from . import Lexers
         if self.fileName:
@@ -3141,8 +3211,10 @@
         res = self.writeFile(fn)
         if res:
             # save to project, if a project is loaded
-            if self.project.isOpen() and \
-                    self.project.startswithProjectPath(fn):
+            if (
+                self.project.isOpen() and
+                self.project.startswithProjectPath(fn)
+            ):
                 self.project.appendFile(fn)
         
         return res
@@ -3169,8 +3241,10 @@
             newName = fn
             
             # save to project, if a project is loaded
-            if self.project.isOpen() and \
-                    self.project.startswithProjectPath(fn):
+            if (
+                self.project.isOpen() and
+                self.project.startswithProjectPath(fn)
+            ):
                 editorConfigEol = self.__getEditorConfig(
                     "EOLMode", nodefault=True,
                     config=self.__loadEditorConfigObject(fn))
@@ -3202,8 +3276,10 @@
                 self.editorRenamed.emit(self.fileName)
                 
                 # save to project, if a project is loaded
-                if self.project.isOpen() and \
-                        self.project.startswithProjectPath(fn):
+                if (
+                    self.project.isOpen() and
+                    self.project.startswithProjectPath(fn)
+                ):
                     self.project.appendFile(fn)
                     self.addedToProject()
                 
@@ -3628,9 +3704,11 @@
         
         # check if line starts with our comment string (i.e. was commented
         # by our comment...() slots
-        if self.hasSelectedText() and \
-                self.__isCommentedLine(self.text(self.getSelection()[0]),
-                                       commentStr):
+        if (
+            self.hasSelectedText() and
+            self.__isCommentedLine(self.text(self.getSelection()[0]),
+                                   commentStr)
+        ):
             self.uncommentLineOrSelection()
         elif not self.__isCommentedLine(self.text(line), commentStr):
             # it doesn't, so comment the line or selection
@@ -3638,14 +3716,18 @@
         else:
             # determine the start of the comment block
             begline = line
-            while begline > 0 and \
-                    self.__isCommentedLine(self.text(begline - 1), commentStr):
+            while (
+                begline > 0 and
+                self.__isCommentedLine(self.text(begline - 1), commentStr)
+            ):
                 begline -= 1
             # determine the end of the comment block
             endline = line
             lines = self.lines()
-            while endline < lines and \
-                    self.__isCommentedLine(self.text(endline + 1), commentStr):
+            while (
+                endline < lines and
+                self.__isCommentedLine(self.text(endline + 1), commentStr)
+            ):
                 endline += 1
             
             self.setSelection(begline, 0, endline, self.lineLength(endline))
@@ -4274,23 +4356,27 @@
         self.setMarginSensitivity(self.__bmMargin, True)
         self.setMarginMarkerMask(self.__bmMargin, marginBmMask)
         
-        marginBpMask = (1 << self.breakpoint) | \
-                       (1 << self.cbreakpoint) | \
-                       (1 << self.tbreakpoint) | \
-                       (1 << self.tcbreakpoint) | \
-                       (1 << self.dbreakpoint)
+        marginBpMask = (
+            (1 << self.breakpoint) |
+            (1 << self.cbreakpoint) |
+            (1 << self.tbreakpoint) |
+            (1 << self.tcbreakpoint) |
+            (1 << self.dbreakpoint)
+        )
         self.setMarginWidth(self.__bpMargin, 16)
         self.setMarginSensitivity(self.__bpMargin, True)
         self.setMarginMarkerMask(self.__bpMargin, marginBpMask)
         
-        marginIndicMask = (1 << self.syntaxerror) | \
-                          (1 << self.notcovered) | \
-                          (1 << self.taskmarker) | \
-                          (1 << self.warning) | \
-                          (1 << self.__changeMarkerUnsaved) | \
-                          (1 << self.__changeMarkerSaved) | \
-                          (1 << self.currentline) | \
-                          (1 << self.errorline)
+        marginIndicMask = (
+            (1 << self.syntaxerror) |
+            (1 << self.notcovered) |
+            (1 << self.taskmarker) |
+            (1 << self.warning) |
+            (1 << self.__changeMarkerUnsaved) |
+            (1 << self.__changeMarkerSaved) |
+            (1 << self.currentline) |
+            (1 << self.errorline)
+        )
         self.setMarginWidth(self.__indicMargin, 16)
         self.setMarginSensitivity(self.__indicMargin, True)
         self.setMarginMarkerMask(self.__indicMargin, marginIndicMask)
@@ -4432,9 +4518,11 @@
         self.indicatorDefine(
             self.searchIndicator, QsciScintilla.INDIC_BOX,
             Preferences.getEditorColour("SearchMarkers"))
-        if not Preferences.getEditor("SearchMarkersEnabled") and \
-           not Preferences.getEditor("QuickSearchMarkersEnabled") and \
-           not Preferences.getEditor("MarkOccurrencesEnabled"):
+        if (
+            not Preferences.getEditor("SearchMarkersEnabled") and
+            not Preferences.getEditor("QuickSearchMarkersEnabled") and
+            not Preferences.getEditor("MarkOccurrencesEnabled")
+        ):
             self.clearAllIndicators(self.searchIndicator)
         
         self.spellingIndicator = QsciScintilla.INDIC_CONTAINER + 1
@@ -4472,9 +4560,11 @@
         """
         Private method to configure the eol mode of the editor.
         """
-        if self.fileName and \
-           self.project.isOpen() and \
-           self.project.isProjectFile(self.fileName):
+        if (
+            self.fileName and
+            self.project.isOpen() and
+            self.project.isProjectFile(self.fileName)
+        ):
             eolMode = self.__getEditorConfig("EOLMode", nodefault=True)
             if eolMode is None:
                 eolStr = self.project.getEolString()
@@ -4533,8 +4623,9 @@
         if Preferences.getEditor("CallTipsEnabled"):
             if calltipsStyle == QsciScintilla.CallTipsNoContext:
                 self.setCallTipsStyle(QsciScintilla.CallTipsNoContext)
-            elif calltipsStyle == \
-                    QsciScintilla.CallTipsNoAutoCompletionContext:
+            elif (
+                calltipsStyle == QsciScintilla.CallTipsNoAutoCompletionContext
+            ):
                 self.setCallTipsStyle(
                     QsciScintilla.CallTipsNoAutoCompletionContext)
             else:
@@ -4586,8 +4677,8 @@
             (boolean)
         """
         if enable:
-            autoCompletionSource = \
-                Preferences.getEditor("AutoCompletionSource")
+            autoCompletionSource = Preferences.getEditor(
+                "AutoCompletionSource")
             if autoCompletionSource == QsciScintilla.AcsDocument:
                 self.setAutoCompletionSource(QsciScintilla.AcsDocument)
             elif autoCompletionSource == QsciScintilla.AcsAPIs:
@@ -4616,8 +4707,10 @@
         """
         char = chr(charNumber)
         # update code documentation viewer
-        if char == "(" and \
-           Preferences.getDocuViewer("ShowInfoOnOpenParenthesis"):
+        if (
+            char == "(" and
+            Preferences.getDocuViewer("ShowInfoOnOpenParenthesis")
+        ):
             self.vm.showEditorInfo(self)
             
         if self.isListActive():
@@ -4630,8 +4723,10 @@
             else:
                 self.__acTimer.stop()
         
-        if self.callTipsStyle() != QsciScintilla.CallTipsNone and \
-           self.lexer_ is not None and chr(charNumber) in '()':
+        if (
+            self.callTipsStyle() != QsciScintilla.CallTipsNone and
+            self.lexer_ is not None and chr(charNumber) in '()'
+        ):
             self.callTip()
         
         if not self.isCallTipActive():
@@ -4692,8 +4787,10 @@
         @param asynchroneous flag indicating an asynchroneous function
         @type bool
         """
-        if key in self.__completionListHookFunctions or \
-           key in self.__completionListAsyncHookFunctions:
+        if (
+            key in self.__completionListHookFunctions or
+            key in self.__completionListAsyncHookFunctions
+        ):
             # it was already registered
             E5MessageBox.warning(
                 self,
@@ -4748,8 +4845,10 @@
         if self.isListActive():
             self.cancelList()
         
-        if self.__completionListHookFunctions or \
-           self.__completionListAsyncHookFunctions:
+        if (
+            self.__completionListHookFunctions or
+            self.__completionListAsyncHookFunctions
+        ):
             # Avoid delayed auto-completion after cursor repositioning
             self.__acText = self.__getAcText()
             if auto and Preferences.getEditor("AutoCompletionTimeout"):
@@ -4852,8 +4951,10 @@
             self.__acWatchdog.stop()
             
             # Autocomplete with QScintilla if no results present
-            if Preferences.getEditor("AutoCompletionScintillaOnFail") and \
-               not self.__acCompletions:
+            if (
+                Preferences.getEditor("AutoCompletionScintillaOnFail") and
+                not self.__acCompletions
+            ):
                 self.autoCompleteQScintilla()
                 return
         
@@ -5175,9 +5276,12 @@
         evt.accept()
         if self.__marginNumber(evt.x()) == -1:
             self.spellingMenuPos = self.positionFromPoint(evt.pos())
-            if self.spellingMenuPos >= 0 and \
-               self.spell is not None and \
-               self.hasIndicator(self.spellingIndicator, self.spellingMenuPos):
+            if (
+                self.spellingMenuPos >= 0 and
+                self.spell is not None and
+                self.hasIndicator(self.spellingIndicator,
+                                  self.spellingMenuPos)
+            ):
                 self.spellingMenu.popup(evt.globalPos())
             else:
                 self.menu.popup(evt.globalPos())
@@ -5214,8 +5318,10 @@
                 self.menuActs["Show"].setEnabled(True)
             else:
                 self.menuActs["Show"].setEnabled(False)
-            if self.fileName and \
-               (self.isPyFile() or self.isRubyFile()):
+            if (
+                self.fileName and
+                (self.isPyFile() or self.isRubyFile())
+            ):
                 self.menuActs["Diagrams"].setEnabled(True)
             else:
                 self.menuActs["Diagrams"].setEnabled(False)
@@ -5295,22 +5401,27 @@
         coEnable = False
         
         # first check if the file belongs to a project
-        if self.project.isOpen() and \
-                self.project.isProjectSource(self.fileName):
+        if (
+            self.project.isOpen() and
+            self.project.isProjectSource(self.fileName)
+        ):
             fn = self.project.getMainScript(True)
             if fn is not None:
                 tfn = Utilities.getTestFileName(fn)
                 basename = os.path.splitext(fn)[0]
                 tbasename = os.path.splitext(tfn)[0]
-                prEnable = prEnable or \
-                    os.path.isfile("{0}.profile".format(basename)) or \
+                prEnable = (
+                    prEnable or
+                    os.path.isfile("{0}.profile".format(basename)) or
                     os.path.isfile("{0}.profile".format(tbasename))
+                )
                 coEnable = (
-                    coEnable or
-                    os.path.isfile("{0}.coverage".format(basename)) or
-                    os.path.isfile("{0}.coverage".format(tbasename))) and \
+                    (coEnable or
+                     os.path.isfile("{0}.coverage".format(basename)) or
+                     os.path.isfile("{0}.coverage".format(tbasename))) and
                     (self.project.isPy3Project() or
-                        self.project.isPy2Project())
+                     self.project.isPy2Project())
+                )
         
         # now check ourselves
         fn = self.getFileName()
@@ -5318,14 +5429,17 @@
             tfn = Utilities.getTestFileName(fn)
             basename = os.path.splitext(fn)[0]
             tbasename = os.path.splitext(tfn)[0]
-            prEnable = prEnable or \
-                os.path.isfile("{0}.profile".format(basename)) or \
+            prEnable = (
+                prEnable or
+                os.path.isfile("{0}.profile".format(basename)) or
                 os.path.isfile("{0}.profile".format(tbasename))
+            )
             coEnable = (
-                coEnable or
-                os.path.isfile("{0}.coverage".format(basename)) or
-                os.path.isfile("{0}.coverage".format(tbasename))) and \
+                (coEnable or
+                 os.path.isfile("{0}.coverage".format(basename)) or
+                 os.path.isfile("{0}.coverage".format(tbasename))) and
                 self.isPyFile()
+            )
         
         # now check for syntax errors
         if self.hasSyntaxErrors():
@@ -5345,8 +5459,10 @@
         Private slot handling the aboutToShow signal of the diagrams context
         menu.
         """
-        if self.project.isOpen() and \
-                self.project.isProjectSource(self.fileName):
+        if (
+            self.project.isOpen() and
+            self.project.isProjectSource(self.fileName)
+        ):
             self.applicationDiagramMenuAct.setEnabled(True)
         else:
             self.applicationDiagramMenuAct.setEnabled(False)
@@ -5409,8 +5525,10 @@
             
             self.marginMenuActs["GotoSyntaxError"].setEnabled(hasSyntaxErrors)
             self.marginMenuActs["ClearSyntaxError"].setEnabled(hasSyntaxErrors)
-            if hasSyntaxErrors and \
-               self.markersAtLine(self.line) & (1 << self.syntaxerror):
+            if (
+                hasSyntaxErrors and
+                self.markersAtLine(self.line) & (1 << self.syntaxerror)
+            ):
                 self.marginMenuActs["ShowSyntaxError"].setEnabled(True)
             else:
                 self.marginMenuActs["ShowSyntaxError"].setEnabled(False)
@@ -5419,8 +5537,10 @@
             self.marginMenuActs["PreviousWarningMarker"].setEnabled(
                 hasWarnings)
             self.marginMenuActs["ClearWarnings"].setEnabled(hasWarnings)
-            if hasWarnings and \
-               self.markersAtLine(self.line) & (1 << self.warning):
+            if (
+                hasWarnings and
+                self.markersAtLine(self.line) & (1 << self.warning)
+            ):
                 self.marginMenuActs["ShowWarning"].setEnabled(True)
             else:
                 self.marginMenuActs["ShowWarning"].setEnabled(False)
@@ -5467,6 +5587,7 @@
         """
         encoding = act.data()
         self.readFile(self.fileName, encoding=encoding)
+        self.__convertTabs()
         self.__checkEncoding()
         
     def __contextSave(self):
@@ -5544,8 +5665,10 @@
         if line1Text in ["", "\r", "\n", "\r\n"]:
             return
         
-        if line0Text.rstrip("\r\n\\ \t").endswith(("'", '"')) and \
-                line1Text.lstrip().startswith(("'", '"')):
+        if (
+            line0Text.rstrip("\r\n\\ \t").endswith(("'", '"')) and
+            line1Text.lstrip().startswith(("'", '"'))
+        ):
             # merging multi line strings
             startChars = "\r\n\\ \t'\""
             endChars = " \t'\""
@@ -5600,9 +5723,11 @@
         
         @return flag indicating this editor should be saved (boolean)
         """
-        return bool(self.fileName) and \
-            not self.autosaveManuallyDisabled and \
+        return (
+            bool(self.fileName) and
+            not self.autosaveManuallyDisabled and
             not self.isReadOnly()
+        )
 
     def checkSyntax(self):
         """
@@ -5613,8 +5738,10 @@
             # adjustment for MicroPython
             fileType = "Python3"
         
-        if self.syntaxCheckService is None or \
-                fileType not in self.syntaxCheckService.getLanguages():
+        if (
+            self.syntaxCheckService is None or
+            fileType not in self.syntaxCheckService.getLanguages()
+        ):
             return
         
         if Preferences.getEditor("AutoCheckSyntax"):
@@ -5715,8 +5842,10 @@
         
         # first check if the file belongs to a project and there is
         # a project coverage file
-        if self.project.isOpen() and \
-                self.project.isProjectSource(self.fileName):
+        if (
+            self.project.isOpen() and
+            self.project.isProjectSource(self.fileName)
+        ):
             fn = self.project.getMainScript(True)
             if fn is not None:
                 tfn = Utilities.getTestFileName(fn)
@@ -5893,8 +6022,10 @@
         
         # first check if the file belongs to a project and there is
         # a project profile file
-        if self.project.isOpen() and \
-                self.project.isProjectSource(self.fileName):
+        if (
+            self.project.isOpen() and
+            self.project.isProjectSource(self.fileName)
+        ):
             fn = self.project.getMainScript(True)
             if fn is not None:
                 tfn = Utilities.getTestFileName(fn)
@@ -5987,8 +6118,10 @@
                 self.syntaxerrorToggled.emit(self)
             else:
                 for handle in list(self.syntaxerrors.keys()):
-                    if self.markerLine(handle) == line - 1 and \
-                       (msg, index) not in self.syntaxerrors[handle]:
+                    if (
+                        self.markerLine(handle) == line - 1 and
+                        (msg, index) not in self.syntaxerrors[handle]
+                    ):
                         self.syntaxerrors[handle].append((msg, index))
             if show:
                 self.setCursorPosition(line - 1, index)
@@ -6142,8 +6275,10 @@
                 self.syntaxerrorToggled.emit(self)
             else:
                 for handle in list(self.warnings.keys()):
-                    if self.markerLine(handle) == line - 1 and \
-                       warn not in self.warnings[handle]:
+                    if (
+                        self.markerLine(handle) == line - 1 and
+                        warn not in self.warnings[handle]
+                    ):
                         self.warnings[handle].append(warn)
         else:
             for handle in list(self.warnings.keys()):
@@ -6310,8 +6445,9 @@
         Private slot to define the style used by inline annotations.
         """
         if hasattr(QsciScintilla, "annotate"):
-            self.annotationWarningStyle = \
+            self.annotationWarningStyle = (
                 QsciScintilla.STYLE_LASTPREDEFINED + 1
+            )
             self.SendScintilla(
                 QsciScintilla.SCI_STYLESETFORE,
                 self.annotationWarningStyle,
@@ -6401,8 +6537,10 @@
         """
         if hasattr(QsciScintilla, "annotate"):
             self.clearAnnotations()
-            for handle in list(self.warnings.keys()) + \
-                    list(self.syntaxerrors.keys()):
+            for handle in (
+                list(self.warnings.keys()) +
+                list(self.syntaxerrors.keys())
+            ):
                 line = self.markerLine(handle)
                 self.__setAnnotation(line)
     
@@ -6733,18 +6871,20 @@
         except AttributeError:
             pass
         self.__updateReadOnly(False)
-        if self.vm.editorsCheckFocusInEnabled() and \
-           not self.inReopenPrompt and self.fileName and \
-           QFileInfo(self.fileName).lastModified().toString() != \
-                self.lastModified.toString():
+        if (
+            self.vm.editorsCheckFocusInEnabled() and
+            not self.inReopenPrompt and self.fileName and
+            QFileInfo(self.fileName).lastModified().toString() !=
+                self.lastModified.toString()
+        ):
             self.inReopenPrompt = True
             if Preferences.getEditor("AutoReopen") and not self.isModified():
                 self.refresh()
             else:
                 msg = self.tr(
                     """<p>The file <b>{0}</b> has been changed while it"""
-                    """ was opened in eric6. Reread it?</p>""")\
-                    .format(self.fileName)
+                    """ was opened in eric6. Reread it?</p>"""
+                ).format(self.fileName)
                 yesDefault = True
                 if self.isModified():
                     msg += self.tr(
@@ -6790,8 +6930,10 @@
         
         @param evt the event, that was generated (QEvent)
         """
-        if evt.type() == QEvent.WindowStateChange and \
-           bool(self.fileName):
+        if (
+            evt.type() == QEvent.WindowStateChange and
+            bool(self.fileName)
+        ):
             if self.windowState() == Qt.WindowStates(Qt.WindowMinimized):
                 cap = os.path.basename(self.fileName)
             else:
@@ -6817,10 +6959,7 @@
         
         @param evt reference to the wheel event (QWheelEvent)
         """
-        if qVersionTuple() >= (5, 0, 0):
-            delta = evt.angleDelta().y()
-        else:
-            delta = evt.delta()
+        delta = evt.angleDelta().y()
         if evt.modifiers() & Qt.ControlModifier:
             if delta < 0:
                 self.zoomOut()
@@ -6908,8 +7047,10 @@
         if self.fileName == "":
             return
         
-        readOnly = not QFileInfo(self.fileName).isWritable() or \
+        readOnly = (
+            not QFileInfo(self.fileName).isWritable() or
             self.isReadOnly()
+        )
         if not bForce and (readOnly == self.isReadOnly()):
             return
         
@@ -6954,6 +7095,7 @@
             # do not prompt for this change again...
             self.lastModified = QDateTime.currentDateTime()
         self.setModified(False)
+        self.__convertTabs()
         
         # re-initialize the online change tracer
         self.__reinitOnlineChangeTrace()
@@ -7213,8 +7355,10 @@
         if not self.checkDirty():
             return
         
-        package = os.path.isdir(self.fileName) and \
+        package = (
+            os.path.isdir(self.fileName) and
             self.fileName or os.path.dirname(self.fileName)
+        )
         res = E5MessageBox.yesNo(
             self,
             self.tr("Package Diagram"),
@@ -7233,8 +7377,10 @@
         if not self.checkDirty():
             return
         
-        package = os.path.isdir(self.fileName) and self.fileName \
-            or os.path.dirname(self.fileName)
+        package = (
+            os.path.isdir(self.fileName) and
+            self.fileName or os.path.dirname(self.fileName)
+        )
         res = E5MessageBox.yesNo(
             self,
             self.tr("Imports Diagram"),
@@ -7380,8 +7526,10 @@
         """
         Public slot to handle the opening of a project.
         """
-        if self.fileName and \
-           self.project.isProjectSource(self.fileName):
+        if (
+            self.fileName and
+            self.project.isProjectSource(self.fileName)
+        ):
             self.project.projectPropertiesChanged.connect(
                 self.__projectPropertiesChanged)
             self.setSpellingForProject()
@@ -7437,9 +7585,11 @@
         Public method to set the spell checking options for files belonging
         to the current project.
         """
-        if self.fileName and \
-           self.project.isOpen() and \
-           self.project.isProjectSource(self.fileName):
+        if (
+            self.fileName and
+            self.project.isOpen() and
+            self.project.isProjectSource(self.fileName)
+        ):
             pwl, pel = self.project.getProjectDictionaries()
             self.__setSpellingLanguage(self.project.getProjectSpellLanguage(),
                                        pwl=pwl, pel=pel)
@@ -7473,8 +7623,10 @@
         if self.__spellCheckStringsOnly:
             style = self.styleAt(pos)
             if self.lexer_ is not None:
-                return self.lexer_.isCommentStyle(style) or \
+                return (
+                    self.lexer_.isCommentStyle(style) or
                     self.lexer_.isStringStyle(style)
+                )
         return True
     
     @pyqtSlot(int)
@@ -7619,10 +7771,14 @@
             and if it is inside a remotely initiated shared edit session
             (boolean, boolean, boolean, boolean)
         """
-        return bool(self.fileName) and \
-            self.project.isOpen() and \
-            self.project.isProjectFile(self.fileName), \
-            self.__isShared, self.__inSharedEdit, self.__inRemoteSharedEdit
+        return (
+            (bool(self.fileName) and
+             self.project.isOpen() and
+             self.project.isProjectFile(self.fileName)),
+            self.__isShared,
+            self.__inSharedEdit,
+            self.__inRemoteSharedEdit
+        )
     
     def shareConnected(self, connected):
         """
@@ -7715,8 +7871,10 @@
         @param command command string (string)
         """
         if self.__isShared:
-            if self.__isSyncing and \
-               not command.startswith(Editor.SyncToken + Editor.Separator):
+            if (
+                self.__isSyncing and
+                not command.startswith(Editor.SyncToken + Editor.Separator)
+            ):
                 self.__receivedWhileSyncing.append(command)
             else:
                 self.__dispatchCommand(command)
@@ -7937,8 +8095,9 @@
         dlg = SortOptionsDialog()
         if dlg.exec_() == QDialog.Accepted:
             ascending, alnum, caseSensitive = dlg.getData()
-            origStartLine, origStartIndex, origEndLine, origEndIndex = \
+            origStartLine, origStartIndex, origEndLine, origEndIndex = (
                 self.getRectangularSelection()
+            )
             # convert to upper-left to lower-right
             startLine = min(origStartLine, origEndLine)
             startIndex = min(origStartIndex, origEndIndex)
@@ -8019,9 +8178,11 @@
         self.vm.eventFilter(self, evt)
         super(Editor, self).mouseReleaseEvent(evt)
         
-        if button != Qt.NoButton and \
-            Preferences.getEditor("MouseClickHandlersEnabled") and \
-                key in self.__mouseClickHandlers:
+        if (
+            button != Qt.NoButton and
+            Preferences.getEditor("MouseClickHandlersEnabled") and
+            key in self.__mouseClickHandlers
+        ):
             evt.accept()
             self.__mouseClickHandlers[key][1](self)
     
@@ -8200,7 +8361,11 @@
             if nodefault:
                 return None
             else:
-                return Preferences.getEditor(option)
+                value = self.__getOverrideValue(option)
+                if value is None:
+                    # no override
+                    value = Preferences.getEditor(option)
+                return value
         
         try:
             if option == "EOLMode":
@@ -8234,7 +8399,10 @@
         
         if value is None and not nodefault:
             # use Preferences in case of error
-            value = Preferences.getEditor(option)
+            value = self.__getOverrideValue(option)
+            if value is None:
+                # no override
+                value = Preferences.getEditor(option)
         
         return value
     
@@ -8249,6 +8417,25 @@
         """
         return self.__getEditorConfig(option)
     
+    def __getOverrideValue(self, option):
+        """
+        Private method to get an override value for the current file type.
+        
+        @param option Preferences option key
+        @type str
+        @return override value; None in case nothing is defined
+        @rtype any
+        """
+        if option in ("TabWidth", "IndentWidth"):
+            overrides = Preferences.getEditor("TabIndentOverride")
+            if self.filetype in overrides:
+                if option == "TabWidth":
+                    return overrides[self.filetype][0]
+                elif option == "IndentWidth":
+                    return overrides[self.filetype][1]
+        
+        return None
+    
     def mouseDoubleClickEvent(self, evt):
         """
         Protected method to handle mouse double click events.

eric ide

mercurial