Plugins/CheckerPlugins/Pep8/Pep8Dialog.py

changeset 2876
bfa39cf40277
parent 2874
0d754c68e1db
child 2878
61042247f793
--- a/Plugins/CheckerPlugins/Pep8/Pep8Dialog.py	Wed Aug 28 19:53:35 2013 +0200
+++ b/Plugins/CheckerPlugins/Pep8/Pep8Dialog.py	Sat Aug 31 15:21:28 2013 +0200
@@ -67,8 +67,8 @@
     lineRole = Qt.UserRole + 2
     positionRole = Qt.UserRole + 3
     messageRole = Qt.UserRole + 4
-    
-    settingsKey = "PEP8/"
+    fixableRole = Qt.UserRole + 5
+    codeRole = Qt.UserRole + 6
     
     def __init__(self, parent=None):
         """
@@ -134,6 +134,7 @@
             self.__lastFileItem.setExpanded(True)
             self.__lastFileItem.setData(0, self.filenameRole, file)
         
+        fixable = False
         code, message = message.split(None, 1)
         itm = QTreeWidgetItem(self.__lastFileItem,
             ["{0:6}".format(line), code, message])
@@ -145,6 +146,7 @@
             itm.setIcon(0, UI.PixmapCache.getIcon("issueFixed.png"))
         elif code in Pep8FixableIssues:
             itm.setIcon(0, UI.PixmapCache.getIcon("issueFixable.png"))
+            fixable = True
         
         itm.setTextAlignment(0, Qt.AlignRight)
         itm.setTextAlignment(1, Qt.AlignHCenter)
@@ -157,6 +159,23 @@
         itm.setData(0, self.lineRole, int(line))
         itm.setData(0, self.positionRole, int(pos))
         itm.setData(0, self.messageRole, message)
+        itm.setData(0, self.fixableRole, fixable)
+        itm.setData(0, self.codeRole, code)
+    
+    def __modifyFixedResultItem(self, itm, text):
+        """
+        Private method to modify a result list entry to show its
+        positive fixed state.
+        
+        @param itm reference to the item to modify (QTreeWidgetItem)
+        @param text text to be appended (string)
+        """
+        message = itm.data(0, self.messageRole) + text
+        itm.setText(2, message)
+        itm.setIcon(0, UI.PixmapCache.getIcon("issueFixed.png"))
+        
+        itm.setData(0, self.messageRole, message)
+        itm.setData(0, self.fixableRole, False)
     
     def __updateStatistics(self, statistics, fixer):
         """
@@ -177,6 +196,14 @@
         if fixer:
             self.__statistics["_IssuesFixed"] += fixer.fixed
     
