|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2013 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 # pylint: disable=C0103 |
|
6 |
|
7 """ |
|
8 Module implementing an interface to add different languages to do a syntax |
|
9 check. |
|
10 """ |
|
11 |
|
12 from __future__ import unicode_literals |
|
13 |
|
14 from PyQt5.QtCore import QObject, pyqtSignal |
|
15 |
|
16 from E5Gui.E5Application import e5App |
|
17 from Utilities import determinePythonVersion |
|
18 |
|
19 |
|
20 class SyntaxCheckService(QObject): |
|
21 """ |
|
22 Implement the syntax check service. |
|
23 |
|
24 Plugins can add other languages to the syntax check by calling addLanguage |
|
25 and support of an extra checker module on the client side which has to |
|
26 connect directly to the background service. |
|
27 |
|
28 @signal syntaxChecked(str, dict) emitted when the syntax check was done for |
|
29 one file |
|
30 @signal batchFinished() emitted when a syntax check batch is done |
|
31 @signal error(str, str) emitted in case of an error |
|
32 """ |
|
33 syntaxChecked = pyqtSignal(str, dict) |
|
34 batchFinished = pyqtSignal() |
|
35 error = pyqtSignal(str, str) |
|
36 |
|
37 def __init__(self): |
|
38 """ |
|
39 Constructor |
|
40 """ |
|
41 super(SyntaxCheckService, self).__init__() |
|
42 self.backgroundService = e5App().getObject("BackgroundService") |
|
43 self.__supportedLanguages = {} |
|
44 |
|
45 self.queuedBatches = [] |
|
46 self.batchesFinished = True |
|
47 |
|
48 def __determineLanguage(self, filename, source): |
|
49 """ |
|
50 Private methode to determine the language of the file. |
|
51 |
|
52 @param filename of the sourcefile (str) |
|
53 @param source code of the file (str) |
|
54 @return language of the file or None if not found (str or None) |
|
55 """ |
|
56 pyVer = determinePythonVersion(filename, source) |
|
57 if pyVer: |
|
58 return 'Python{0}'.format(pyVer) |
|
59 |
|
60 for lang, (_env, _getArgs, getExt) in \ |
|
61 self.__supportedLanguages.items(): |
|
62 if filename.endswith(tuple(getExt())): |
|
63 return lang |
|
64 |
|
65 return None |
|
66 |
|
67 def addLanguage( |
|
68 self, lang, env, path, module, getArgs, getExt, callback, onError): |
|
69 """ |
|
70 Public method to register a new language to the supported languages. |
|
71 |
|
72 @param lang new language to check syntax (str) |
|
73 @param env the environment in which the checker is implemented (str) |
|
74 @param path full path to the module (str) |
|
75 @param module name to import (str) |
|
76 @param getArgs function to collect the required arguments to call the |
|
77 syntax checker on client side (function) |
|
78 @param getExt function that returns the supported file extensions of |
|
79 the syntax checker (function) |
|
80 @param callback function on service response (function) |
|
81 @param onError callback function if client or service isn't available |
|
82 (function) |
|
83 """ |
|
84 self.__supportedLanguages[lang] = env, getArgs, getExt |
|
85 # Connect to the background service |
|
86 self.backgroundService.serviceConnect( |
|
87 '{0}Syntax'.format(lang), env, path, module, callback, onError, |
|
88 onBatchDone=self.batchJobDone) |
|
89 |
|
90 def getLanguages(self): |
|
91 """ |
|
92 Public method to return the supported language names. |
|
93 |
|
94 @return list of languanges supported (list of str) |
|
95 """ |
|
96 return list(self.__supportedLanguages.keys()) |
|
97 |
|
98 def removeLanguage(self, lang): |
|
99 """ |
|
100 Public method to remove the language from syntax check. |
|
101 |
|
102 @param lang language to remove (str) |
|
103 """ |
|
104 self.__supportedLanguages.pop(lang, None) |
|
105 self.backgroundService.serviceDisconnect( |
|
106 '{0}Syntax'.format(lang), lang) |
|
107 |
|
108 def getExtensions(self): |
|
109 """ |
|
110 Public method to return all supported file extensions for the |
|
111 syntax checker dialog. |
|
112 |
|
113 @return set of all supported file extensions (set of str) |
|
114 """ |
|
115 extensions = set() |
|
116 for _env, _getArgs, getExt in self.__supportedLanguages.values(): |
|
117 for ext in getExt(): |
|
118 extensions.add(ext) |
|
119 return extensions |
|
120 |
|
121 def syntaxCheck(self, lang, filename, source): |
|
122 """ |
|
123 Public method to prepare a syntax check of one source file. |
|
124 |
|
125 @param lang language of the file or None to determine by internal |
|
126 algorithm (str or None) |
|
127 @param filename source filename (string) |
|
128 @param source string containing the code to check (string) |
|
129 """ |
|
130 if not lang: |
|
131 lang = self.__determineLanguage(filename, source) |
|
132 if lang not in self.getLanguages(): |
|
133 return |
|
134 data = [source] |
|
135 # Call the getArgs function to get the required arguments |
|
136 env, args, getExt = self.__supportedLanguages[lang] |
|
137 data.extend(args()) |
|
138 self.backgroundService.enqueueRequest( |
|
139 '{0}Syntax'.format(lang), env, filename, data) |
|
140 |
|
141 def syntaxBatchCheck(self, argumentsList): |
|
142 """ |
|
143 Public method to prepare a syntax check on multiple source files. |
|
144 |
|
145 @param argumentsList list of arguments tuples with each tuple |
|
146 containing filename and source (string, string) |
|
147 """ |
|
148 data = { |
|
149 } |
|
150 for lang in self.getLanguages(): |
|
151 data[lang] = [] |
|
152 |
|
153 for filename, source in argumentsList: |
|
154 lang = self.__determineLanguage(filename, source) |
|
155 if lang not in self.getLanguages(): |
|
156 continue |
|
157 else: |
|
158 jobData = [source] |
|
159 # Call the getArgs function to get the required arguments |
|
160 args = self.__supportedLanguages[lang][1] |
|
161 jobData.extend(args()) |
|
162 data[lang].append((filename, jobData)) |
|
163 |
|
164 self.queuedBatches = [] |
|
165 for lang in self.getLanguages(): |
|
166 if data[lang]: |
|
167 self.queuedBatches.append(lang) |
|
168 env = self.__supportedLanguages[lang][0] |
|
169 self.backgroundService.enqueueRequest( |
|
170 'batch_{0}Syntax'.format(lang), env, "", data[lang]) |
|
171 self.batchesFinished = False |
|
172 |
|
173 def cancelSyntaxBatchCheck(self): |
|
174 """ |
|
175 Public method to cancel all batch jobs. |
|
176 """ |
|
177 for lang in self.getLanguages(): |
|
178 env = self.__supportedLanguages[lang][0] |
|
179 self.backgroundService.requestCancel( |
|
180 'batch_{0}Syntax'.format(lang), env) |
|
181 |
|
182 def __serviceError(self, fn, msg): |
|
183 """ |
|
184 Private slot handling service errors. |
|
185 |
|
186 @param fn file name (string) |
|
187 @param msg message text (string) |
|
188 """ |
|
189 self.error.emit(fn, msg) |
|
190 |
|
191 def serviceErrorPy2(self, fx, lang, fn, msg): |
|
192 """ |
|
193 Public method handling service errors for Python 2. |
|
194 |
|
195 @param fx service name (string) |
|
196 @param lang language (string) |
|
197 @param fn file name (string) |
|
198 @param msg message text (string) |
|
199 """ |
|
200 if fx in ['Python2Syntax', 'batch_Python2Syntax']: |
|
201 if fx == 'Python2Syntax': |
|
202 self.__serviceError(fn, msg) |
|
203 else: |
|
204 self.__serviceError(self.tr("Python 2 batch check"), msg) |
|
205 self.batchJobDone(fx, lang) |
|
206 |
|
207 def serviceErrorPy3(self, fx, lang, fn, msg): |
|
208 """ |
|
209 Public method handling service errors for Python 2. |
|
210 |
|
211 @param fx service name (string) |
|
212 @param lang language (string) |
|
213 @param fn file name (string) |
|
214 @param msg message text (string) |
|
215 """ |
|
216 if fx in ['Python3Syntax', 'batch_Python3Syntax']: |
|
217 if fx == 'Python3Syntax': |
|
218 self.__serviceError(fn, msg) |
|
219 else: |
|
220 self.__serviceError(self.tr("Python 3 batch check"), msg) |
|
221 self.batchJobDone(fx, lang) |
|
222 |
|
223 def serviceErrorJavaScript(self, fx, lang, fn, msg): |
|
224 """ |
|
225 Public method handling service errors for JavaScript. |
|
226 |
|
227 @param fx service name (string) |
|
228 @param lang language (string) |
|
229 @param fn file name (string) |
|
230 @param msg message text (string) |
|
231 """ |
|
232 if fx in ['JavaScriptSyntax', 'batch_JavaScriptSyntax']: |
|
233 if fx == 'JavaScriptSyntax': |
|
234 self.__serviceError(fn, msg) |
|
235 else: |
|
236 self.__serviceError(self.tr("JavaScript batch check"), msg) |
|
237 self.batchJobDone(fx, lang) |
|
238 |
|
239 def batchJobDone(self, fx, lang): |
|
240 """ |
|
241 Public slot handling the completion of a batch job. |
|
242 |
|
243 @param fx service name (string) |
|
244 @param lang language (string) |
|
245 """ |
|
246 if fx in ['Python2Syntax', 'batch_Python2Syntax', |
|
247 'Python3Syntax', 'batch_Python3Syntax', |
|
248 'JavaScriptSyntax', 'batch_JavaScriptSyntax']: |
|
249 if lang in self.queuedBatches: |
|
250 self.queuedBatches.remove(lang) |
|
251 # prevent sending the signal multiple times |
|
252 if len(self.queuedBatches) == 0 and not self.batchesFinished: |
|
253 self.batchFinished.emit() |
|
254 self.batchesFinished = True |