eric7/Plugins/PluginSyntaxChecker.py

branch
eric7
changeset 8312
800c432b34c8
parent 8240
93b8a353c4bf
child 8314
e3642a6a1e71
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2007 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the Syntax Checker plugin.
8 """
9
10 import os
11 import contextlib
12
13 from PyQt5.QtCore import QObject
14
15 from E5Gui.E5Action import E5Action
16 from E5Gui.E5Application import e5App
17 from eric6config import getConfig
18
19 from Project.ProjectBrowserModel import ProjectBrowserFileItem
20
21 import Preferences
22 import UI.Info
23
24 # Start-Of-Header
25 name = "Syntax Checker Plugin"
26 author = "Detlev Offenbach <detlev@die-offenbachs.de>"
27 autoactivate = True
28 deactivateable = True
29 version = UI.Info.VersionOnly
30 className = "SyntaxCheckerPlugin"
31 packageName = "__core__"
32 shortDescription = "Show the Syntax Checker dialog."
33 longDescription = (
34 """This plugin implements the Syntax Checker dialog."""
35 """ Syntax Checker is used to check Python source files for correct"""
36 """ syntax."""
37 )
38 pyqtApi = 2
39 # End-Of-Header
40
41 error = ""
42
43
44 class SyntaxCheckerPlugin(QObject):
45 """
46 Class implementing the Syntax Checker plugin.
47 """
48 def __init__(self, ui):
49 """
50 Constructor
51
52 @param ui reference to the user interface object (UI.UserInterface)
53 """
54 super().__init__(ui)
55 self.__ui = ui
56 self.__initialize()
57
58 from Plugins.CheckerPlugins.SyntaxChecker.SyntaxCheckService import (
59 SyntaxCheckService
60 )
61 self.syntaxCheckService = SyntaxCheckService()
62 e5App().registerObject("SyntaxCheckService", self.syntaxCheckService)
63
64 ericPath = getConfig('ericDir')
65 path = os.path.join(ericPath, 'Plugins', 'CheckerPlugins',
66 'SyntaxChecker')
67
68 self.syntaxCheckService.addLanguage(
69 'Python3', 'Python3', path, 'SyntaxCheck',
70 self.__getPythonOptions,
71 lambda: Preferences.getPython("Python3Extensions"),
72 self.__translateSyntaxCheck,
73 self.syntaxCheckService.serviceErrorPy3)
74
75 self.syntaxCheckService.addLanguage(
76 'JavaScript', 'Python3', path,
77 'jsCheckSyntax',
78 lambda: [], # No options
79 lambda: ['.js'],
80 lambda fn, problems:
81 self.syntaxCheckService.syntaxChecked.emit(fn, problems),
82 self.syntaxCheckService.serviceErrorJavaScript)
83
84 # YAML syntax check via Python3
85 self.syntaxCheckService.addLanguage(
86 'YAML', 'Python3', path,
87 'yamlCheckSyntax',
88 lambda: [], # No options
89 lambda: ['.yml', '.yaml'],
90 lambda fn, problems:
91 self.syntaxCheckService.syntaxChecked.emit(fn, problems),
92 self.syntaxCheckService.serviceErrorYAML)
93
94 # JSON syntax check via Python3
95 self.syntaxCheckService.addLanguage(
96 'JSON', 'Python3', path,
97 'jsonCheckSyntax',
98 lambda: [], # No options
99 lambda: ['.json'],
100 lambda fn, problems:
101 self.syntaxCheckService.syntaxChecked.emit(fn, problems),
102 self.syntaxCheckService.serviceErrorJSON)
103
104 # TOML syntax check via Python3
105 self.syntaxCheckService.addLanguage(
106 'TOML', 'Python3', path,
107 'tomlCheckSyntax',
108 lambda: [], # No options
109 lambda: ['.toml'],
110 lambda fn, problems:
111 self.syntaxCheckService.syntaxChecked.emit(fn, problems),
112 self.syntaxCheckService.serviceErrorTOML)
113
114 def __initialize(self):
115 """
116 Private slot to (re)initialize the plugin.
117 """
118 self.__projectAct = None
119 self.__projectSyntaxCheckerDialog = None
120
121 self.__projectBrowserAct = None
122 self.__projectBrowserMenu = None
123 self.__projectBrowserSyntaxCheckerDialog = None
124
125 self.__editors = []
126 self.__editorAct = None
127 self.__editorSyntaxCheckerDialog = None
128
129 def __getPythonOptions(self):
130 """
131 Private methode to determine the syntax check options.
132
133 @return state of checkFlakes and ignoreStarImportWarnings (bool, bool)
134 """
135 checkFlakes = Preferences.getFlakes("IncludeInSyntaxCheck")
136 ignoreStarImportWarnings = Preferences.getFlakes(
137 "IgnoreStarImportWarnings")
138 return checkFlakes, ignoreStarImportWarnings
139
140 def __translateSyntaxCheck(self, fn, problems):
141 """
142 Private slot to translate the resulting messages.
143
144 If checkFlakes is True, warnings contains a list of strings containing
145 the warnings (marker, file name, line number, message)
146 The values are only valid, if nok is False.
147
148 @param fn filename of the checked file (str)
149 @param problems dictionary with the keys 'error' and 'warnings' which
150 hold a list containing details about the error/ warnings
151 (file name, line number, column, codestring (only at syntax
152 errors), the message, a list with arguments for the message)
153 """
154 from CheckerPlugins.SyntaxChecker.pyflakes.translations import (
155 getTranslatedFlakesMessage
156 )
157 warnings = problems.get('warnings', [])
158 for warning in warnings:
159 # Translate messages
160 msg_args = warning.pop()
161 warning[4] = getTranslatedFlakesMessage(warning[4], msg_args)
162
163 problems['warnings'] = warnings
164 self.syntaxCheckService.syntaxChecked.emit(fn, problems)
165
166 def activate(self):
167 """
168 Public method to activate this plugin.
169
170 @return tuple of None and activation status (boolean)
171 """
172 menu = e5App().getObject("Project").getMenu("Checks")
173 if menu:
174 self.__projectAct = E5Action(
175 self.tr('Check Syntax'),
176 self.tr('&Syntax...'), 0, 0,
177 self, 'project_check_syntax')
178 self.__projectAct.setStatusTip(
179 self.tr('Check syntax.'))
180 self.__projectAct.setWhatsThis(self.tr(
181 """<b>Check Syntax...</b>"""
182 """<p>This checks Python files for syntax errors.</p>"""
183 ))
184 self.__projectAct.triggered.connect(self.__projectSyntaxCheck)
185 e5App().getObject("Project").addE5Actions([self.__projectAct])
186 menu.addAction(self.__projectAct)
187
188 self.__editorAct = E5Action(
189 self.tr('Check Syntax'),
190 self.tr('&Syntax...'), 0, 0,
191 self, "")
192 self.__editorAct.setWhatsThis(self.tr(
193 """<b>Check Syntax...</b>"""
194 """<p>This checks Python files for syntax errors.</p>"""
195 ))
196 self.__editorAct.triggered.connect(self.__editorSyntaxCheck)
197
198 e5App().getObject("Project").showMenu.connect(self.__projectShowMenu)
199 e5App().getObject("ProjectBrowser").getProjectBrowser(
200 "sources").showMenu.connect(self.__projectBrowserShowMenu)
201 e5App().getObject("ViewManager").editorOpenedEd.connect(
202 self.__editorOpened)
203 e5App().getObject("ViewManager").editorClosedEd.connect(
204 self.__editorClosed)
205
206 for editor in e5App().getObject("ViewManager").getOpenEditors():
207 self.__editorOpened(editor)
208
209 return None, True
210
211 def deactivate(self):
212 """
213 Public method to deactivate this plugin.
214 """
215 e5App().getObject("Project").showMenu.disconnect(
216 self.__projectShowMenu)
217 e5App().getObject("ProjectBrowser").getProjectBrowser(
218 "sources").showMenu.disconnect(self.__projectBrowserShowMenu)
219 e5App().getObject("ViewManager").editorOpenedEd.disconnect(
220 self.__editorOpened)
221 e5App().getObject("ViewManager").editorClosedEd.disconnect(
222 self.__editorClosed)
223
224 menu = e5App().getObject("Project").getMenu("Checks")
225 if menu:
226 menu.removeAction(self.__projectAct)
227
228 if self.__projectBrowserMenu and self.__projectBrowserAct:
229 self.__projectBrowserMenu.removeAction(
230 self.__projectBrowserAct)
231
232 for editor in self.__editors:
233 editor.showMenu.disconnect(self.__editorShowMenu)
234 menu = editor.getMenu("Checks")
235 if menu is not None:
236 menu.removeAction(self.__editorAct)
237
238 self.__initialize()
239
240 def __projectShowMenu(self, menuName, menu):
241 """
242 Private slot called, when the the project menu or a submenu is
243 about to be shown.
244
245 @param menuName name of the menu to be shown (string)
246 @param menu reference to the menu (QMenu)
247 """
248 if menuName == "Checks" and self.__projectAct is not None:
249 self.__projectAct.setEnabled(
250 e5App().getObject("Project").getProjectLanguage() in
251 self.syntaxCheckService.getLanguages())
252
253 def __projectBrowserShowMenu(self, menuName, menu):
254 """
255 Private slot called, when the the project browser menu or a submenu is
256 about to be shown.
257
258 @param menuName name of the menu to be shown (string)
259 @param menu reference to the menu (QMenu)
260 """
261 if (
262 menuName == "Checks" and
263 e5App().getObject("Project").getProjectLanguage() in
264 self.syntaxCheckService.getLanguages()
265 ):
266 self.__projectBrowserMenu = menu
267 if self.__projectBrowserAct is None:
268 self.__projectBrowserAct = E5Action(
269 self.tr('Check Syntax'),
270 self.tr('&Syntax...'), 0, 0,
271 self, "")
272 self.__projectBrowserAct.setWhatsThis(self.tr(
273 """<b>Check Syntax...</b>"""
274 """<p>This checks Python files for syntax errors.</p>"""
275 ))
276 self.__projectBrowserAct.triggered.connect(
277 self.__projectBrowserSyntaxCheck)
278 if self.__projectBrowserAct not in menu.actions():
279 menu.addAction(self.__projectBrowserAct)
280
281 def __projectSyntaxCheck(self):
282 """
283 Private slot used to check the project files for syntax errors.
284 """
285 project = e5App().getObject("Project")
286 project.saveAllScripts()
287 ppath = project.getProjectPath()
288 extensions = tuple(self.syntaxCheckService.getExtensions())
289 files = [os.path.join(ppath, file)
290 for file in project.pdata["SOURCES"]
291 if file.endswith(extensions)]
292
293 from CheckerPlugins.SyntaxChecker.SyntaxCheckerDialog import (
294 SyntaxCheckerDialog
295 )
296 self.__projectSyntaxCheckerDialog = SyntaxCheckerDialog()
297 self.__projectSyntaxCheckerDialog.show()
298 self.__projectSyntaxCheckerDialog.prepare(files, project)
299
300 def __projectBrowserSyntaxCheck(self):
301 """
302 Private method to handle the syntax check context menu action of the
303 project sources browser.
304 """
305 browser = e5App().getObject("ProjectBrowser").getProjectBrowser(
306 "sources")
307 if browser.getSelectedItemsCount([ProjectBrowserFileItem]) > 1:
308 fn = []
309 for itm in browser.getSelectedItems([ProjectBrowserFileItem]):
310 fn.append(itm.fileName())
311 else:
312 itm = browser.model().item(browser.currentIndex())
313 try:
314 fn = itm.fileName()
315 except AttributeError:
316 fn = itm.dirName()
317
318 from CheckerPlugins.SyntaxChecker.SyntaxCheckerDialog import (
319 SyntaxCheckerDialog
320 )
321 self.__projectBrowserSyntaxCheckerDialog = SyntaxCheckerDialog()
322 self.__projectBrowserSyntaxCheckerDialog.show()
323 self.__projectBrowserSyntaxCheckerDialog.start(fn)
324
325 def __editorOpened(self, editor):
326 """
327 Private slot called, when a new editor was opened.
328
329 @param editor reference to the new editor (QScintilla.Editor)
330 """
331 menu = editor.getMenu("Checks")
332 if menu is not None:
333 menu.addAction(self.__editorAct)
334 editor.showMenu.connect(self.__editorShowMenu)
335 self.__editors.append(editor)
336
337 def __editorClosed(self, editor):
338 """
339 Private slot called, when an editor was closed.
340
341 @param editor reference to the editor (QScintilla.Editor)
342 """
343 with contextlib.suppress(ValueError):
344 self.__editors.remove(editor)
345
346 def __editorShowMenu(self, menuName, menu, editor):
347 """
348 Private slot called, when the the editor context menu or a submenu is
349 about to be shown.
350
351 @param menuName name of the menu to be shown (string)
352 @param menu reference to the menu (QMenu)
353 @param editor reference to the editor
354 """
355 if menuName == "Checks":
356 if self.__editorAct not in menu.actions():
357 menu.addAction(self.__editorAct)
358 self.__editorAct.setEnabled(
359 editor.getLanguage() in self.syntaxCheckService.getLanguages())
360
361 def __editorSyntaxCheck(self):
362 """
363 Private slot to handle the syntax check context menu action of the
364 editors.
365 """
366 editor = e5App().getObject("ViewManager").activeWindow()
367 if editor is not None:
368 from CheckerPlugins.SyntaxChecker.SyntaxCheckerDialog import (
369 SyntaxCheckerDialog
370 )
371 self.__editorSyntaxCheckerDialog = SyntaxCheckerDialog()
372 self.__editorSyntaxCheckerDialog.show()
373 if editor.isJavascriptFile():
374 unnamed = "Unnamed.js"
375 else:
376 unnamed = "Unnamed.py"
377 self.__editorSyntaxCheckerDialog.start(
378 editor.getFileName() or unnamed, editor.text())

eric ide

mercurial