src/eric7/CodeFormatting/BlackFormattingDialog.py

branch
eric7
changeset 9281
76caf27cb8a8
parent 9221
bf71ee032bb4
child 9283
0e9d2c4e379e
equal deleted inserted replaced
9280:94f4e2751790 9281:76caf27cb8a8
9 9
10 import copy 10 import copy
11 import datetime 11 import datetime
12 import pathlib 12 import pathlib
13 13
14 from dataclasses import dataclass
15
14 import black 16 import black
15 17
16 from PyQt6.QtCore import pyqtSlot, Qt, QCoreApplication 18 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QCoreApplication, QObject
17 from PyQt6.QtWidgets import ( 19 from PyQt6.QtWidgets import (
18 QAbstractButton, 20 QAbstractButton,
19 QDialog, 21 QDialog,
20 QDialogButtonBox, 22 QDialogButtonBox,
21 QHeaderView, 23 QHeaderView,
74 76
75 self.resultsList.header().setSortIndicator(1, Qt.SortOrder.AscendingOrder) 77 self.resultsList.header().setSortIndicator(1, Qt.SortOrder.AscendingOrder)
76 78
77 self.statisticsGroup.setVisible(False) 79 self.statisticsGroup.setVisible(False)
78 80
81 self.__statistics = BlackStatistics()
82
79 self.__report = BlackReport(self) 83 self.__report = BlackReport(self)
80 self.__report.check = action is BlackFormattingAction.Check 84 self.__report.check = action is BlackFormattingAction.Check
81 self.__report.diff = action is BlackFormattingAction.Diff 85 self.__report.diff = action is BlackFormattingAction.Diff
86 self.__report.result.connect(self.__handleBlackFormattingResult)
82 87
83 self.__config = copy.deepcopy(configuration) 88 self.__config = copy.deepcopy(configuration)
84 self.__project = project 89 self.__project = project
85 self.__action = action 90 self.__action = action
86 91
189 if self.__action is BlackFormattingAction.Format 194 if self.__action is BlackFormattingAction.Format
190 else self.tr("would reformat") 195 else self.tr("would reformat")
191 ) 196 )
192 197
193 total = self.progressBar.maximum() 198 total = self.progressBar.maximum()
194 processed = total - self.__report.ignored_count 199 processed = total - self.__statistics.ignoreCount
195 200
196 self.totalCountLabel.setText("{0:n}".format(total)) 201 self.totalCountLabel.setText("{0:n}".format(total))
197 self.excludedCountLabel.setText("{0:n}".format(self.__report.ignored_count)) 202 self.excludedCountLabel.setText("{0:n}".format(self.__statistics.ignoreCount))
198 self.failuresCountLabel.setText("{0:n}".format(self.__report.failure_count)) 203 self.failuresCountLabel.setText("{0:n}".format(self.__statistics.failureCount))
199 self.processedCountLabel.setText("{0:n}".format(processed)) 204 self.processedCountLabel.setText("{0:n}".format(processed))
200 self.reformattedCountLabel.setText("{0:n}".format(self.__report.change_count)) 205 self.reformattedCountLabel.setText(
201 self.unchangedCountLabel.setText("{0:n}".format(self.__report.same_count)) 206 "{0:n}".format(self.__statistics.changeCount)
207 )
208 self.unchangedCountLabel.setText("{0:n}".format(self.__statistics.sameCount))
202 209
203 self.statisticsGroup.setVisible(True) 210 self.statisticsGroup.setVisible(True)
204 211
205 @pyqtSlot(QAbstractButton) 212 @pyqtSlot(QAbstractButton)
206 def on_buttonBox_clicked(self, button): 213 def on_buttonBox_clicked(self, button):
237 elif dataType == "diff": 244 elif dataType == "diff":
238 if self.__diffDialog is None: 245 if self.__diffDialog is None:
239 self.__diffDialog = BlackDiffWidget() 246 self.__diffDialog = BlackDiffWidget()
240 self.__diffDialog.showDiff(item.data(0, BlackFormattingDialog.DataRole)) 247 self.__diffDialog.showDiff(item.data(0, BlackFormattingDialog.DataRole))
241 248
242 def addResultEntry(self, status, fileName, isError=False, data=None):
243 """
244 Public method to add an entry to the result list.
245
246 @param status status of the operation
247 @type str
248 @param fileName name of the processed file
249 @type str
250 @param isError flag indicating that data contains an error message (defaults to
251 False)
252 @type bool (optional)
253 @param data associated data (diff or error message) (defaults to None)
254 @type str (optional)
255 """
256 if self.__project:
257 fileName = self.__project.getRelativePath(fileName)
258
259 itm = QTreeWidgetItem(self.resultsList, [status, fileName])
260 if data:
261 itm.setData(
262 0, BlackFormattingDialog.DataTypeRole, "error" if isError else "diff"
263 )
264 itm.setData(0, BlackFormattingDialog.DataRole, data)
265
266 self.progressBar.setValue(self.progressBar.value() + 1)
267
268 QCoreApplication.processEvents()
269
270 def __formatFiles(self): 249 def __formatFiles(self):
271 """ 250 """
272 Private method to format the list of files according the configuration. 251 Private method to format the list of files according the configuration.
273 """ 252 """
274 writeBack = black.WriteBack.from_configuration( 253 writeBack = black.WriteBack.from_configuration(
368 itm.setHidden( 347 itm.setHidden(
369 status != self.__allFilter 348 status != self.__allFilter
370 and itm.text(BlackFormattingDialog.StatusColumn) != status 349 and itm.text(BlackFormattingDialog.StatusColumn) != status
371 ) 350 )
372 351
373 352 @pyqtSlot(str, str, str)
374 class BlackReport(black.Report): 353 def __handleBlackFormattingResult(self, status, filename, data):
354 """
355 Private slot to handle the result of a black reformatting action.
356
357 @param status status of the performed action (one of 'changed', 'unchanged',
358 'unmodified', 'failed' or 'ignored')
359 @type str
360 @param filename name of the processed file
361 @type str
362 @param data action data (error message or unified diff)
363 @type str
364 """
365 isError = False
366
367 if status == "changed":
368 statusMsg = (
369 self.tr("would reformat")
370 if self.__action
371 in (BlackFormattingAction.Check, BlackFormattingAction.Diff)
372 else self.tr("reformatted")
373 )
374 self.__statistics.changeCount += 1
375
376 elif status == "unchanged":
377 statusMsg = self.tr("unchanged")
378 self.__statistics.sameCount += 1
379
380 elif status == "unmodified":
381 statusMsg = self.tr("unmodified")
382 self.__statistics.sameCount += 1
383
384 elif status == "ignored":
385 statusMsg = self.tr("ignored")
386 self.__statistics.ignoreCount += 1
387
388 elif status == "failed":
389 statusMsg = self.tr("failed")
390 self.__statistics.failureCount += 1
391 isError = True
392
393 else:
394 statusMsg = self.tr("invalid status ({0})").format(status)
395 self.__statistics.failureCount += 1
396 isError = True
397
398 if self.__project:
399 filename = self.__project.getRelativePath(filename)
400
401 itm = QTreeWidgetItem(self.resultsList, [statusMsg, filename])
402 if data:
403 itm.setData(
404 0, BlackFormattingDialog.DataTypeRole, "error" if isError else "diff"
405 )
406 itm.setData(0, BlackFormattingDialog.DataRole, data)
407
408 self.progressBar.setValue(self.progressBar.value() + 1)
409
410 QCoreApplication.processEvents()
411
412
413 @dataclass
414 class BlackStatistics:
415 """
416 Class containing the reformatting statistic data.
417 """
418
419 ignoreCount: int = 0
420 changeCount: int = 0
421 sameCount: int = 0
422 failureCount: int = 0
423
424
425 class BlackReport(QObject, black.Report):
375 """ 426 """
376 Class extending the black Report to work with our dialog. 427 Class extending the black Report to work with our dialog.
377 """ 428
429 @signal result(status, file name, data) emitted to signal the reformatting result
430 as three strings giving the status (one of 'changed', 'unchanged', 'unmodified',
431 'failed' or 'ignored'), the file name and data related to the result
432 """
433
434 result = pyqtSignal(str, str, str)
378 435
379 def __init__(self, dialog): 436 def __init__(self, dialog):
380 """ 437 """
381 Constructor 438 Constructor
382 439
383 @param dialog reference to the result dialog 440 @param dialog reference to the result dialog
384 @type QDialog 441 @type QDialog
385 """ 442 """
386 super().__init__() 443 QObject.__init__(self, dialog)
444 black.Report.__init__(self)
387 445
388 self.ignored_count = 0 446 self.ignored_count = 0
389 447
390 self.__dialog = dialog 448 self.__dialog = dialog
391 449
399 @type black.Changed 457 @type black.Changed
400 @param diff unified diff of potential changes (defaults to "") 458 @param diff unified diff of potential changes (defaults to "")
401 @type str 459 @type str
402 """ 460 """
403 if changed is black.Changed.YES: 461 if changed is black.Changed.YES:
404 status = ( 462 status = "changed"
405 QCoreApplication.translate("BlackFormattingDialog", "would reformat")
406 if self.check or self.diff
407 else QCoreApplication.translate("BlackFormattingDialog", "reformatted")
408 )
409 self.change_count += 1
410 463
411 elif changed is black.Changed.NO: 464 elif changed is black.Changed.NO:
412 status = QCoreApplication.translate("BlackFormattingDialog", "unchanged") 465 status = "unchanged"
413 self.same_count += 1
414 466
415 elif changed is black.Changed.CACHED: 467 elif changed is black.Changed.CACHED:
416 status = QCoreApplication.translate("BlackFormattingDialog", "unmodified") 468 status = "unmodified"
417 self.same_count += 1 469
418 470 self.result.emit(status, str(src), diff)
419 if self.diff:
420 self.__dialog.addResultEntry(status, str(src), data=diff)
421 else:
422 self.__dialog.addResultEntry(status, str(src))
423 471
424 def failed(self, src, message): 472 def failed(self, src, message):
425 """ 473 """
426 Public method to handle a reformat failure. 474 Public method to handle a reformat failure.
427 475
428 @param src name of the processed file 476 @param src name of the processed file
429 @type pathlib.Path 477 @type pathlib.Path
430 @param message error message 478 @param message error message
431 @type str 479 @type str
432 """ 480 """
433 status = QCoreApplication.translate("BlackFormattingDialog", "failed") 481 self.result.emit("failed", str(src), message)
434 self.failure_count += 1
435
436 self.__dialog.addResultEntry(status, str(src), isError=True, data=message)
437 482
438 def path_ignored(self, src, message=""): 483 def path_ignored(self, src, message=""):
439 """ 484 """
440 Public method handling an ignored path. 485 Public method handling an ignored path.
441 486
442 @param src name of the processed file 487 @param src name of the processed file
443 @type pathlib.Path or str 488 @type pathlib.Path or str
444 @param message ignore message (default to "") 489 @param message ignore message (default to "")
445 @type str (optional) 490 @type str (optional)
446 """ 491 """
447 status = QCoreApplication.translate("BlackFormattingDialog", "ignored") 492 self.result.emit("ignored", str(src), "")
448 self.ignored_count += 1
449
450 self.__dialog.addResultEntry(status, str(src))

eric ide

mercurial