--- a/src/eric7/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckerDialog.py Sat Nov 11 12:44:51 2023 +0100 +++ b/src/eric7/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckerDialog.py Sat Nov 11 16:21:15 2023 +0100 @@ -73,6 +73,7 @@ self.__fileList = [] self.__project = None self.__arguments = () + self.__statistics = self.__defaultStatistics() self.filterFrame.setVisible(False) self.checkProgress.setVisible(False) @@ -94,6 +95,65 @@ self.resultList.sortColumn(), self.resultList.header().sortIndicatorOrder() ) + def __defaultStatistics(self): + """ + Private method to return the default statistics entry. + + @return dictionary with default statistics entry + @rtype dict + """ + return { + "files_checked": 0, + "files_issues": 0, + "errors": 0, + "py_warnings": 0, + "warnings": 0, + } + + def __updateStatistics(self, fileStatistics): + """ + Private method to update the statistics. + + @param fileStatistics dictionary containing the file statistics + @type dict + """ + self.__statistics["files_checked"] += 1 + if any(fileStatistics.values()): + self.__statistics["files_issues"] += 1 + self.__statistics["errors"] += fileStatistics["errors"] + self.__statistics["py_warnings"] += fileStatistics["py_warnings"] + self.__statistics["warnings"] += fileStatistics["warnings"] + + def __resetStatistics(self, skipped): + """ + Private method to reset the statistics data. + + @param skipped number of files not being checked + @type int + """ + self.__statistics["files_checked"] = 0 + self.__statistics["files_skipped"] = skipped + self.__statistics["files_issues"] = 0 + self.__statistics["errors"] = 0 + self.__statistics["py_warnings"] = 0 + self.__statistics["warnings"] = 0 + + def __createFileStatistics(self, problems): + """ + Private method to return the file statistics entry. + + @param problems dictionary with the keys 'error', 'py_warnings' and + 'warnings' which hold a list of issues each + @type dict + @return dictionary with the file statistics + @rtype dict + """ + return { + "errors": 1 if problems.get("error") else 0, + "py_warnings": len(problems.get("py_warnings", [])), + "warnings": len(problems.get("warnings", [])), + } + def __createErrorItem(self, filename, message): """ Private slot to create a new error item in the result list. @@ -114,6 +174,42 @@ itm.setForeground(0, Qt.GlobalColor.red) itm.setFirstColumnSpanned(True) + def __createHeaderItem(self, filename, fileStatistics=None): + """ + Private method to create a header item in the result list. + + @param filename file name of file + @type str + @param fileStatistics dictionary containing statistical data of the check + result (defaults to None) + @type dict (optional) + """ + itemText = self.__project.getRelativePath(filename) + + if fileStatistics: + statisticsTextList = [] + if fileStatistics["errors"]: + statisticsTextList.append( + self.tr("Errors: {0}").format(fileStatistics["errors"]) + ) + if fileStatistics["py_warnings"]: + statisticsTextList.append( + self.tr("Python Warnings: {0}").format( + fileStatistics["py_warnings"] + ) + ) + if fileStatistics["warnings"]: + statisticsTextList.append( + self.tr("Warnings: {0}").format(fileStatistics["warnings"]) + ) + if statisticsTextList: + itemText += "{0}\n{1}".format(itemText, ", ".join(statisticsTextList)) + + self.__lastFileItem = QTreeWidgetItem(self.resultList, [itemText]) + self.__lastFileItem.setFirstColumnSpanned(True) + self.__lastFileItem.setExpanded(True) + self.__lastFileItem.setData(0, self.filenameRole, filename) + def __createResultItem( self, filename, line, index, error, sourcecode, isWarning=False ): @@ -138,12 +234,7 @@ or self.__lastFileItem.data(0, self.filenameRole) != filename ): # It's a new file - self.__lastFileItem = QTreeWidgetItem( - self.resultList, [self.__project.getRelativePath(filename)] - ) - self.__lastFileItem.setFirstColumnSpanned(True) - self.__lastFileItem.setExpanded(True) - self.__lastFileItem.setData(0, self.filenameRole, filename) + self.__createHeaderItem(filename) itm = QTreeWidgetItem(self.__lastFileItem) if isWarning: @@ -236,7 +327,7 @@ self.on_startButton_clicked() # press the start button - def start(self, fn, codestring=""): + def start(self, fn, codestring="", skipped=0): """ Public slot to start the syntax check. @@ -245,6 +336,8 @@ @param codestring string containing the code to be checked. If this is given, fn must be a single file name. @type str + @param skipped number of files not being checked + @type int """ self.__batch = False @@ -279,6 +372,7 @@ self.__errorItem = None self.__clearErrors(self.files) + self.__resetStatistics(skipped) if codestring or len(self.files) > 0: self.checkProgress.setMaximum(max(1, len(self.files))) @@ -412,10 +506,10 @@ @param fn filename of the checked file @type str - @param problems dictionary with the keys 'error' and 'warnings' which - hold a list containing details about the error/ warnings - (file name, line number, column, codestring (only at syntax - errors), the message) + @param problems dictionary with the keys 'error', 'py_warnings' and + 'warnings' which hold a list containing details about the error or + warnings (file name, line number, column, codestring (only at syntax + errors), message) @type dict """ if self.__finished: @@ -426,33 +520,38 @@ if not self.__batch and fn != self.filename: return - error = problems.get("error") - if error: - self.noResults = False - _fn, lineno, col, code, msg = error - self.__createResultItem(_fn, lineno, col, msg, code, False) + fileStatistics = self.__createFileStatistics(problems) + self.__updateStatistics(fileStatistics) + if any(fileStatistics.values()): + self.__createHeaderItem(fn, fileStatistics) + + error = problems.get("error") + if error: + self.noResults = False + filename, lineno, col, code, msg = error + self.__createResultItem(filename, lineno, col, msg, code, False) - warnings = problems.get("py_warnings", []) + problems.get("warnings", []) - if warnings: - if self.__batch: - try: - source = Utilities.readEncodedFile(fn)[0] - source = Utilities.normalizeCode(source) - source = source.splitlines() - except (OSError, UnicodeError): - source = "" - else: - source = self.source.splitlines() - for filename, lineno, col, _code, msg in warnings: - self.noResults = False - if source: + warnings = problems.get("py_warnings", []) + problems.get("warnings", []) + if warnings: + if self.__batch: try: - src_line = source[lineno - 1].strip() - except IndexError: + source = Utilities.readEncodedFile(fn)[0] + source = Utilities.normalizeCode(source) + source = source.splitlines() + except (OSError, UnicodeError): + source = "" + else: + source = self.source.splitlines() + for filename, lineno, col, _code, msg in warnings: + self.noResults = False + if source: + try: + src_line = source[lineno - 1].strip() + except IndexError: + src_line = "" + else: src_line = "" - else: - src_line = "" - self.__createResultItem(filename, lineno, col, msg, src_line, True) + self.__createResultItem(filename, lineno, col, msg, src_line, True) self.progress += 1 self.checkProgress.setValue(self.progress) @@ -464,6 +563,20 @@ if not self.__batch: self.check() + def __updateStatisticsArea(self): + """ + Private method to update the statistics area of the dialog. + """ + self.totalLabel.setText( + str(self.__statistics["files_skipped"] + self.__statistics["files_checked"]) + ) + self.skippedLabel.setText(str(self.__statistics["files_skipped"])) + self.checkedLabel.setText(str(self.__statistics["files_checked"])) + self.issuesLabel.setText(str(self.__statistics["files_issues"])) + self.errorsLabel.setText(str(self.__statistics["errors"])) + self.warningsLabel.setText(str(self.__statistics["warnings"])) + self.pyWarningsLabel.setText(str(self.__statistics["py_warnings"])) + def __finish(self): """ Private slot called when the syntax check finished or the user @@ -493,6 +606,7 @@ QHeaderView.ResizeMode.ResizeToContents ) self.resultList.header().setStretchLastSection(True) + self.__updateStatisticsArea() self.checkProgress.setVisible(False) @@ -535,6 +649,7 @@ Private slot to start a syntax check run. """ fileList = self.__fileList[:] + totalLen = len(fileList) filterString = self.excludeFilesEdit.text() self.__data["ExcludeFiles"] = filterString @@ -550,7 +665,7 @@ self.noResults = True self.cancelled = False self.setArguments((self.__data["AdditionalBuiltins"].split(),)) - self.start(fileList) + self.start(fileList, skipped=totalLen - len(fileList)) @pyqtSlot(QTreeWidgetItem, int) def on_resultList_itemActivated(self, itm, col):