28 |
28 |
29 class BlackConfigurationDialog(QDialog, Ui_BlackConfigurationDialog): |
29 class BlackConfigurationDialog(QDialog, Ui_BlackConfigurationDialog): |
30 """ |
30 """ |
31 Class implementing a dialog to enter the parameters for a Black formatting run. |
31 Class implementing a dialog to enter the parameters for a Black formatting run. |
32 """ |
32 """ |
|
33 |
33 def __init__(self, withProject=True, parent=None): |
34 def __init__(self, withProject=True, parent=None): |
34 """ |
35 """ |
35 Constructor |
36 Constructor |
36 |
37 |
37 @param withProject flag indicating to look for project configurations |
38 @param withProject flag indicating to look for project configurations |
38 (defaults to True) |
39 (defaults to True) |
39 @type bool |
40 @type bool |
40 @param parent reference to the parent widget (defaults to None) |
41 @param parent reference to the parent widget (defaults to None) |
41 @type QWidget (optional) |
42 @type QWidget (optional) |
42 """ |
43 """ |
43 super().__init__(parent) |
44 super().__init__(parent) |
44 self.setupUi(self) |
45 self.setupUi(self) |
45 |
46 |
46 self.__project = ericApp().getObject("Project") if withProject else None |
47 self.__project = ericApp().getObject("Project") if withProject else None |
47 |
48 |
48 indentTabWidth = ( |
49 indentTabWidth = ( |
49 QFontMetricsF(self.excludeEdit.font()).horizontalAdvance(" ") * 2 |
50 QFontMetricsF(self.excludeEdit.font()).horizontalAdvance(" ") * 2 |
50 ) |
51 ) |
51 self.excludeEdit.document().setIndentWidth(indentTabWidth) |
52 self.excludeEdit.document().setIndentWidth(indentTabWidth) |
52 self.excludeEdit.setTabStopDistance(indentTabWidth) |
53 self.excludeEdit.setTabStopDistance(indentTabWidth) |
53 |
54 |
54 self.__pyprojectData = {} |
55 self.__pyprojectData = {} |
55 self.__projectData = {} |
56 self.__projectData = {} |
56 |
57 |
57 self.__tomlButton = self.buttonBox.addButton( |
58 self.__tomlButton = self.buttonBox.addButton( |
58 self.tr("Generate TOML"), |
59 self.tr("Generate TOML"), QDialogButtonBox.ButtonRole.ActionRole |
59 QDialogButtonBox.ButtonRole.ActionRole |
60 ) |
60 ) |
61 self.__tomlButton.setToolTip( |
61 self.__tomlButton.setToolTip(self.tr( |
62 self.tr("Place a code snippet for 'pyproject.toml' into the clipboard.") |
62 "Place a code snippet for 'pyproject.toml' into the clipboard." |
63 ) |
63 )) |
|
64 self.__tomlButton.clicked.connect(self.__createTomlSnippet) |
64 self.__tomlButton.clicked.connect(self.__createTomlSnippet) |
65 |
65 |
66 # setup the source combobox |
66 # setup the source combobox |
67 self.sourceComboBox.addItem("", "") |
67 self.sourceComboBox.addItem("", "") |
68 if self.__project: |
68 if self.__project: |
69 pyprojectPath = pathlib.Path( |
69 pyprojectPath = ( |
70 self.__project.getProjectPath() |
70 pathlib.Path(self.__project.getProjectPath()) / "pyproject.toml" |
71 ) / "pyproject.toml" |
71 ) |
72 if pyprojectPath.exists(): |
72 if pyprojectPath.exists(): |
73 with contextlib.suppress(tomlkit.exceptions.ParseError, OSError): |
73 with contextlib.suppress(tomlkit.exceptions.ParseError, OSError): |
74 with pyprojectPath.open("r", encoding="utf-8") as f: |
74 with pyprojectPath.open("r", encoding="utf-8") as f: |
75 data = tomlkit.load(f) |
75 data = tomlkit.load(f) |
76 config = data.get("tool", {}).get("black", {}) |
76 config = data.get("tool", {}).get("black", {}) |
85 self.__project.getData("OTHERTOOLSPARMS", "Black") |
85 self.__project.getData("OTHERTOOLSPARMS", "Black") |
86 ) |
86 ) |
87 self.sourceComboBox.addItem(self.tr("Project File"), "project") |
87 self.sourceComboBox.addItem(self.tr("Project File"), "project") |
88 self.sourceComboBox.addItem(self.tr("Defaults"), "default") |
88 self.sourceComboBox.addItem(self.tr("Defaults"), "default") |
89 self.sourceComboBox.addItem(self.tr("Configuration Below"), "dialog") |
89 self.sourceComboBox.addItem(self.tr("Configuration Below"), "dialog") |
90 |
90 |
91 self.__populateTargetVersionsList() |
91 self.__populateTargetVersionsList() |
92 |
92 |
93 if self.__projectData: |
93 if self.__projectData: |
94 source = self.__projectData.get("source", "") |
94 source = self.__projectData.get("source", "") |
95 self.sourceComboBox.setCurrentIndex(self.sourceComboBox.findData(source)) |
95 self.sourceComboBox.setCurrentIndex(self.sourceComboBox.findData(source)) |
96 |
96 |
97 def __populateTargetVersionsList(self): |
97 def __populateTargetVersionsList(self): |
98 """ |
98 """ |
99 Private method to populate the target versions list widget with checkable |
99 Private method to populate the target versions list widget with checkable |
100 Python version entries. |
100 Python version entries. |
101 """ |
101 """ |
109 "Python {0}.{1}".format(target[0], target[1]), self.targetVersionsList |
109 "Python {0}.{1}".format(target[0], target[1]), self.targetVersionsList |
110 ) |
110 ) |
111 itm.setData(Qt.ItemDataRole.UserRole, target[2]) |
111 itm.setData(Qt.ItemDataRole.UserRole, target[2]) |
112 itm.setFlags(itm.flags() | Qt.ItemFlag.ItemIsUserCheckable) |
112 itm.setFlags(itm.flags() | Qt.ItemFlag.ItemIsUserCheckable) |
113 itm.setCheckState(Qt.CheckState.Unchecked) |
113 itm.setCheckState(Qt.CheckState.Unchecked) |
114 |
114 |
115 def __loadConfiguration(self, configurationDict): |
115 def __loadConfiguration(self, configurationDict): |
116 """ |
116 """ |
117 Private method to load the configuration section with data of the given |
117 Private method to load the configuration section with data of the given |
118 dictionary. |
118 dictionary. |
119 |
119 |
120 @param configurationDict reference to the data to be loaded |
120 @param configurationDict reference to the data to be loaded |
121 @type dict |
121 @type dict |
122 """ |
122 """ |
123 confDict = copy.deepcopy(BlackUtilities.getDefaultConfiguration()) |
123 confDict = copy.deepcopy(BlackUtilities.getDefaultConfiguration()) |
124 confDict.update(configurationDict) |
124 confDict.update(configurationDict) |
125 |
125 |
126 self.lineLengthSpinBox.setValue(int(confDict["line-length"])) |
126 self.lineLengthSpinBox.setValue(int(confDict["line-length"])) |
127 self.skipStringNormalCheckBox.setChecked(confDict["skip-string-normalization"]) |
127 self.skipStringNormalCheckBox.setChecked(confDict["skip-string-normalization"]) |
128 self.skipMagicCommaCheckBox.setChecked(confDict["skip-magic-trailing-comma"]) |
128 self.skipMagicCommaCheckBox.setChecked(confDict["skip-magic-trailing-comma"]) |
129 self.excludeEdit.setPlainText(confDict["extend-exclude"]) |
129 self.excludeEdit.setPlainText(confDict["extend-exclude"]) |
130 for row in range(self.targetVersionsList.count()): |
130 for row in range(self.targetVersionsList.count()): |
131 itm = self.targetVersionsList.item(row) |
131 itm = self.targetVersionsList.item(row) |
132 itm.setCheckState( |
132 itm.setCheckState( |
133 Qt.CheckState.Checked |
133 Qt.CheckState.Checked |
134 if itm.data(Qt.ItemDataRole.UserRole).lower() |
134 if itm.data(Qt.ItemDataRole.UserRole).lower() |
135 in confDict["target-version"] else |
135 in confDict["target-version"] |
136 Qt.CheckState.Unchecked |
136 else Qt.CheckState.Unchecked |
137 ) |
137 ) |
138 |
138 |
139 @pyqtSlot(str) |
139 @pyqtSlot(str) |
140 def on_sourceComboBox_currentTextChanged(self, selection): |
140 def on_sourceComboBox_currentTextChanged(self, selection): |
141 """ |
141 """ |
142 Private slot to handle the selection of a configuration source. |
142 Private slot to handle the selection of a configuration source. |
143 |
143 |
144 @param selection text of the currently selected item |
144 @param selection text of the currently selected item |
145 @type str |
145 @type str |
146 """ |
146 """ |
147 self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled( |
147 self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled( |
148 bool(selection) |
148 bool(selection) |
149 ) |
149 ) |
150 |
150 |
151 source = self.sourceComboBox.currentData() |
151 source = self.sourceComboBox.currentData() |
152 if source == "pyproject": |
152 if source == "pyproject": |
153 self.__loadConfiguration(self.__pyprojectData) |
153 self.__loadConfiguration(self.__pyprojectData) |
154 elif source == "project": |
154 elif source == "project": |
155 self.__loadConfiguration(self.__projectData) |
155 self.__loadConfiguration(self.__projectData) |
156 elif source == "default": |
156 elif source == "default": |
157 self.__loadConfiguration(BlackUtilities.getDefaultConfiguration()) |
157 self.__loadConfiguration(BlackUtilities.getDefaultConfiguration()) |
158 elif source == "dialog": |
158 elif source == "dialog": |
159 # just leave the current entries |
159 # just leave the current entries |
160 pass |
160 pass |
161 |
161 |
162 @pyqtSlot() |
162 @pyqtSlot() |
163 def on_excludeEdit_textChanged(self): |
163 def on_excludeEdit_textChanged(self): |
164 """ |
164 """ |
165 Private slot to enable the validate button depending on the exclude text. |
165 Private slot to enable the validate button depending on the exclude text. |
166 """ |
166 """ |
167 self.validateButton.setEnabled(bool(self.excludeEdit.toPlainText())) |
167 self.validateButton.setEnabled(bool(self.excludeEdit.toPlainText())) |
168 |
168 |
169 @pyqtSlot() |
169 @pyqtSlot() |
170 def on_validateButton_clicked(self): |
170 def on_validateButton_clicked(self): |
171 """ |
171 """ |
172 Private slot to validate the entered exclusion regular expression. |
172 Private slot to validate the entered exclusion regular expression. |
173 """ |
173 """ |
175 valid, error = BlackUtilities.validateRegExp(regexp) |
175 valid, error = BlackUtilities.validateRegExp(regexp) |
176 if valid: |
176 if valid: |
177 EricMessageBox.information( |
177 EricMessageBox.information( |
178 self, |
178 self, |
179 self.tr("Validation"), |
179 self.tr("Validation"), |
180 self.tr("""The exclusion expression is valid.""") |
180 self.tr("""The exclusion expression is valid."""), |
181 ) |
181 ) |
182 else: |
182 else: |
183 EricMessageBox.critical( |
183 EricMessageBox.critical(self, self.tr("Validation Error"), error) |
184 self, |
184 |
185 self.tr("Validation Error"), |
|
186 error |
|
187 ) |
|
188 |
|
189 def __getTargetList(self): |
185 def __getTargetList(self): |
190 """ |
186 """ |
191 Private method to get the list of checked target versions. |
187 Private method to get the list of checked target versions. |
192 |
188 |
193 @return list of target versions |
189 @return list of target versions |
194 @rtype list of str |
190 @rtype list of str |
195 """ |
191 """ |
196 targets = [] |
192 targets = [] |
197 for row in range(self.targetVersionsList.count()): |
193 for row in range(self.targetVersionsList.count()): |
198 itm = self.targetVersionsList.item(row) |
194 itm = self.targetVersionsList.item(row) |
199 if itm.checkState() == Qt.CheckState.Checked: |
195 if itm.checkState() == Qt.CheckState.Checked: |
200 targets.append(itm.data(Qt.ItemDataRole.UserRole).lower()) |
196 targets.append(itm.data(Qt.ItemDataRole.UserRole).lower()) |
201 |
197 |
202 return targets |
198 return targets |
203 |
199 |
204 @pyqtSlot() |
200 @pyqtSlot() |
205 def __createTomlSnippet(self): |
201 def __createTomlSnippet(self): |
206 """ |
202 """ |
207 Private slot to generate a TOML snippet of the current configuration. |
203 Private slot to generate a TOML snippet of the current configuration. |
208 |
204 |
209 Note: Only non-default values are included in this snippet. |
205 Note: Only non-default values are included in this snippet. |
210 |
206 |
211 The code snippet is copied to the clipboard and may be placed inside the |
207 The code snippet is copied to the clipboard and may be placed inside the |
212 'pyproject.toml' file. |
208 'pyproject.toml' file. |
213 """ |
209 """ |
214 doc = tomlkit.document() |
210 doc = tomlkit.document() |
215 |
211 |
216 black = tomlkit.table() |
212 black = tomlkit.table() |
217 targetList = self.__getTargetList() |
213 targetList = self.__getTargetList() |
218 if targetList: |
214 if targetList: |
219 black["target-version"] = targetList |
215 black["target-version"] = targetList |
220 black["line-length"] = self.lineLengthSpinBox.value() |
216 black["line-length"] = self.lineLengthSpinBox.value() |
221 if self.skipStringNormalCheckBox.isChecked(): |
217 if self.skipStringNormalCheckBox.isChecked(): |
222 black["skip-string-normalization"] = True |
218 black["skip-string-normalization"] = True |
223 if self.skipMagicCommaCheckBox.isChecked(): |
219 if self.skipMagicCommaCheckBox.isChecked(): |
224 black["skip-magic-trailing-comma"] = True |
220 black["skip-magic-trailing-comma"] = True |
225 |
221 |
226 excludeRegexp = self.excludeEdit.toPlainText() |
222 excludeRegexp = self.excludeEdit.toPlainText() |
227 if excludeRegexp and BlackUtilities.validateRegExp(excludeRegexp)[0]: |
223 if excludeRegexp and BlackUtilities.validateRegExp(excludeRegexp)[0]: |
228 black["extend-exclude"] = tomlkit.string( |
224 black["extend-exclude"] = tomlkit.string( |
229 "\n{0}\n".format(excludeRegexp.strip()), |
225 "\n{0}\n".format(excludeRegexp.strip()), literal=True, multiline=True |
230 literal=True, |
226 ) |
231 multiline=True |
227 |
232 ) |
|
233 |
|
234 doc["tool"] = tomlkit.table(is_super_table=True) |
228 doc["tool"] = tomlkit.table(is_super_table=True) |
235 doc["tool"]["black"] = black |
229 doc["tool"]["black"] = black |
236 |
230 |
237 QGuiApplication.clipboard().setText(tomlkit.dumps(doc)) |
231 QGuiApplication.clipboard().setText(tomlkit.dumps(doc)) |
238 |
232 |
239 EricMessageBox.information( |
233 EricMessageBox.information( |
240 self, |
234 self, |
241 self.tr("Create TOML snippet"), |
235 self.tr("Create TOML snippet"), |
242 self.tr("""The 'pyproject.toml' snippet was copied to the clipboard""" |
236 self.tr( |
243 """ successfully.""") |
237 """The 'pyproject.toml' snippet was copied to the clipboard""" |
244 ) |
238 """ successfully.""" |
245 |
239 ), |
|
240 ) |
|
241 |
246 def getConfiguration(self): |
242 def getConfiguration(self): |
247 """ |
243 """ |
248 Public method to get the current configuration parameters. |
244 Public method to get the current configuration parameters. |
249 |
245 |
250 @return dictionary containing the configuration parameters |
246 @return dictionary containing the configuration parameters |
251 @rtype dict |
247 @rtype dict |
252 """ |
248 """ |
253 configuration = BlackUtilities.getDefaultConfiguration() |
249 configuration = BlackUtilities.getDefaultConfiguration() |
254 |
250 |
255 configuration["source"] = self.sourceComboBox.currentData() |
251 configuration["source"] = self.sourceComboBox.currentData() |
256 configuration["target-version"] = self.__getTargetList() |
252 configuration["target-version"] = self.__getTargetList() |
257 configuration["line-length"] = self.lineLengthSpinBox.value() |
253 configuration["line-length"] = self.lineLengthSpinBox.value() |
258 configuration["skip-string-normalization"] = ( |
254 configuration[ |
259 self.skipStringNormalCheckBox.isChecked() |
255 "skip-string-normalization" |
260 ) |
256 ] = self.skipStringNormalCheckBox.isChecked() |
261 configuration["skip-magic-trailing-comma"] = ( |
257 configuration[ |
262 self.skipMagicCommaCheckBox.isChecked() |
258 "skip-magic-trailing-comma" |
263 ) |
259 ] = self.skipMagicCommaCheckBox.isChecked() |
264 configuration["extend-exclude"] = self.excludeEdit.toPlainText().strip() |
260 configuration["extend-exclude"] = self.excludeEdit.toPlainText().strip() |
265 |
261 |
266 if self.__project: |
262 if self.__project: |
267 self.__project.setData("OTHERTOOLSPARMS", "Black", configuration) |
263 self.__project.setData("OTHERTOOLSPARMS", "Black", configuration) |
268 |
264 |
269 return configuration |
265 return configuration |