+    def __updateFixerStatistics(self, fixer):
+        """
+        Private method to update the collected fixer related statistics.
+        
+        @param fixer reference to the PEP 8 fixer (Pep8Fixer)
+        """
+        self.__statistics["_IssuesFixed"] += fixer.fixed
+    
     def __resetStatistics(self):
         """
         Private slot to reset the statistics data.
@@ -217,12 +244,15 @@
             self.__data["MaxLineLength"] = pep8.MAX_LINE_LENGTH,
         if "HangClosing" not in self.__data:
             self.__data["HangClosing"] = False
+        if "NoFixCodes" not in self.__data:
+            self.__data["NoFixCodes"] = "E501"
         
         self.excludeFilesEdit.setText(self.__data["ExcludeFiles"])
         self.excludeMessagesEdit.setText(self.__data["ExcludeMessages"])
         self.includeMessagesEdit.setText(self.__data["IncludeMessages"])
         self.repeatCheckBox.setChecked(self.__data["RepeatMessages"])
         self.fixIssuesEdit.setText(self.__data["FixCodes"])
+        self.noFixIssuesEdit.setText(self.__data["NoFixCodes"])
         self.fixIssuesCheckBox.setChecked(self.__data["FixIssues"])
         self.lineLengthSpinBox.setValue(self.__data["MaxLineLength"])
         self.hangClosingCheckBox.setChecked(self.__data["HangClosing"])
@@ -247,6 +277,7 @@
         self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
         self.statisticsButton.setEnabled(False)
         self.showButton.setEnabled(False)
+        self.fixButton.setEnabled(False)
         if repeat is not None:
             self.repeatCheckBox.setChecked(repeat)
         QApplication.processEvents()
@@ -295,6 +326,7 @@
             includeMessages = self.includeMessagesEdit.text()
             repeatMessages = self.repeatCheckBox.isChecked()
             fixCodes = self.fixIssuesEdit.text()
+            noFixCodes = self.noFixIssuesEdit.text()
             fixIssues = self.fixIssuesCheckBox.isChecked() and repeatMessages
             maxLineLength = self.lineLengthSpinBox.value()
             hangClosing = self.hangClosingCheckBox.isChecked()
@@ -332,7 +364,8 @@
                     if fixIssues:
                         from .Pep8Fixer import Pep8Fixer
                         fixer = Pep8Fixer(self.__project, file, source,
-                                          fixCodes, True)  # always fix in place
+                                          fixCodes, noFixCodes, maxLineLength,
+                                          True)  # always fix in place
                     else:
                         fixer = None
                     if ("FileType" in flags and
@@ -440,6 +473,7 @@
                 "IncludeMessages": self.includeMessagesEdit.text(),
                 "RepeatMessages": self.repeatCheckBox.isChecked(),
                 "FixCodes": self.fixIssuesEdit.text(),
+                "NoFixCodes": self.noFixIssuesEdit.text(),
                 "FixIssues": self.fixIssuesCheckBox.isChecked(),
                 "MaxLineLength": self.lineLengthSpinBox.value(),
                 "HangClosing": self.hangClosingCheckBox.isChecked(),
@@ -454,17 +488,26 @@
         self.cancelled = False
         self.start(self.__fileOrFileList)
     
+    def __selectCodes(self, edit, showFixCodes):
+        """
+        Private method to select message codes via a selection dialog.
+        
+        @param edit reference of the line edit to be populated (QLineEdit)
+        @param showFixCodes flag indicating to show a list of fixable
+            issues (boolean)
+        """
+        from .Pep8CodeSelectionDialog import Pep8CodeSelectionDialog
+        dlg = Pep8CodeSelectionDialog(edit.text(), showFixCodes, self)
+        if dlg.exec_() == QDialog.Accepted:
+            edit.setText(dlg.getSelectedCodes())
+    
     @pyqtSlot()
     def on_excludeMessagesSelectButton_clicked(self):
         """
         Private slot to select the message codes to be excluded via a
         selection dialog.
         """
-        from .Pep8CodeSelectionDialog import Pep8CodeSelectionDialog
-        dlg = Pep8CodeSelectionDialog(
-            self.excludeMessagesEdit.text(), False, self)
-        if dlg.exec_() == QDialog.Accepted:
-            self.excludeMessagesEdit.setText(dlg.getSelectedCodes())
+        self.__selectCodes(self.excludeMessagesEdit, False)
     
     @pyqtSlot()
     def on_includeMessagesSelectButton_clicked(self):
@@ -472,11 +515,7 @@
         Private slot to select the message codes to be included via a
         selection dialog.
         """
-        from .Pep8CodeSelectionDialog import Pep8CodeSelectionDialog
-        dlg = Pep8CodeSelectionDialog(
-            self.includeMessagesEdit.text(), False, self)
-        if dlg.exec_() == QDialog.Accepted:
-            self.includeMessagesEdit.setText(dlg.getSelectedCodes())
+        self.__selectCodes(self.includeMessagesEdit, False)
     
     @pyqtSlot()
     def on_fixIssuesSelectButton_clicked(self):
@@ -484,11 +523,15 @@
         Private slot to select the issue codes to be fixed via a
         selection dialog.
         """
-        from .Pep8CodeSelectionDialog import Pep8CodeSelectionDialog
-        dlg = Pep8CodeSelectionDialog(
-            self.fixIssuesEdit.text(), True, self)
-        if dlg.exec_() == QDialog.Accepted:
-            self.fixIssuesEdit.setText(dlg.getSelectedCodes())
+        self.__selectCodes(self.fixIssuesEdit, True)
+    
+    @pyqtSlot()
+    def on_noFixIssuesSelectButton_clicked(self):
+        """
+        Private slot to select the issue codes not to be fixed via a
+        selection dialog.
+        """
+        self.__selectCodes(self.noFixIssuesEdit, True)
     
     @pyqtSlot(QTreeWidgetItem, int)
     def on_resultList_itemActivated(self, item, column):
@@ -514,6 +557,13 @@
             editor.toggleFlakesWarning(lineno, True, message)
     
     @pyqtSlot()
+    def on_resultList_itemSelectionChanged(self):
+        """
+        Private slot to change the dialog state depending on the selection.
+        """
+        self.fixButton.setEnabled(len(self.__getSelectedFixableItems()) > 0)
+    
+    @pyqtSlot()
     def on_showButton_clicked(self):
         """
         Private slot to handle the "Show" button press.
