|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2011 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a dialog to show the results of the code style check. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 |
|
12 import os |
|
13 import fnmatch |
|
14 |
|
15 from PyQt5.QtCore import pyqtSlot, Qt, QTimer |
|
16 from PyQt5.QtGui import QIcon |
|
17 from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QAbstractButton, \ |
|
18 QDialogButtonBox, QApplication, QHeaderView, QListWidgetItem |
|
19 |
|
20 from E5Gui.E5Application import e5App |
|
21 |
|
22 from .Ui_CodeStyleCheckerDialog import Ui_CodeStyleCheckerDialog |
|
23 |
|
24 import UI.PixmapCache |
|
25 import Preferences |
|
26 import Utilities |
|
27 |
|
28 from . import pycodestyle |
|
29 |
|
30 try: |
|
31 basestring # __IGNORE_WARNING__ |
|
32 except Exception: |
|
33 basestring = str # define for Python3 |
|
34 |
|
35 |
|
36 class CodeStyleCheckerDialog(QDialog, Ui_CodeStyleCheckerDialog): |
|
37 """ |
|
38 Class implementing a dialog to show the results of the code style check. |
|
39 """ |
|
40 filenameRole = Qt.UserRole + 1 |
|
41 lineRole = Qt.UserRole + 2 |
|
42 positionRole = Qt.UserRole + 3 |
|
43 messageRole = Qt.UserRole + 4 |
|
44 fixableRole = Qt.UserRole + 5 |
|
45 codeRole = Qt.UserRole + 6 |
|
46 ignoredRole = Qt.UserRole + 7 |
|
47 |
|
48 availableFutures = [ |
|
49 'division', 'absolute_import', 'with_statement', |
|
50 'print_function', 'unicode_literals', 'generator_stop'] |
|
51 |
|
52 noResults = 0 |
|
53 noFiles = 1 |
|
54 hasResults = 2 |
|
55 |
|
56 def __init__(self, styleCheckService, parent=None): |
|
57 """ |
|
58 Constructor |
|
59 |
|
60 @param styleCheckService reference to the service |
|
61 (CodeStyleCheckService) |
|
62 @param parent reference to the parent widget (QWidget) |
|
63 """ |
|
64 super(CodeStyleCheckerDialog, self).__init__(parent) |
|
65 self.setupUi(self) |
|
66 self.setWindowFlags(Qt.Window) |
|
67 |
|
68 self.optionsTabWidget.setCurrentIndex(0) |
|
69 |
|
70 self.excludeMessagesSelectButton.setIcon( |
|
71 UI.PixmapCache.getIcon("select.png")) |
|
72 self.includeMessagesSelectButton.setIcon( |
|
73 UI.PixmapCache.getIcon("select.png")) |
|
74 self.fixIssuesSelectButton.setIcon( |
|
75 UI.PixmapCache.getIcon("select.png")) |
|
76 self.noFixIssuesSelectButton.setIcon( |
|
77 UI.PixmapCache.getIcon("select.png")) |
|
78 |
|
79 self.docTypeComboBox.addItem(self.tr("PEP-257"), "pep257") |
|
80 self.docTypeComboBox.addItem(self.tr("Eric"), "eric") |
|
81 |
|
82 for future in CodeStyleCheckerDialog.availableFutures: |
|
83 itm = QListWidgetItem(future, self.futuresList) |
|
84 itm.setFlags(itm.flags() | Qt.ItemIsUserCheckable) |
|
85 itm.setCheckState(Qt.Unchecked) |
|
86 |
|
87 self.statisticsButton = self.buttonBox.addButton( |
|
88 self.tr("Statistics..."), QDialogButtonBox.ActionRole) |
|
89 self.statisticsButton.setToolTip( |
|
90 self.tr("Press to show some statistics for the last run")) |
|
91 self.statisticsButton.setEnabled(False) |
|
92 self.showButton = self.buttonBox.addButton( |
|
93 self.tr("Show"), QDialogButtonBox.ActionRole) |
|
94 self.showButton.setToolTip( |
|
95 self.tr("Press to show all files containing an issue")) |
|
96 self.showButton.setEnabled(False) |
|
97 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) |
|
98 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) |
|
99 |
|
100 self.resultList.headerItem().setText(self.resultList.columnCount(), "") |
|
101 self.resultList.header().setSortIndicator(0, Qt.AscendingOrder) |
|
102 |
|
103 self.addBuiltinButton.setIcon(UI.PixmapCache.getIcon("plus.png")) |
|
104 self.deleteBuiltinButton.setIcon(UI.PixmapCache.getIcon("minus.png")) |
|
105 |
|
106 self.checkProgress.setVisible(False) |
|
107 self.checkProgressLabel.setVisible(False) |
|
108 self.checkProgressLabel.setMaximumWidth(600) |
|
109 |
|
110 self.styleCheckService = styleCheckService |
|
111 self.styleCheckService.styleChecked.connect(self.__processResult) |
|
112 self.styleCheckService.batchFinished.connect(self.__batchFinished) |
|
113 self.styleCheckService.error.connect(self.__processError) |
|
114 self.filename = None |
|
115 |
|
116 self.results = CodeStyleCheckerDialog.noResults |
|
117 self.cancelled = False |
|
118 self.__lastFileItem = None |
|
119 self.__batch = False |
|
120 self.__finished = True |
|
121 self.__errorItem = None |
|
122 |
|
123 self.__fileOrFileList = "" |
|
124 self.__project = None |
|
125 self.__forProject = False |
|
126 self.__data = {} |
|
127 self.__statistics = {} |
|
128 self.__onlyFixes = {} |
|
129 self.__noFixCodesList = [] |
|
130 |
|
131 self.on_loadDefaultButton_clicked() |
|
132 |
|
133 def __resort(self): |
|
134 """ |
|
135 Private method to resort the tree. |
|
136 """ |
|
137 self.resultList.sortItems(self.resultList.sortColumn(), |
|
138 self.resultList.header().sortIndicatorOrder() |
|
139 ) |
|
140 |
|
141 def __createErrorItem(self, filename, message): |
|
142 """ |
|
143 Private slot to create a new error item in the result list. |
|
144 |
|
145 @param filename name of the file |
|
146 @type str |
|
147 @param message error message |
|
148 @type str |
|
149 """ |
|
150 if self.__errorItem is None: |
|
151 self.__errorItem = QTreeWidgetItem(self.resultList, [ |
|
152 self.tr("Errors")]) |
|
153 self.__errorItem.setExpanded(True) |
|
154 self.__errorItem.setForeground(0, Qt.red) |
|
155 |
|
156 msg = "{0} ({1})".format(self.__project.getRelativePath(filename), |
|
157 message) |
|
158 if not self.resultList.findItems(msg, Qt.MatchExactly): |
|
159 itm = QTreeWidgetItem(self.__errorItem, [msg]) |
|
160 itm.setForeground(0, Qt.red) |
|
161 itm.setFirstColumnSpanned(True) |
|
162 |
|
163 def __createResultItem(self, filename, line, pos, message, fixed, |
|
164 autofixing, ignored): |
|
165 """ |
|
166 Private method to create an entry in the result list. |
|
167 |
|
168 @param filename file name of the file (string) |
|
169 @param line line number of issue (integer or string) |
|
170 @param pos character position of issue (integer or string) |
|
171 @param message message text (string) |
|
172 @param fixed flag indicating a fixed issue (boolean) |
|
173 @param autofixing flag indicating, that we are fixing issues |
|
174 automatically (boolean) |
|
175 @param ignored flag indicating an ignored issue (boolean) |
|
176 @return reference to the created item (QTreeWidgetItem) |
|
177 """ |
|
178 from .CodeStyleFixer import FixableCodeStyleIssues |
|
179 |
|
180 if self.__lastFileItem is None: |
|
181 # It's a new file |
|
182 self.__lastFileItem = QTreeWidgetItem(self.resultList, [ |
|
183 self.__project.getRelativePath(filename)]) |
|
184 self.__lastFileItem.setFirstColumnSpanned(True) |
|
185 self.__lastFileItem.setExpanded(True) |
|
186 self.__lastFileItem.setData(0, self.filenameRole, filename) |
|
187 |
|
188 fixable = False |
|
189 code, message = message.split(None, 1) |
|
190 itm = QTreeWidgetItem( |
|
191 self.__lastFileItem, |
|
192 ["{0:6}".format(line), code, message]) |
|
193 if code.startswith(("W", "-", "C", "M")): |
|
194 itm.setIcon(1, UI.PixmapCache.getIcon("warning.png")) |
|
195 elif code.startswith("N"): |
|
196 itm.setIcon(1, UI.PixmapCache.getIcon("namingError.png")) |
|
197 elif code.startswith("D"): |
|
198 itm.setIcon(1, UI.PixmapCache.getIcon("docstringError.png")) |
|
199 else: |
|
200 itm.setIcon(1, UI.PixmapCache.getIcon("syntaxError.png")) |
|
201 if fixed: |
|
202 itm.setIcon(0, UI.PixmapCache.getIcon("issueFixed.png")) |
|
203 elif code in FixableCodeStyleIssues and not autofixing and \ |
|
204 code not in self.__noFixCodesList: |
|
205 itm.setIcon(0, UI.PixmapCache.getIcon("issueFixable.png")) |
|
206 fixable = True |
|
207 |
|
208 itm.setTextAlignment(0, Qt.AlignRight) |
|
209 itm.setTextAlignment(1, Qt.AlignHCenter) |
|
210 |
|
211 itm.setTextAlignment(0, Qt.AlignVCenter) |
|
212 itm.setTextAlignment(1, Qt.AlignVCenter) |
|
213 itm.setTextAlignment(2, Qt.AlignVCenter) |
|
214 |
|
215 itm.setData(0, self.filenameRole, filename) |
|
216 itm.setData(0, self.lineRole, int(line)) |
|
217 itm.setData(0, self.positionRole, int(pos)) |
|
218 itm.setData(0, self.messageRole, message) |
|
219 itm.setData(0, self.fixableRole, fixable) |
|
220 itm.setData(0, self.codeRole, code) |
|
221 itm.setData(0, self.ignoredRole, ignored) |
|
222 |
|
223 if ignored: |
|
224 font = itm.font(0) |
|
225 font.setItalic(True) |
|
226 for col in range(itm.columnCount()): |
|
227 itm.setFont(col, font) |
|
228 |
|
229 return itm |
|
230 |
|
231 def __modifyFixedResultItem(self, itm, text, fixed): |
|
232 """ |
|
233 Private method to modify a result list entry to show its |
|
234 positive fixed state. |
|
235 |
|
236 @param itm reference to the item to modify (QTreeWidgetItem) |
|
237 @param text text to be appended (string) |
|
238 @param fixed flag indicating a fixed issue (boolean) |
|
239 """ |
|
240 if fixed: |
|
241 code, message = text.split(None, 1) |
|
242 itm.setText(2, message) |
|
243 itm.setIcon(0, UI.PixmapCache.getIcon("issueFixed.png")) |
|
244 |
|
245 itm.setData(0, self.messageRole, message) |
|
246 else: |
|
247 itm.setIcon(0, QIcon()) |
|
248 itm.setData(0, self.fixableRole, False) |
|
249 |
|
250 def __updateStatistics(self, statistics, fixer, ignoredErrors): |
|
251 """ |
|
252 Private method to update the collected statistics. |
|
253 |
|
254 @param statistics dictionary of statistical data with |
|
255 message code as key and message count as value |
|
256 @param fixer reference to the code style fixer (CodeStyleFixer) |
|
257 @param ignoredErrors number of ignored errors (integer) |
|
258 """ |
|
259 self.__statistics["_FilesCount"] += 1 |
|
260 stats = [k for k in statistics.keys() if k[0].isupper()] |
|
261 if stats: |
|
262 self.__statistics["_FilesIssues"] += 1 |
|
263 for key in statistics: |
|
264 if key in self.__statistics: |
|
265 self.__statistics[key] += statistics[key] |
|
266 else: |
|
267 self.__statistics[key] = statistics[key] |
|
268 self.__statistics["_IssuesFixed"] += fixer |
|
269 self.__statistics["_IgnoredErrors"] += ignoredErrors |
|
270 |
|
271 def __updateFixerStatistics(self, fixer): |
|
272 """ |
|
273 Private method to update the collected fixer related statistics. |
|
274 |
|
275 @param fixer reference to the code style fixer (CodeStyleFixer) |
|
276 """ |
|
277 self.__statistics["_IssuesFixed"] += fixer |
|
278 |
|
279 def __resetStatistics(self): |
|
280 """ |
|
281 Private slot to reset the statistics data. |
|
282 """ |
|
283 self.__statistics = {} |
|
284 self.__statistics["_FilesCount"] = 0 |
|
285 self.__statistics["_FilesIssues"] = 0 |
|
286 self.__statistics["_IssuesFixed"] = 0 |
|
287 self.__statistics["_IgnoredErrors"] = 0 |
|
288 |
|
289 def prepare(self, fileList, project): |
|
290 """ |
|
291 Public method to prepare the dialog with a list of filenames. |
|
292 |
|
293 @param fileList list of filenames (list of strings) |
|
294 @param project reference to the project object (Project) |
|
295 """ |
|
296 self.__fileOrFileList = fileList[:] |
|
297 self.__project = project |
|
298 self.__forProject = True |
|
299 |
|
300 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) |
|
301 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) |
|
302 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) |
|
303 |
|
304 self.__data = self.__project.getData("CHECKERSPARMS", "Pep8Checker") |
|
305 if self.__data is None or \ |
|
306 len(self.__data) < 6: |
|
307 # initialize the data structure |
|
308 self.__data = { |
|
309 "ExcludeFiles": "", |
|
310 "ExcludeMessages": pycodestyle.DEFAULT_IGNORE, |
|
311 "IncludeMessages": "", |
|
312 "RepeatMessages": False, |
|
313 "FixCodes": "", |
|
314 "FixIssues": False, |
|
315 } |
|
316 if "MaxLineLength" not in self.__data: |
|
317 self.__data["MaxLineLength"] = pycodestyle.MAX_LINE_LENGTH |
|
318 if "MaxDocLineLength" not in self.__data: |
|
319 # Use MAX_LINE_LENGTH to avoid messages on existing code |
|
320 self.__data["MaxDocLineLength"] = pycodestyle.MAX_LINE_LENGTH |
|
321 if "BlankLines" not in self.__data: |
|
322 self.__data["BlankLines"] = (2, 1) |
|
323 # top level, method |
|
324 if "HangClosing" not in self.__data: |
|
325 self.__data["HangClosing"] = False |
|
326 if "NoFixCodes" not in self.__data: |
|
327 self.__data["NoFixCodes"] = "E501" |
|
328 if "DocstringType" not in self.__data: |
|
329 self.__data["DocstringType"] = "pep257" |
|
330 if "ShowIgnored" not in self.__data: |
|
331 self.__data["ShowIgnored"] = False |
|
332 if "MaxCodeComplexity" not in self.__data: |
|
333 self.__data["MaxCodeComplexity"] = 10 |
|
334 if "LineComplexity" not in self.__data: |
|
335 self.__data["LineComplexity"] = 15 |
|
336 if "LineComplexityScore" not in self.__data: |
|
337 self.__data["LineComplexityScore"] = 10 |
|
338 if "ValidEncodings" not in self.__data: |
|
339 self.__data["ValidEncodings"] = "latin-1, utf-8" |
|
340 if "CopyrightMinFileSize" not in self.__data or \ |
|
341 "CopyrightAuthor" not in self.__data: |
|
342 self.__data["CopyrightMinFileSize"] = 0 |
|
343 self.__data["CopyrightAuthor"] = "" |
|
344 if "FutureChecker" not in self.__data: |
|
345 self.__data["FutureChecker"] = "" |
|
346 if "BuiltinsChecker" not in self.__data: |
|
347 self.__data["BuiltinsChecker"] = { |
|
348 "str": ["unicode", ], |
|
349 "chr": ["unichr", ], |
|
350 } |
|
351 |
|
352 self.excludeFilesEdit.setText(self.__data["ExcludeFiles"]) |
|
353 self.excludeMessagesEdit.setText(self.__data["ExcludeMessages"]) |
|
354 self.includeMessagesEdit.setText(self.__data["IncludeMessages"]) |
|
355 self.repeatCheckBox.setChecked(self.__data["RepeatMessages"]) |
|
356 self.fixIssuesEdit.setText(self.__data["FixCodes"]) |
|
357 self.noFixIssuesEdit.setText(self.__data["NoFixCodes"]) |
|
358 self.fixIssuesCheckBox.setChecked(self.__data["FixIssues"]) |
|
359 self.ignoredCheckBox.setChecked(self.__data["ShowIgnored"]) |
|
360 self.lineLengthSpinBox.setValue(self.__data["MaxLineLength"]) |
|
361 self.docLineLengthSpinBox.setValue(self.__data["MaxDocLineLength"]) |
|
362 self.blankBeforeTopLevelSpinBox.setValue(self.__data["BlankLines"][0]) |
|
363 self.blankBeforeMethodSpinBox.setValue(self.__data["BlankLines"][1]) |
|
364 self.hangClosingCheckBox.setChecked(self.__data["HangClosing"]) |
|
365 self.docTypeComboBox.setCurrentIndex( |
|
366 self.docTypeComboBox.findData(self.__data["DocstringType"])) |
|
367 self.complexitySpinBox.setValue(self.__data["MaxCodeComplexity"]) |
|
368 self.lineComplexitySpinBox.setValue(self.__data["LineComplexity"]) |
|
369 self.lineComplexityScoreSpinBox.setValue( |
|
370 self.__data["LineComplexityScore"]) |
|
371 self.encodingsEdit.setText(self.__data["ValidEncodings"]) |
|
372 self.copyrightFileSizeSpinBox.setValue( |
|
373 self.__data["CopyrightMinFileSize"]) |
|
374 self.copyrightAuthorEdit.setText(self.__data["CopyrightAuthor"]) |
|
375 self.__initFuturesList(self.__data["FutureChecker"]) |
|
376 self.__initBuiltinsIgnoreList(self.__data["BuiltinsChecker"]) |
|
377 |
|
378 def start(self, fn, save=False, repeat=None): |
|
379 """ |
|
380 Public slot to start the code style check. |
|
381 |
|
382 @param fn file or list of files or directory to be checked |
|
383 (string or list of strings) |
|
384 @keyparam save flag indicating to save the given |
|
385 file/file list/directory (boolean) |
|
386 @keyparam repeat state of the repeat check box if it is not None |
|
387 (None or boolean) |
|
388 """ |
|
389 if self.__project is None: |
|
390 self.__project = e5App().getObject("Project") |
|
391 |
|
392 self.cancelled = False |
|
393 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) |
|
394 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True) |
|
395 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) |
|
396 self.statisticsButton.setEnabled(False) |
|
397 self.showButton.setEnabled(False) |
|
398 self.fixButton.setEnabled(False) |
|
399 self.startButton.setEnabled(False) |
|
400 if repeat is not None: |
|
401 self.repeatCheckBox.setChecked(repeat) |
|
402 self.checkProgress.setVisible(True) |
|
403 QApplication.processEvents() |
|
404 |
|
405 if save: |
|
406 self.__fileOrFileList = fn |
|
407 |
|
408 if isinstance(fn, list): |
|
409 self.files = fn[:] |
|
410 elif os.path.isdir(fn): |
|
411 self.files = [] |
|
412 extensions = set(Preferences.getPython("PythonExtensions") + |
|
413 Preferences.getPython("Python3Extensions")) |
|
414 for ext in extensions: |
|
415 self.files.extend(Utilities.direntries( |
|
416 fn, True, '*{0}'.format(ext), 0)) |
|
417 else: |
|
418 self.files = [fn] |
|
419 |
|
420 # filter the list depending on the filter string |
|
421 if self.files: |
|
422 filterString = self.excludeFilesEdit.text() |
|
423 filterList = [f.strip() for f in filterString.split(",") |
|
424 if f.strip()] |
|
425 for fileFilter in filterList: |
|
426 self.files = \ |
|
427 [f for f in self.files |
|
428 if not fnmatch.fnmatch(f, fileFilter.strip())] |
|
429 |
|
430 self.__errorItem = None |
|
431 self.__resetStatistics() |
|
432 self.__clearErrors(self.files) |
|
433 |
|
434 if len(self.files) > 0: |
|
435 self.checkProgress.setMaximum(len(self.files)) |
|
436 self.checkProgressLabel.setVisible(len(self.files) > 1) |
|
437 self.checkProgress.setVisible(len(self.files) > 1) |
|
438 QApplication.processEvents() |
|
439 |
|
440 # extract the configuration values |
|
441 excludeMessages = self.excludeMessagesEdit.text() |
|
442 includeMessages = self.includeMessagesEdit.text() |
|
443 repeatMessages = self.repeatCheckBox.isChecked() |
|
444 fixCodes = self.fixIssuesEdit.text() |
|
445 noFixCodes = self.noFixIssuesEdit.text() |
|
446 self.__noFixCodesList = \ |
|
447 [c.strip() for c in noFixCodes.split(",") if c.strip()] |
|
448 fixIssues = self.fixIssuesCheckBox.isChecked() and repeatMessages |
|
449 self.showIgnored = self.ignoredCheckBox.isChecked() and \ |
|
450 repeatMessages |
|
451 maxLineLength = self.lineLengthSpinBox.value() |
|
452 maxDocLineLength = self.docLineLengthSpinBox.value() |
|
453 blankLines = ( |
|
454 self.blankBeforeTopLevelSpinBox.value(), |
|
455 self.blankBeforeMethodSpinBox.value() |
|
456 ) |
|
457 hangClosing = self.hangClosingCheckBox.isChecked() |
|
458 docType = self.docTypeComboBox.itemData( |
|
459 self.docTypeComboBox.currentIndex()) |
|
460 codeComplexityArgs = { |
|
461 "McCabeComplexity": self.complexitySpinBox.value(), |
|
462 "LineComplexity": self.lineComplexitySpinBox.value(), |
|
463 "LineComplexityScore": self.lineComplexityScoreSpinBox.value(), |
|
464 } |
|
465 miscellaneousArgs = { |
|
466 "CodingChecker": self.encodingsEdit.text(), |
|
467 "CopyrightChecker": { |
|
468 "MinFilesize": self.copyrightFileSizeSpinBox.value(), |
|
469 "Author": self.copyrightAuthorEdit.text(), |
|
470 }, |
|
471 "FutureChecker": self.__getSelectedFutureImports(), |
|
472 "BuiltinsChecker": self.__getBuiltinsIgnoreList(), |
|
473 } |
|
474 |
|
475 self.__options = [excludeMessages, includeMessages, repeatMessages, |
|
476 fixCodes, noFixCodes, fixIssues, maxLineLength, |
|
477 maxDocLineLength, blankLines, hangClosing, |
|
478 docType, codeComplexityArgs, miscellaneousArgs] |
|
479 |
|
480 # now go through all the files |
|
481 self.progress = 0 |
|
482 self.files.sort() |
|
483 if len(self.files) == 1: |
|
484 self.__batch = False |
|
485 self.check() |
|
486 else: |
|
487 self.__batch = True |
|
488 self.checkBatch() |
|
489 else: |
|
490 self.results = CodeStyleCheckerDialog.noFiles |
|
491 self.__finished = False |
|
492 self.__finish() |
|
493 |
|
494 def __modifyOptions(self, source): |
|
495 """ |
|
496 Private method to modify the options based on eflag: entries. |
|
497 |
|
498 This method looks for comment lines like '# eflag: noqa = M601' |
|
499 at the end of the source in order to extend the list of excluded |
|
500 messages for one file only. |
|
501 |
|
502 @param source source text (list of str or str) |
|
503 @return list of checker options |
|
504 """ |
|
505 options = self.__options[:] |
|
506 flags = Utilities.extractFlags(source) |
|
507 if "noqa" in flags and isinstance(flags["noqa"], basestring): |
|
508 excludeMessages = \ |
|
509 options[0].strip().rstrip(",") |
|
510 if excludeMessages: |
|
511 excludeMessages += "," |
|
512 excludeMessages += flags["noqa"] |
|
513 options[0] = excludeMessages |
|
514 return options |
|
515 |
|
516 def check(self, codestring=''): |
|
517 """ |
|
518 Public method to start a style check for one file. |
|
519 |
|
520 The results are reported to the __processResult slot. |
|
521 |
|
522 @keyparam codestring optional sourcestring (str) |
|
523 """ |
|
524 if not self.files: |
|
525 self.checkProgressLabel.setPath("") |
|
526 self.checkProgress.setMaximum(1) |
|
527 self.checkProgress.setValue(1) |
|
528 self.__finish() |
|
529 return |
|
530 |
|
531 self.filename = self.files.pop(0) |
|
532 self.checkProgress.setValue(self.progress) |
|
533 self.checkProgressLabel.setPath(self.filename) |
|
534 QApplication.processEvents() |
|
535 |
|
536 if self.cancelled: |
|
537 self.__resort() |
|
538 return |
|
539 |
|
540 self.__lastFileItem = None |
|
541 |
|
542 if codestring: |
|
543 source = codestring.splitlines(True) |
|
544 encoding = Utilities.get_coding(source) |
|
545 else: |
|
546 try: |
|
547 source, encoding = Utilities.readEncodedFile( |
|
548 self.filename) |
|
549 source = source.splitlines(True) |
|
550 except (UnicodeError, IOError) as msg: |
|
551 self.results = CodeStyleCheckerDialog.hasResults |
|
552 self.__createResultItem( |
|
553 self.filename, 1, 1, |
|
554 self.tr("Error: {0}").format(str(msg)) |
|
555 .rstrip(), False, False, False) |
|
556 self.progress += 1 |
|
557 # Continue with next file |
|
558 self.check() |
|
559 return |
|
560 if encoding.endswith( |
|
561 ('-selected', '-default', '-guessed', '-ignore')): |
|
562 encoding = encoding.rsplit('-', 1)[0] |
|
563 |
|
564 options = self.__modifyOptions(source) |
|
565 |
|
566 errors = [] |
|
567 self.__itms = [] |
|
568 for error, itm in self.__onlyFixes.pop(self.filename, []): |
|
569 errors.append(error) |
|
570 self.__itms.append(itm) |
|
571 |
|
572 eol = self.__getEol(self.filename) |
|
573 args = options + [ |
|
574 errors, eol, encoding, Preferences.getEditor("CreateBackupFile") |
|
575 ] |
|
576 self.__finished = False |
|
577 self.styleCheckService.styleCheck( |
|
578 None, self.filename, source, args) |
|
579 |
|
580 def checkBatch(self): |
|
581 """ |
|
582 Public method to start a style check batch job. |
|
583 |
|
584 The results are reported to the __processResult slot. |
|
585 """ |
|
586 self.__lastFileItem = None |
|
587 |
|
588 self.checkProgressLabel.setPath(self.tr("Preparing files...")) |
|
589 progress = 0 |
|
590 |
|
591 argumentsList = [] |
|
592 for filename in self.files: |
|
593 progress += 1 |
|
594 self.checkProgress.setValue(progress) |
|
595 QApplication.processEvents() |
|
596 |
|
597 try: |
|
598 source, encoding = Utilities.readEncodedFile( |
|
599 filename) |
|
600 source = source.splitlines(True) |
|
601 except (UnicodeError, IOError) as msg: |
|
602 self.results = CodeStyleCheckerDialog.hasResults |
|
603 self.__createResultItem( |
|
604 filename, 1, 1, |
|
605 self.tr("Error: {0}").format(str(msg)) |
|
606 .rstrip(), False, False, False) |
|
607 continue |
|
608 |
|
609 if encoding.endswith( |
|
610 ('-selected', '-default', '-guessed', '-ignore')): |
|
611 encoding = encoding.rsplit('-', 1)[0] |
|
612 |
|
613 options = self.__modifyOptions(source) |
|
614 |
|
615 errors = [] |
|
616 self.__itms = [] |
|
617 for error, itm in self.__onlyFixes.pop(filename, []): |
|
618 errors.append(error) |
|
619 self.__itms.append(itm) |
|
620 |
|
621 eol = self.__getEol(filename) |
|
622 args = options + [ |
|
623 errors, eol, encoding, |
|
624 Preferences.getEditor("CreateBackupFile") |
|
625 ] |
|
626 argumentsList.append((filename, source, args)) |
|
627 |
|
628 # reset the progress bar to the checked files |
|
629 self.checkProgress.setValue(self.progress) |
|
630 self.checkProgressLabel.setPath(self.tr("Transferring data...")) |
|
631 QApplication.processEvents() |
|
632 |
|
633 self.__finished = False |
|
634 self.styleCheckService.styleBatchCheck(argumentsList) |
|
635 |
|
636 def __batchFinished(self): |
|
637 """ |
|
638 Private slot handling the completion of a batch job. |
|
639 """ |
|
640 self.checkProgressLabel.setPath("") |
|
641 self.checkProgress.setMaximum(1) |
|
642 self.checkProgress.setValue(1) |
|
643 self.__finish() |
|
644 |
|
645 def __processError(self, fn, msg): |
|
646 """ |
|
647 Private slot to process an error indication from the service. |
|
648 |
|
649 @param fn filename of the file |
|
650 @type str |
|
651 @param msg error message |
|
652 @type str |
|
653 """ |
|
654 self.__createErrorItem(fn, msg) |
|
655 |
|
656 if not self.__batch: |
|
657 self.check() |
|
658 |
|
659 def __processResult(self, fn, codeStyleCheckerStats, fixes, results): |
|
660 """ |
|
661 Private slot called after perfoming a style check on one file. |
|
662 |
|
663 @param fn filename of the just checked file (str) |
|
664 @param codeStyleCheckerStats stats of style and name check (dict) |
|
665 @param fixes number of applied fixes (int) |
|
666 @param results tuple for each found violation of style (tuple of |
|
667 lineno (int), position (int), text (str), ignored (bool), |
|
668 fixed (bool), autofixing (bool)) |
|
669 """ |
|
670 if self.__finished: |
|
671 return |
|
672 |
|
673 # Check if it's the requested file, otherwise ignore signal if not |
|
674 # in batch mode |
|
675 if not self.__batch and fn != self.filename: |
|
676 return |
|
677 |
|
678 # disable updates of the list for speed |
|
679 self.resultList.setUpdatesEnabled(False) |
|
680 self.resultList.setSortingEnabled(False) |
|
681 |
|
682 fixed = None |
|
683 ignoredErrors = 0 |
|
684 if self.__itms: |
|
685 for itm, (_lineno, _position, text, _ignored, fixed, |
|
686 _autofixing) in zip(self.__itms, results): |
|
687 self.__modifyFixedResultItem(itm, text, fixed) |
|
688 self.__updateFixerStatistics(fixes) |
|
689 else: |
|
690 self.__lastFileItem = None |
|
691 |
|
692 for lineno, position, text, ignored, fixed, autofixing in results: |
|
693 if ignored: |
|
694 ignoredErrors += 1 |
|
695 if self.showIgnored: |
|
696 text = self.tr("{0} (ignored)").format(text) |
|
697 else: |
|
698 continue |
|
699 self.results = CodeStyleCheckerDialog.hasResults |
|
700 self.__createResultItem( |
|
701 fn, lineno, position, text, fixed, autofixing, ignored) |
|
702 |
|
703 self.__updateStatistics( |
|
704 codeStyleCheckerStats, fixes, ignoredErrors) |
|
705 |
|
706 if fixed: |
|
707 vm = e5App().getObject("ViewManager") |
|
708 editor = vm.getOpenEditor(fn) |
|
709 if editor: |
|
710 editor.refresh() |
|
711 |
|
712 self.progress += 1 |
|
713 |
|
714 self.__resort() |
|
715 # reenable updates of the list |
|
716 self.resultList.setSortingEnabled(True) |
|
717 self.resultList.setUpdatesEnabled(True) |
|
718 |
|
719 self.checkProgress.setValue(self.progress) |
|
720 self.checkProgressLabel.setPath(fn) |
|
721 QApplication.processEvents() |
|
722 |
|
723 if not self.__batch: |
|
724 self.check() |
|
725 |
|
726 def __finish(self): |
|
727 """ |
|
728 Private slot called when the code style check finished or the user |
|
729 pressed the cancel button. |
|
730 """ |
|
731 if not self.__finished: |
|
732 self.__finished = True |
|
733 |
|
734 self.cancelled = True |
|
735 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) |
|
736 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) |
|
737 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) |
|
738 self.statisticsButton.setEnabled(True) |
|
739 self.showButton.setEnabled(True) |
|
740 self.startButton.setEnabled(True) |
|
741 |
|
742 if self.results != CodeStyleCheckerDialog.hasResults: |
|
743 if self.results == CodeStyleCheckerDialog.noResults: |
|
744 QTreeWidgetItem( |
|
745 self.resultList, [self.tr('No issues found.')]) |
|
746 else: |
|
747 QTreeWidgetItem( |
|
748 self.resultList, |
|
749 [self.tr('No files found (check your ignore list).')]) |
|
750 QApplication.processEvents() |
|
751 self.showButton.setEnabled(False) |
|
752 else: |
|
753 self.showButton.setEnabled(True) |
|
754 self.resultList.header().resizeSections( |
|
755 QHeaderView.ResizeToContents) |
|
756 self.resultList.header().setStretchLastSection(True) |
|
757 |
|
758 self.checkProgress.setVisible(False) |
|
759 self.checkProgressLabel.setVisible(False) |
|
760 |
|
761 def __getEol(self, fn): |
|
762 """ |
|
763 Private method to get the applicable eol string. |
|
764 |
|
765 @param fn filename where to determine the line ending (str) |
|
766 @return eol string (string) |
|
767 """ |
|
768 if self.__project.isOpen() and self.__project.isProjectFile(fn): |
|
769 eol = self.__project.getEolString() |
|
770 else: |
|
771 eol = Utilities.linesep() |
|
772 return eol |
|
773 |
|
774 @pyqtSlot() |
|
775 def on_startButton_clicked(self): |
|
776 """ |
|
777 Private slot to start a code style check run. |
|
778 """ |
|
779 if self.__forProject: |
|
780 data = { |
|
781 "ExcludeFiles": self.excludeFilesEdit.text(), |
|
782 "ExcludeMessages": self.excludeMessagesEdit.text(), |
|
783 "IncludeMessages": self.includeMessagesEdit.text(), |
|
784 "RepeatMessages": self.repeatCheckBox.isChecked(), |
|
785 "FixCodes": self.fixIssuesEdit.text(), |
|
786 "NoFixCodes": self.noFixIssuesEdit.text(), |
|
787 "FixIssues": self.fixIssuesCheckBox.isChecked(), |
|
788 "ShowIgnored": self.ignoredCheckBox.isChecked(), |
|
789 "MaxLineLength": self.lineLengthSpinBox.value(), |
|
790 "MaxDocLineLength": self.docLineLengthSpinBox.value(), |
|
791 "BlankLines": ( |
|
792 self.blankBeforeTopLevelSpinBox.value(), |
|
793 self.blankBeforeMethodSpinBox.value() |
|
794 ), |
|
795 "HangClosing": self.hangClosingCheckBox.isChecked(), |
|
796 "DocstringType": self.docTypeComboBox.itemData( |
|
797 self.docTypeComboBox.currentIndex()), |
|
798 "MaxCodeComplexity": self.complexitySpinBox.value(), |
|
799 "LineComplexity": self.lineComplexitySpinBox.value(), |
|
800 "LineComplexityScore": self.lineComplexityScoreSpinBox.value(), |
|
801 "ValidEncodings": self.encodingsEdit.text(), |
|
802 "CopyrightMinFileSize": self.copyrightFileSizeSpinBox.value(), |
|
803 "CopyrightAuthor": self.copyrightAuthorEdit.text(), |
|
804 "FutureChecker": self.__getSelectedFutureImports(), |
|
805 "BuiltinsChecker": self.__getBuiltinsIgnoreList(), |
|
806 } |
|
807 if data != self.__data: |
|
808 self.__data = data |
|
809 self.__project.setData("CHECKERSPARMS", "Pep8Checker", |
|
810 self.__data) |
|
811 |
|
812 self.resultList.clear() |
|
813 self.results = CodeStyleCheckerDialog.noResults |
|
814 self.cancelled = False |
|
815 self.start(self.__fileOrFileList) |
|
816 |
|
817 def __selectCodes(self, edit, showFixCodes): |
|
818 """ |
|
819 Private method to select message codes via a selection dialog. |
|
820 |
|
821 @param edit reference of the line edit to be populated (QLineEdit) |
|
822 @param showFixCodes flag indicating to show a list of fixable |
|
823 issues (boolean) |
|
824 """ |
|
825 from .CodeStyleCodeSelectionDialog import CodeStyleCodeSelectionDialog |
|
826 dlg = CodeStyleCodeSelectionDialog(edit.text(), showFixCodes, self) |
|
827 if dlg.exec_() == QDialog.Accepted: |
|
828 edit.setText(dlg.getSelectedCodes()) |
|
829 |
|
830 @pyqtSlot() |
|
831 def on_excludeMessagesSelectButton_clicked(self): |
|
832 """ |
|
833 Private slot to select the message codes to be excluded via a |
|
834 selection dialog. |
|
835 """ |
|
836 self.__selectCodes(self.excludeMessagesEdit, False) |
|
837 |
|
838 @pyqtSlot() |
|
839 def on_includeMessagesSelectButton_clicked(self): |
|
840 """ |
|
841 Private slot to select the message codes to be included via a |
|
842 selection dialog. |
|
843 """ |
|
844 self.__selectCodes(self.includeMessagesEdit, False) |
|
845 |
|
846 @pyqtSlot() |
|
847 def on_fixIssuesSelectButton_clicked(self): |
|
848 """ |
|
849 Private slot to select the issue codes to be fixed via a |
|
850 selection dialog. |
|
851 """ |
|
852 self.__selectCodes(self.fixIssuesEdit, True) |
|
853 |
|
854 @pyqtSlot() |
|
855 def on_noFixIssuesSelectButton_clicked(self): |
|
856 """ |
|
857 Private slot to select the issue codes not to be fixed via a |
|
858 selection dialog. |
|
859 """ |
|
860 self.__selectCodes(self.noFixIssuesEdit, True) |
|
861 |
|
862 @pyqtSlot(QTreeWidgetItem, int) |
|
863 def on_resultList_itemActivated(self, item, column): |
|
864 """ |
|
865 Private slot to handle the activation of an item. |
|
866 |
|
867 @param item reference to the activated item (QTreeWidgetItem) |
|
868 @param column column the item was activated in (integer) |
|
869 """ |
|
870 if self.results != CodeStyleCheckerDialog.hasResults: |
|
871 return |
|
872 |
|
873 if item.parent(): |
|
874 fn = Utilities.normabspath(item.data(0, self.filenameRole)) |
|
875 lineno = item.data(0, self.lineRole) |
|
876 position = item.data(0, self.positionRole) |
|
877 message = item.data(0, self.messageRole) |
|
878 code = item.data(0, self.codeRole) |
|
879 |
|
880 vm = e5App().getObject("ViewManager") |
|
881 vm.openSourceFile(fn, lineno=lineno, pos=position + 1) |
|
882 editor = vm.getOpenEditor(fn) |
|
883 |
|
884 if code in ["E901", "E902"]: |
|
885 editor.toggleSyntaxError(lineno, 0, True, message, True) |
|
886 else: |
|
887 editor.toggleWarning( |
|
888 lineno, 0, True, message, warningType=editor.WarningStyle) |
|
889 |
|
890 editor.updateVerticalScrollBar() |
|
891 |
|
892 @pyqtSlot() |
|
893 def on_resultList_itemSelectionChanged(self): |
|
894 """ |
|
895 Private slot to change the dialog state depending on the selection. |
|
896 """ |
|
897 self.fixButton.setEnabled(len(self.__getSelectedFixableItems()) > 0) |
|
898 |
|
899 @pyqtSlot() |
|
900 def on_showButton_clicked(self): |
|
901 """ |
|
902 Private slot to handle the "Show" button press. |
|
903 """ |
|
904 vm = e5App().getObject("ViewManager") |
|
905 |
|
906 selectedIndexes = [] |
|
907 for index in range(self.resultList.topLevelItemCount()): |
|
908 if self.resultList.topLevelItem(index).isSelected(): |
|
909 selectedIndexes.append(index) |
|
910 if len(selectedIndexes) == 0: |
|
911 selectedIndexes = list(range(self.resultList.topLevelItemCount())) |
|
912 for index in selectedIndexes: |
|
913 itm = self.resultList.topLevelItem(index) |
|
914 fn = Utilities.normabspath(itm.data(0, self.filenameRole)) |
|
915 vm.openSourceFile(fn, 1) |
|
916 editor = vm.getOpenEditor(fn) |
|
917 editor.clearStyleWarnings() |
|
918 for cindex in range(itm.childCount()): |
|
919 citm = itm.child(cindex) |
|
920 lineno = citm.data(0, self.lineRole) |
|
921 message = citm.data(0, self.messageRole) |
|
922 editor.toggleWarning( |
|
923 lineno, 0, True, message, warningType=editor.WarningStyle) |
|
924 |
|
925 # go through the list again to clear warning markers for files, |
|
926 # that are ok |
|
927 openFiles = vm.getOpenFilenames() |
|
928 errorFiles = [] |
|
929 for index in range(self.resultList.topLevelItemCount()): |
|
930 itm = self.resultList.topLevelItem(index) |
|
931 errorFiles.append( |
|
932 Utilities.normabspath(itm.data(0, self.filenameRole))) |
|
933 for file in openFiles: |
|
934 if file not in errorFiles: |
|
935 editor = vm.getOpenEditor(file) |
|
936 editor.clearStyleWarnings() |
|
937 |
|
938 editor = vm.activeWindow() |
|
939 editor.updateVerticalScrollBar() |
|
940 |
|
941 @pyqtSlot() |
|
942 def on_statisticsButton_clicked(self): |
|
943 """ |
|
944 Private slot to show the statistics dialog. |
|
945 """ |
|
946 from .CodeStyleStatisticsDialog import CodeStyleStatisticsDialog |
|
947 dlg = CodeStyleStatisticsDialog(self.__statistics, self) |
|
948 dlg.exec_() |
|
949 |
|
950 @pyqtSlot() |
|
951 def on_loadDefaultButton_clicked(self): |
|
952 """ |
|
953 Private slot to load the default configuration values. |
|
954 """ |
|
955 self.excludeFilesEdit.setText(Preferences.Prefs.settings.value( |
|
956 "PEP8/ExcludeFilePatterns", "")) |
|
957 self.excludeMessagesEdit.setText(Preferences.Prefs.settings.value( |
|
958 "PEP8/ExcludeMessages", pycodestyle.DEFAULT_IGNORE)) |
|
959 self.includeMessagesEdit.setText(Preferences.Prefs.settings.value( |
|
960 "PEP8/IncludeMessages", "")) |
|
961 self.repeatCheckBox.setChecked(Preferences.toBool( |
|
962 Preferences.Prefs.settings.value("PEP8/RepeatMessages", False))) |
|
963 self.fixIssuesEdit.setText(Preferences.Prefs.settings.value( |
|
964 "PEP8/FixCodes", "")) |
|
965 self.noFixIssuesEdit.setText(Preferences.Prefs.settings.value( |
|
966 "PEP8/NoFixCodes", "E501")) |
|
967 self.fixIssuesCheckBox.setChecked(Preferences.toBool( |
|
968 Preferences.Prefs.settings.value("PEP8/FixIssues", False))) |
|
969 self.ignoredCheckBox.setChecked(Preferences.toBool( |
|
970 Preferences.Prefs.settings.value("PEP8/ShowIgnored", False))) |
|
971 self.lineLengthSpinBox.setValue(int(Preferences.Prefs.settings.value( |
|
972 "PEP8/MaxLineLength", pycodestyle.MAX_LINE_LENGTH))) |
|
973 # Use MAX_LINE_LENGTH to avoid messages on existing code |
|
974 self.docLineLengthSpinBox.setValue(int( |
|
975 Preferences.Prefs.settings.value( |
|
976 "PEP8/MaxDocLineLength", pycodestyle.MAX_LINE_LENGTH))) |
|
977 self.blankBeforeTopLevelSpinBox.setValue( |
|
978 int(Preferences.Prefs.settings.value( |
|
979 "PEP8/BlankLinesBeforeTopLevel", 2))) |
|
980 self.blankBeforeMethodSpinBox.setValue( |
|
981 int(Preferences.Prefs.settings.value( |
|
982 "PEP8/BlankLinesBeforeMethod", 1))) |
|
983 self.hangClosingCheckBox.setChecked(Preferences.toBool( |
|
984 Preferences.Prefs.settings.value("PEP8/HangClosing", False))) |
|
985 self.docTypeComboBox.setCurrentIndex(self.docTypeComboBox.findData( |
|
986 Preferences.Prefs.settings.value("PEP8/DocstringType", "pep257"))) |
|
987 self.complexitySpinBox.setValue(int(Preferences.Prefs.settings.value( |
|
988 "PEP8/MaxCodeComplexity", 10))) |
|
989 self.lineComplexitySpinBox.setValue( |
|
990 int(Preferences.Prefs.settings.value( |
|
991 "PEP8/LineComplexity", 15))) |
|
992 self.lineComplexityScoreSpinBox.setValue( |
|
993 int(Preferences.Prefs.settings.value( |
|
994 "PEP8/LineComplexityScore", 10))) |
|
995 self.encodingsEdit.setText(Preferences.Prefs.settings.value( |
|
996 "PEP8/ValidEncodings", "latin-1, utf-8")) |
|
997 self.copyrightFileSizeSpinBox.setValue(int( |
|
998 Preferences.Prefs.settings.value("PEP8/CopyrightMinFileSize", 0))) |
|
999 self.copyrightAuthorEdit.setText( |
|
1000 Preferences.Prefs.settings.value("PEP8/CopyrightAuthor", "")) |
|
1001 self.__initFuturesList( |
|
1002 Preferences.Prefs.settings.value("PEP8/FutureChecker", "")) |
|
1003 self.__initBuiltinsIgnoreList(Preferences.toDict( |
|
1004 Preferences.Prefs.settings.value("PEP8/BuiltinsChecker", { |
|
1005 "str": ["unicode", ], |
|
1006 "chr": ["unichr", ], |
|
1007 }))) |
|
1008 |
|
1009 @pyqtSlot() |
|
1010 def on_storeDefaultButton_clicked(self): |
|
1011 """ |
|
1012 Private slot to store the current configuration values as |
|
1013 default values. |
|
1014 """ |
|
1015 Preferences.Prefs.settings.setValue( |
|
1016 "PEP8/ExcludeFilePatterns", self.excludeFilesEdit.text()) |
|
1017 Preferences.Prefs.settings.setValue( |
|
1018 "PEP8/ExcludeMessages", self.excludeMessagesEdit.text()) |
|
1019 Preferences.Prefs.settings.setValue( |
|
1020 "PEP8/IncludeMessages", self.includeMessagesEdit.text()) |
|
1021 Preferences.Prefs.settings.setValue( |
|
1022 "PEP8/RepeatMessages", self.repeatCheckBox.isChecked()) |
|
1023 Preferences.Prefs.settings.setValue( |
|
1024 "PEP8/FixCodes", self.fixIssuesEdit.text()) |
|
1025 Preferences.Prefs.settings.setValue( |
|
1026 "PEP8/NoFixCodes", self.noFixIssuesEdit.text()) |
|
1027 Preferences.Prefs.settings.setValue( |
|
1028 "PEP8/FixIssues", self.fixIssuesCheckBox.isChecked()) |
|
1029 Preferences.Prefs.settings.setValue( |
|
1030 "PEP8/ShowIgnored", self.ignoredCheckBox.isChecked()) |
|
1031 Preferences.Prefs.settings.setValue( |
|
1032 "PEP8/MaxLineLength", self.lineLengthSpinBox.value()) |
|
1033 Preferences.Prefs.settings.setValue( |
|
1034 "PEP8/MaxDocLineLength", self.docLineLengthSpinBox.value()) |
|
1035 Preferences.Prefs.settings.setValue( |
|
1036 "PEP8/BlankLinesBeforeTopLevel", |
|
1037 self.blankBeforeTopLevelSpinBox.value()) |
|
1038 Preferences.Prefs.settings.setValue( |
|
1039 "PEP8/BlankLinesBeforeMethod", |
|
1040 self.blankBeforeMethodSpinBox.value()) |
|
1041 Preferences.Prefs.settings.setValue( |
|
1042 "PEP8/HangClosing", self.hangClosingCheckBox.isChecked()) |
|
1043 Preferences.Prefs.settings.setValue( |
|
1044 "PEP8/DocstringType", self.docTypeComboBox.itemData( |
|
1045 self.docTypeComboBox.currentIndex())) |
|
1046 Preferences.Prefs.settings.setValue( |
|
1047 "PEP8/MaxCodeComplexity", self.complexitySpinBox.value()) |
|
1048 Preferences.Prefs.settings.setValue( |
|
1049 "PEP8/LineComplexity", self.lineComplexitySpinBox.value()) |
|
1050 Preferences.Prefs.settings.setValue( |
|
1051 "PEP8/LineComplexityScore", |
|
1052 self.lineComplexityScoreSpinBox.value()) |
|
1053 Preferences.Prefs.settings.setValue( |
|
1054 "PEP8/ValidEncodings", self.encodingsEdit.text()) |
|
1055 Preferences.Prefs.settings.setValue( |
|
1056 "PEP8/CopyrightMinFileSize", self.copyrightFileSizeSpinBox.value()) |
|
1057 Preferences.Prefs.settings.setValue( |
|
1058 "PEP8/CopyrightAuthor", self.copyrightAuthorEdit.text()) |
|
1059 Preferences.Prefs.settings.setValue( |
|
1060 "PEP8/FutureChecker", self.__getSelectedFutureImports()) |
|
1061 Preferences.Prefs.settings.setValue( |
|
1062 "PEP8/BuiltinsChecker", self.__getBuiltinsIgnoreList()) |
|
1063 |
|
1064 @pyqtSlot() |
|
1065 def on_resetDefaultButton_clicked(self): |
|
1066 """ |
|
1067 Private slot to reset the configuration values to their default values. |
|
1068 """ |
|
1069 Preferences.Prefs.settings.setValue("PEP8/ExcludeFilePatterns", "") |
|
1070 Preferences.Prefs.settings.setValue( |
|
1071 "PEP8/ExcludeMessages", pycodestyle.DEFAULT_IGNORE) |
|
1072 Preferences.Prefs.settings.setValue("PEP8/IncludeMessages", "") |
|
1073 Preferences.Prefs.settings.setValue("PEP8/RepeatMessages", False) |
|
1074 Preferences.Prefs.settings.setValue("PEP8/FixCodes", "") |
|
1075 Preferences.Prefs.settings.setValue("PEP8/NoFixCodes", "E501") |
|
1076 Preferences.Prefs.settings.setValue("PEP8/FixIssues", False) |
|
1077 Preferences.Prefs.settings.setValue("PEP8/ShowIgnored", False) |
|
1078 Preferences.Prefs.settings.setValue( |
|
1079 "PEP8/MaxLineLength", pycodestyle.MAX_LINE_LENGTH) |
|
1080 # Hard reset to pycodestyle preferences |
|
1081 Preferences.Prefs.settings.setValue( |
|
1082 "PEP8/MaxDocLineLength", pycodestyle.MAX_DOC_LENGTH) |
|
1083 Preferences.Prefs.settings.setValue( |
|
1084 "PEP8/BlankLinesBeforeTopLevel", 2) |
|
1085 Preferences.Prefs.settings.setValue( |
|
1086 "PEP8/BlankLinesBeforeMethod", 1) |
|
1087 Preferences.Prefs.settings.setValue("PEP8/HangClosing", False) |
|
1088 Preferences.Prefs.settings.setValue("PEP8/DocstringType", "pep257") |
|
1089 Preferences.Prefs.settings.setValue("PEP8/MaxCodeComplexity", 10) |
|
1090 Preferences.Prefs.settings.setValue("PEP8/LineComplexity", 15) |
|
1091 Preferences.Prefs.settings.setValue("PEP8/LineComplexityScore", 10) |
|
1092 Preferences.Prefs.settings.setValue( |
|
1093 "PEP8/ValidEncodings", "latin-1, utf-8") |
|
1094 Preferences.Prefs.settings.setValue("PEP8/CopyrightMinFileSize", 0) |
|
1095 Preferences.Prefs.settings.setValue("PEP8/CopyrightAuthor", "") |
|
1096 Preferences.Prefs.settings.setValue("PEP8/FutureChecker", "") |
|
1097 Preferences.Prefs.settings.setValue("PEP8/BuiltinsChecker", { |
|
1098 "str": ["unicode", ], |
|
1099 "chr": ["unichr", ], |
|
1100 }) |
|
1101 # Update UI with default values |
|
1102 self.on_loadDefaultButton_clicked() |
|
1103 |
|
1104 @pyqtSlot(QAbstractButton) |
|
1105 def on_buttonBox_clicked(self, button): |
|
1106 """ |
|
1107 Private slot called by a button of the button box clicked. |
|
1108 |
|
1109 @param button button that was clicked (QAbstractButton) |
|
1110 """ |
|
1111 if button == self.buttonBox.button(QDialogButtonBox.Close): |
|
1112 self.close() |
|
1113 elif button == self.buttonBox.button(QDialogButtonBox.Cancel): |
|
1114 if self.__batch: |
|
1115 self.styleCheckService.cancelStyleBatchCheck() |
|
1116 QTimer.singleShot(1000, self.__finish) |
|
1117 else: |
|
1118 self.__finish() |
|
1119 elif button == self.showButton: |
|
1120 self.on_showButton_clicked() |
|
1121 elif button == self.statisticsButton: |
|
1122 self.on_statisticsButton_clicked() |
|
1123 |
|
1124 def __clearErrors(self, files): |
|
1125 """ |
|
1126 Private method to clear all warning markers of open editors to be |
|
1127 checked. |
|
1128 |
|
1129 @param files list of files to be checked (list of string) |
|
1130 """ |
|
1131 vm = e5App().getObject("ViewManager") |
|
1132 openFiles = vm.getOpenFilenames() |
|
1133 for file in [f for f in openFiles if f in files]: |
|
1134 editor = vm.getOpenEditor(file) |
|
1135 editor.clearStyleWarnings() |
|
1136 |
|
1137 @pyqtSlot() |
|
1138 def on_fixButton_clicked(self): |
|
1139 """ |
|
1140 Private slot to fix selected issues. |
|
1141 |
|
1142 Build a dictionary of issues to fix. Update the initialized __options. |
|
1143 Then call check with the dict as keyparam to fix selected issues. |
|
1144 """ |
|
1145 fixableItems = self.__getSelectedFixableItems() |
|
1146 # dictionary of lists of tuples containing the issue and the item |
|
1147 fixesDict = {} |
|
1148 for itm in fixableItems: |
|
1149 filename = itm.data(0, self.filenameRole) |
|
1150 if filename not in fixesDict: |
|
1151 fixesDict[filename] = [] |
|
1152 fixesDict[filename].append(( |
|
1153 (filename, itm.data(0, self.lineRole), |
|
1154 itm.data(0, self.positionRole), |
|
1155 "{0} {1}".format(itm.data(0, self.codeRole), |
|
1156 itm.data(0, self.messageRole))), |
|
1157 itm |
|
1158 )) |
|
1159 |
|
1160 # update the configuration values (3: fixCodes, 4: noFixCodes, |
|
1161 # 5: fixIssues, 6: maxLineLength) |
|
1162 self.__options[3] = self.fixIssuesEdit.text() |
|
1163 self.__options[4] = self.noFixIssuesEdit.text() |
|
1164 self.__options[5] = True |
|
1165 self.__options[6] = self.lineLengthSpinBox.value() |
|
1166 |
|
1167 self.files = list(fixesDict.keys()) |
|
1168 # now go through all the files |
|
1169 self.progress = 0 |
|
1170 self.files.sort() |
|
1171 self.cancelled = False |
|
1172 self.__onlyFixes = fixesDict |
|
1173 self.check() |
|
1174 |
|
1175 def __getSelectedFixableItems(self): |
|
1176 """ |
|
1177 Private method to extract all selected items for fixable issues. |
|
1178 |
|
1179 @return selected items for fixable issues (list of QTreeWidgetItem) |
|
1180 """ |
|
1181 fixableItems = [] |
|
1182 for itm in self.resultList.selectedItems(): |
|
1183 if itm.childCount() > 0: |
|
1184 for index in range(itm.childCount()): |
|
1185 citm = itm.child(index) |
|
1186 if self.__itemFixable(citm) and citm not in fixableItems: |
|
1187 fixableItems.append(citm) |
|
1188 elif self.__itemFixable(itm) and itm not in fixableItems: |
|
1189 fixableItems.append(itm) |
|
1190 |
|
1191 return fixableItems |
|
1192 |
|
1193 def __itemFixable(self, itm): |
|
1194 """ |
|
1195 Private method to check, if an item has a fixable issue. |
|
1196 |
|
1197 @param itm item to be checked (QTreeWidgetItem) |
|
1198 @return flag indicating a fixable issue (boolean) |
|
1199 """ |
|
1200 return (itm.data(0, self.fixableRole) and |
|
1201 not itm.data(0, self.ignoredRole)) |
|
1202 |
|
1203 def __initFuturesList(self, selectedFutures): |
|
1204 """ |
|
1205 Private method to set the selected status of the future imports. |
|
1206 |
|
1207 @param selectedFutures comma separated list of expected future imports |
|
1208 @type str |
|
1209 """ |
|
1210 if selectedFutures: |
|
1211 expectedImports = [ |
|
1212 i.strip() for i in selectedFutures.split(",") |
|
1213 if bool(i.strip())] |
|
1214 else: |
|
1215 expectedImports = [] |
|
1216 for row in range(self.futuresList.count()): |
|
1217 itm = self.futuresList.item(row) |
|
1218 if itm.text() in expectedImports: |
|
1219 itm.setCheckState(Qt.Checked) |
|
1220 else: |
|
1221 itm.setCheckState(Qt.Unchecked) |
|
1222 |
|
1223 def __getSelectedFutureImports(self): |
|
1224 """ |
|
1225 Private method to get the expected future imports. |
|
1226 |
|
1227 @return expected future imports as a comma separated string |
|
1228 @rtype str |
|
1229 """ |
|
1230 selectedFutures = [] |
|
1231 for row in range(self.futuresList.count()): |
|
1232 itm = self.futuresList.item(row) |
|
1233 if itm.checkState() == Qt.Checked: |
|
1234 selectedFutures.append(itm.text()) |
|
1235 return ", ".join(selectedFutures) |
|
1236 |
|
1237 def __initBuiltinsIgnoreList(self, builtinsIgnoreDict): |
|
1238 """ |
|
1239 Private method to populate the list of shadowed builtins to be ignored. |
|
1240 |
|
1241 @param builtinsIgnoreDict dictionary containing the builtins |
|
1242 assignments to be ignored |
|
1243 @type dict of list of str |
|
1244 """ |
|
1245 self.builtinsAssignmentList.clear() |
|
1246 for left, rightList in builtinsIgnoreDict.items(): |
|
1247 for right in rightList: |
|
1248 QTreeWidgetItem(self.builtinsAssignmentList, [left, right]) |
|
1249 |
|
1250 self.on_builtinsAssignmentList_itemSelectionChanged() |
|
1251 |
|
1252 def __getBuiltinsIgnoreList(self): |
|
1253 """ |
|
1254 Private method to get a dictionary containing the builtins assignments |
|
1255 to be ignored. |
|
1256 |
|
1257 @return dictionary containing the builtins assignments to be ignored |
|
1258 @rtype dict of list of str |
|
1259 """ |
|
1260 builtinsIgnoreDict = {} |
|
1261 for row in range(self.builtinsAssignmentList.topLevelItemCount()): |
|
1262 itm = self.builtinsAssignmentList.topLevelItem(row) |
|
1263 left, right = itm.text(0), itm.text(1) |
|
1264 if left not in builtinsIgnoreDict: |
|
1265 builtinsIgnoreDict[left] = [] |
|
1266 builtinsIgnoreDict[left].append(right) |
|
1267 |
|
1268 return builtinsIgnoreDict |
|
1269 |
|
1270 @pyqtSlot() |
|
1271 def on_builtinsAssignmentList_itemSelectionChanged(self): |
|
1272 """ |
|
1273 Private slot to react upon changes of the selected builtin assignments. |
|
1274 """ |
|
1275 self.deleteBuiltinButton.setEnabled( |
|
1276 len(self.builtinsAssignmentList.selectedItems()) > 0) |
|
1277 |
|
1278 @pyqtSlot() |
|
1279 def on_addBuiltinButton_clicked(self): |
|
1280 """ |
|
1281 Private slot to add a built-in assignment to be ignored. |
|
1282 """ |
|
1283 from .CodeStyleAddBuiltinIgnoreDialog import \ |
|
1284 CodeStyleAddBuiltinIgnoreDialog |
|
1285 dlg = CodeStyleAddBuiltinIgnoreDialog(self) |
|
1286 if dlg.exec_() == QDialog.Accepted: |
|
1287 left, right = dlg.getData() |
|
1288 QTreeWidgetItem(self.builtinsAssignmentList, [left, right]) |
|
1289 |
|
1290 @pyqtSlot() |
|
1291 def on_deleteBuiltinButton_clicked(self): |
|
1292 """ |
|
1293 Private slot to delete the selected items from the list. |
|
1294 """ |
|
1295 for itm in self.builtinsAssignmentList.selectedItems(): |
|
1296 index = self.builtinsAssignmentList.indexOfTopLevelItem(itm) |
|
1297 self.builtinsAssignmentList.takeTopLevelItem(index) |
|
1298 del itm |