@@ -573,6 +623,8 @@
             "PEP8/IncludeMessages"))
         self.fixIssuesEdit.setText(Preferences.Prefs.settings.value(
             "PEP8/FixCodes"))
+        self.noFixIssuesEdit.setText(Preferences.Prefs.settings.value(
+            "PEP8/NoFixCodes"))
         self.fixIssuesCheckBox.setChecked(Preferences.toBool(
             Preferences.Prefs.settings.value("PEP8/FixIssues")))
         self.lineLengthSpinBox.setValue(int(Preferences.Prefs.settings.value(
@@ -594,6 +646,8 @@
             self.includeMessagesEdit.text())
         Preferences.Prefs.settings.setValue("PEP8/FixCodes",
             self.fixIssuesEdit.text())
+        Preferences.Prefs.settings.setValue("PEP8/NoFixCodes",
+            self.noFixIssuesEdit.text())
         Preferences.Prefs.settings.setValue("PEP8/FixIssues",
             self.fixIssuesCheckBox.isChecked())
         Preferences.Prefs.settings.setValue("PEP8/MaxLineLength",
@@ -626,3 +680,96 @@
         for file in openFiles:
             editor = vm.getOpenEditor(file)
             editor.clearFlakesWarnings()
+    
+    @pyqtSlot()
+    def on_fixButton_clicked(self):
+        """
+        Private slot to fix selected issues.
+        """
+        # TODO: test this
+        from .Pep8Fixer import Pep8Fixer
+        
+        # build a dictionary of issues to fix
+        fixableItems = self.__getSelectedFixableItems()
+        fixesDict = {}      # dictionary of lists of tuples containing
+                            # the issue and the item
+        for itm in fixableItems:
+            filename = itm.data(0, self.filenameRole)
+            if filename not in fixesDict:
+                fixesDict[filename] = []
+            fixesDict[filename].append((
+                (itm.data(0, self.lineRole),
+                 itm.data(0, self.positionRole),
+                 "{0} {1}".format(itm.data(0, self.codeRole), 
+                                  itm.data(0, self.messageRole))),
+                itm
+            ))
+        
+        # extract the configuration values
+        fixCodes = self.fixIssuesEdit.text()
+        noFixCodes = self.noFixIssuesEdit.text()
+        maxLineLength = self.lineLengthSpinBox.value()
+        
+        # now go through all the files
+        if fixesDict:
+            self.checkProgress.setMaximum(len(fixesDict))
+            progress = 0
+            for file in fixesDict:
+                self.checkProgress.setValue(progress)
+                QApplication.processEvents()
+                
+                try:
+                    source, encoding = Utilities.readEncodedFile(file)
+                    source = source.splitlines(True)
+                except (UnicodeError, IOError) as msg:
+                    # skip silently because that should not happen
+                    progress += 1
+                    continue
+                
+                fixer = Pep8Fixer(self.__project, file, source,
+                                  fixCodes, noFixCodes, maxLineLength,
+                                  True)  # always fix in place
+                errors = fixesDict[file]
+                errors.sort(key=lambda a: a[0][0])
+                for error in errors:
+                    (lineno, position, text), itm = error
+                    if lineno > len(source):
+                        lineno = len(source)
+                    fixed, msg = fixer.fixIssue(lineno, position, text)
+                    if fixed:
+                        text = "\n" + self.trUtf8("Fix: {0}").format(msg)
+                        self.__modifyFixedResultItem(itm, text)
+                fixer.saveFile(encoding)
+                
+                self.__updateFixerStatistics(fixer)
+                progress += 1
+            
+            self.checkProgress.setValue(progress)
+            QApplication.processEvents()
+
+    def __getSelectedFixableItems(self):
+        """
+        Private method to extract all selected items for fixable issues.
+        
+        @return selected items for fixable issues (list of QTreeWidgetItem)
+        """
+        fixableItems = []
+        for itm in self.resultList.selectedItems():
+            if itm.childCount() > 0:
+                for index in range(itm.childCount()):
+                    citm = itm.child(index)
+                    if self.__itemFixable(citm) and not citm in fixableItems:
+                        fixableItems.append(citm)
+            elif self.__itemFixable(itm) and not itm in fixableItems:
+                fixableItems.append(itm)
+        
+        return fixableItems
+    
+    def __itemFixable(self, itm):
+        """
+        Private method to check, if an item has a fixable issue.
+        
+        @param itm item to be checked (QTreeWidgetItem)
+        @return flag indicating a fixable issue (boolean)
+        """
+        return itm.data(0, self.fixableRole)

eric ide

mercurial