|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2015 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the autocompletion interface to jedi. |
|
8 """ |
|
9 |
|
10 import contextlib |
|
11 import os |
|
12 import uuid |
|
13 |
|
14 from PyQt6.QtCore import pyqtSlot, QCoreApplication, QTimer |
|
15 |
|
16 from EricWidgets.EricApplication import ericApp |
|
17 |
|
18 from EricNetwork.EricJsonServer import EricJsonServer |
|
19 |
|
20 from QScintilla.Editor import Editor |
|
21 |
|
22 import Preferences |
|
23 import Globals |
|
24 |
|
25 |
|
26 class JediServer(EricJsonServer): |
|
27 """ |
|
28 Class implementing the interface to the jedi library. |
|
29 """ |
|
30 IdProject = "Project" |
|
31 |
|
32 PictureIDs = { |
|
33 "class": "?{0}".format(Editor.ClassID), |
|
34 "_class": "?{0}".format(Editor.ClassProtectedID), |
|
35 "__class": "?{0}".format(Editor.ClassPrivateID), |
|
36 "instance": "?{0}".format(Editor.ClassID), |
|
37 "_instance": "?{0}".format(Editor.ClassProtectedID), |
|
38 "__instance": "?{0}".format(Editor.ClassPrivateID), |
|
39 "function": "?{0}".format(Editor.MethodID), |
|
40 "_function": "?{0}".format(Editor.MethodProtectedID), |
|
41 "__function": "?{0}".format(Editor.MethodPrivateID), |
|
42 "module": "?{0}".format(Editor.ModuleID), |
|
43 "_module": "?{0}".format(Editor.ModuleID), |
|
44 "__module": "?{0}".format(Editor.ModuleID), |
|
45 "param": "?{0}".format(Editor.AttributeID), |
|
46 "_param": "?{0}".format(Editor.AttributeProtectedID), |
|
47 "__param": "?{0}".format(Editor.AttributePrivateID), |
|
48 "statement": "?{0}".format(Editor.AttributeID), |
|
49 "_statement": "?{0}".format(Editor.AttributeProtectedID), |
|
50 "__statement": "?{0}".format(Editor.AttributePrivateID), |
|
51 "import": "", |
|
52 "None": "", |
|
53 } |
|
54 |
|
55 def __init__(self, viewManager, project, ui): |
|
56 """ |
|
57 Constructor |
|
58 |
|
59 @param viewManager reference to the viewmanager object |
|
60 @type ViewManager |
|
61 @param project reference to the project object |
|
62 @type Project |
|
63 @param ui reference to the user interface |
|
64 @type UserInterface |
|
65 """ |
|
66 super().__init__( |
|
67 "JediServer", multiplex=True, parent=ui) |
|
68 |
|
69 self.__ui = ui |
|
70 self.__vm = viewManager |
|
71 self.__ericProject = project |
|
72 |
|
73 self.__editorLanguageMapping = {} |
|
74 |
|
75 self.__documentationViewer = None |
|
76 |
|
77 # attributes to store the resuls of the client side |
|
78 self.__completions = None |
|
79 self.__calltips = None |
|
80 |
|
81 self.__methodMapping = { |
|
82 "CompletionsResult": self.__processCompletionsResult, |
|
83 "CallTipsResult": self.__processCallTipsResult, |
|
84 "DocumentationResult": self.__processDocumentationResult, |
|
85 "HoverHelpResult": self.__processHoverHelpResult, |
|
86 "GotoDefinitionResult": self.__processGotoDefinitionResult, |
|
87 "GotoReferencesResult": self.__processGotoReferencesResult, |
|
88 |
|
89 "ClientException": self.__processClientException, |
|
90 } |
|
91 |
|
92 # temporary store for editor references indexed by Uuid |
|
93 self.__editors = {} |
|
94 |
|
95 # Python 3 |
|
96 self.__ensureActive("Python3") |
|
97 |
|
98 def __updateEditorLanguageMapping(self): |
|
99 """ |
|
100 Private method to update the editor language to connection mapping. |
|
101 """ |
|
102 self.__editorLanguageMapping = {} |
|
103 for name in self.connectionNames(): |
|
104 if name == "Python3": |
|
105 self.__editorLanguageMapping.update({ |
|
106 "Python3": "Python3", |
|
107 "MicroPython": "Python3", |
|
108 "Pygments|Python": "Python3", |
|
109 "Pygments|Python 2.x": "Python3", |
|
110 "Cython": "Python3", |
|
111 }) |
|
112 |
|
113 def isSupportedLanguage(self, language): |
|
114 """ |
|
115 Public method to check, if the given language is supported. |
|
116 |
|
117 @param language editor programming language to check |
|
118 @type str |
|
119 @return flag indicating the support status |
|
120 @rtype bool |
|
121 """ |
|
122 return language in self.__editorLanguageMapping |
|
123 |
|
124 def __idString(self, editor): |
|
125 """ |
|
126 Private method to determine the ID string for the back-end. |
|
127 |
|
128 @param editor reference to the editor to determine the ID string for |
|
129 @type Editor |
|
130 @return ID string |
|
131 @rtype str |
|
132 """ |
|
133 idString = "" |
|
134 |
|
135 language = editor.getLanguage() |
|
136 if ( |
|
137 self.__ericProject.isOpen() and |
|
138 self.__ericProject.getProjectLanguage() == language |
|
139 ): |
|
140 filename = editor.getFileName() |
|
141 if self.__ericProject.isProjectSource(filename): |
|
142 idString = JediServer.IdProject |
|
143 |
|
144 if not idString and language in self.__editorLanguageMapping: |
|
145 idString = self.__editorLanguageMapping[language] |
|
146 |
|
147 return idString |
|
148 |
|
149 def __prepareData(self, editor): |
|
150 """ |
|
151 Private method to gather data about current cursor position. |
|
152 |
|
153 @param editor reference to the editor object, that called this method |
|
154 @type Editor |
|
155 @return tuple of filename, line, index, source |
|
156 @rtype tuple (str, int, int, str) |
|
157 """ |
|
158 filename = editor.getFileName() |
|
159 line, index = editor.getCursorPosition() |
|
160 line += 1 # jedi line numbers are 1 based |
|
161 source = editor.text() |
|
162 return filename, line, index, source |
|
163 |
|
164 def requestCompletions(self, editor, context, acText): |
|
165 """ |
|
166 Public method to request a list of possible completions. |
|
167 |
|
168 @param editor reference to the editor object, that called this method |
|
169 @type Editor |
|
170 @param context flag indicating to autocomplete a context |
|
171 @type bool |
|
172 @param acText text to be completed |
|
173 @type str |
|
174 """ |
|
175 if not Preferences.getJedi("JediCompletionsEnabled"): |
|
176 return |
|
177 |
|
178 idString = self.__idString(editor) |
|
179 if not idString: |
|
180 return |
|
181 |
|
182 filename, line, index, source = self.__prepareData(editor) |
|
183 fuzzy = Preferences.getJedi("JediFuzzyCompletionsEnabled") |
|
184 |
|
185 self.__ensureActive(idString) |
|
186 |
|
187 self.sendJson("getCompletions", { |
|
188 "FileName": filename, |
|
189 "Source": source, |
|
190 "Line": line, |
|
191 "Index": index, |
|
192 "Fuzzy": fuzzy, |
|
193 "CompletionText": acText, |
|
194 }, idString=idString) |
|
195 |
|
196 def __processCompletionsResult(self, result): |
|
197 """ |
|
198 Private method to process the completions sent by the client. |
|
199 |
|
200 @param result dictionary containing the result sent by the client |
|
201 @type dict |
|
202 """ |
|
203 names = [] |
|
204 for completion in result["Completions"]: |
|
205 name = completion['Name'] |
|
206 context = completion['FullName'] |
|
207 if context.endswith(".{0}".format(name)): |
|
208 context = context.rsplit(".", 1)[0] |
|
209 if context: |
|
210 name = "{0} ({1})".format(name, context) |
|
211 |
|
212 name += JediServer.PictureIDs.get(completion['CompletionType'], '') |
|
213 names.append(name) |
|
214 |
|
215 if "Error" not in result: |
|
216 editor = self.__vm.getOpenEditor(result["FileName"]) |
|
217 if editor is not None: |
|
218 editor.completionsListReady(names, |
|
219 result["CompletionText"]) |
|
220 |
|
221 def getCallTips(self, editor, pos, commas): |
|
222 """ |
|
223 Public method to calculate calltips. |
|
224 |
|
225 @param editor reference to the editor object, that called this method |
|
226 @type Editor |
|
227 @param pos position in the text for the calltip |
|
228 @type int |
|
229 @param commas minimum number of commas contained in the calltip |
|
230 @type int |
|
231 @return list of possible calltips |
|
232 @rtype list of str |
|
233 """ |
|
234 if not Preferences.getJedi("JediCalltipsEnabled"): |
|
235 return [] |
|
236 |
|
237 # reset the calltips buffer |
|
238 self.__calltips = None |
|
239 |
|
240 idString = self.__idString(editor) |
|
241 if not idString: |
|
242 return [] |
|
243 |
|
244 filename, line, index, source = self.__prepareData(editor) |
|
245 |
|
246 self.__ensureActive(idString) |
|
247 self.sendJson("getCallTips", { |
|
248 "FileName": filename, |
|
249 "Source": source, |
|
250 "Line": line, |
|
251 "Index": index, |
|
252 }, idString=idString) |
|
253 |
|
254 # emulate the synchronous behaviour |
|
255 timer = QTimer() |
|
256 timer.setSingleShot(True) |
|
257 timer.start(5000) # 5s timeout |
|
258 while self.__calltips is None and timer.isActive(): |
|
259 QCoreApplication.processEvents() |
|
260 |
|
261 return [] if self.__calltips is None else self.__calltips |
|
262 |
|
263 def __processCallTipsResult(self, result): |
|
264 """ |
|
265 Private method to process the calltips sent by the client. |
|
266 |
|
267 @param result dictionary containing the result sent by the client |
|
268 @type dict |
|
269 """ |
|
270 if "Error" in result: |
|
271 self.__calltips = [] |
|
272 else: |
|
273 self.__calltips = result["CallTips"] |
|
274 |
|
275 def requestCodeDocumentation(self, editor): |
|
276 """ |
|
277 Public method to request source code documentation for the given |
|
278 editor. |
|
279 |
|
280 @param editor reference to the editor to get source code documentation |
|
281 for |
|
282 @type Editor |
|
283 """ |
|
284 if self.__documentationViewer is None: |
|
285 return |
|
286 |
|
287 idString = self.__idString(editor) |
|
288 |
|
289 if not idString: |
|
290 language = editor.getLanguage() |
|
291 warning = ( |
|
292 self.tr("Language <b>{0}</b> is not supported.") |
|
293 .format(language) |
|
294 ) |
|
295 self.__documentationViewer.documentationReady( |
|
296 warning, isWarning=True) |
|
297 return |
|
298 |
|
299 filename, line, index, source = self.__prepareData(editor) |
|
300 sourceLines = source.splitlines() |
|
301 # Correct index if cursor is standing after an opening bracket |
|
302 if line > 0 and index > 0 and sourceLines[line - 1][index - 1] == '(': |
|
303 index -= 1 |
|
304 |
|
305 self.__ensureActive(idString) |
|
306 self.sendJson("getDocumentation", { |
|
307 "FileName": filename, |
|
308 "Source": source, |
|
309 "Line": line, |
|
310 "Index": index, |
|
311 }, idString=idString) |
|
312 |
|
313 def __processDocumentationResult(self, result): |
|
314 """ |
|
315 Private method to process the documentation sent by the client. |
|
316 |
|
317 @param result dictionary containing the result sent by the client |
|
318 @type dict with keys 'name', 'module', 'argspec', 'docstring' |
|
319 """ |
|
320 if self.__documentationViewer is None: |
|
321 return |
|
322 |
|
323 docu = None |
|
324 |
|
325 if "Error" not in result: |
|
326 docu = result["DocumentationDict"] |
|
327 docu["note"] = ( |
|
328 self.tr("Present in <i>{0}</i> module") |
|
329 .format(docu["module"])) |
|
330 |
|
331 if docu is None: |
|
332 msg = self.tr("No documentation available.") |
|
333 self.__documentationViewer.documentationReady( |
|
334 msg, isDocWarning=True) |
|
335 else: |
|
336 self.__documentationViewer.documentationReady(docu) |
|
337 |
|
338 def gotoDefinition(self, editor): |
|
339 """ |
|
340 Public slot to find the definition for the word at the cursor position |
|
341 and go to it. |
|
342 |
|
343 Note: This is executed upon a mouse click sequence. |
|
344 |
|
345 @param editor reference to the calling editor |
|
346 @type Editor |
|
347 """ |
|
348 if not Preferences.getJedi("MouseClickEnabled"): |
|
349 return |
|
350 |
|
351 idString = self.__idString(editor) |
|
352 if not idString: |
|
353 return |
|
354 |
|
355 filename, line, index, source = self.__prepareData(editor) |
|
356 |
|
357 self.__ensureActive(idString) |
|
358 |
|
359 euuid = str(uuid.uuid4()) |
|
360 self.__editors[euuid] = editor |
|
361 |
|
362 self.sendJson("gotoDefinition", { |
|
363 "FileName": filename, |
|
364 "Source": source, |
|
365 "Line": line, |
|
366 "Index": index, |
|
367 "Uuid": euuid, |
|
368 }, idString=idString) |
|
369 |
|
370 def __processGotoDefinitionResult(self, result): |
|
371 """ |
|
372 Private method callback for the goto definition result. |
|
373 |
|
374 @param result dictionary containing the result data |
|
375 @type dict |
|
376 """ |
|
377 euuid = result["Uuid"] |
|
378 if "Error" not in result: |
|
379 # ignore errors silently |
|
380 location = result["GotoDefinitionDict"] |
|
381 if location: |
|
382 try: |
|
383 editor = self.__editors[euuid] |
|
384 except KeyError: |
|
385 editor = None |
|
386 |
|
387 if ( |
|
388 editor is not None and |
|
389 editor.getFileName() == location["ModulePath"] and |
|
390 editor.getCursorPosition()[0] + 1 == location["Line"] |
|
391 ): |
|
392 # this was a click onto the definition line |
|
393 # -> get references |
|
394 idString = self.__idString(editor) |
|
395 filename, line, index, source = self.__prepareData(editor) |
|
396 self.sendJson("gotoReferences", { |
|
397 "FileName": filename, |
|
398 "Source": source, |
|
399 "Line": line, |
|
400 "Index": index, |
|
401 "Uuid": euuid, |
|
402 }, idString=idString) |
|
403 return |
|
404 |
|
405 self.__vm.openSourceFile(location["ModulePath"], |
|
406 location["Line"], |
|
407 addNext=True) |
|
408 else: |
|
409 ericApp().getObject("UserInterface").statusBar().showMessage( |
|
410 self.tr('Jedi: No definition found'), 5000) |
|
411 |
|
412 with contextlib.suppress(KeyError): |
|
413 del self.__editors[euuid] |
|
414 |
|
415 def __processGotoReferencesResult(self, result): |
|
416 """ |
|
417 Private method callback for the goto references result. |
|
418 |
|
419 @param result dictionary containing the result data |
|
420 @type dict |
|
421 """ |
|
422 euuid = result["Uuid"] |
|
423 with contextlib.suppress(ImportError): |
|
424 from QScintilla.Editor import ReferenceItem |
|
425 |
|
426 if "Error" not in result: |
|
427 # ignore errors silently |
|
428 references = result["GotoReferencesList"] |
|
429 if references: |
|
430 try: |
|
431 editor = self.__editors[euuid] |
|
432 except KeyError: |
|
433 editor = None |
|
434 if editor is not None: |
|
435 referenceItemsList = [ |
|
436 ReferenceItem( |
|
437 modulePath=ref["ModulePath"], |
|
438 codeLine=ref["Code"], |
|
439 line=ref["Line"], |
|
440 column=ref["Column"], |
|
441 ) for ref in references |
|
442 ] |
|
443 editor.gotoReferenceHandler(referenceItemsList) |
|
444 |
|
445 with contextlib.suppress(KeyError): |
|
446 del self.__editors[euuid] |
|
447 |
|
448 def hoverHelp(self, editor, line, index): |
|
449 """ |
|
450 Public method to initiate the display of mouse hover help. |
|
451 |
|
452 @param editor reference to the calling editor |
|
453 @type Editor |
|
454 @param line line number (zero based) |
|
455 @type int |
|
456 @param index index within the line (zero based) |
|
457 @type int |
|
458 """ |
|
459 idString = self.__idString(editor) |
|
460 if not idString: |
|
461 return |
|
462 |
|
463 filename = editor.getFileName() |
|
464 line += 1 # jedi line numbers are 1 based |
|
465 source = editor.text() |
|
466 |
|
467 self.__ensureActive(idString) |
|
468 |
|
469 euuid = str(uuid.uuid4()) |
|
470 self.__editors[euuid] = editor |
|
471 |
|
472 self.sendJson("hoverHelp", { |
|
473 "FileName": filename, |
|
474 "Source": source, |
|
475 "Line": line, |
|
476 "Index": index, |
|
477 "Uuid": euuid, |
|
478 }, idString=idString) |
|
479 |
|
480 def __processHoverHelpResult(self, result): |
|
481 """ |
|
482 Private method callback for the goto definition result. |
|
483 |
|
484 @param result dictionary containing the result data |
|
485 @type dict |
|
486 """ |
|
487 euuid = result["Uuid"] |
|
488 if "Error" not in result: |
|
489 # ignore errors silently |
|
490 helpText = result["HoverHelp"] |
|
491 if helpText: |
|
492 with contextlib.suppress(KeyError): |
|
493 self.__editors[euuid].showMouseHoverHelpData( |
|
494 result["Line"] - 1, |
|
495 result["Index"], |
|
496 helpText |
|
497 ) |
|
498 else: |
|
499 ericApp().getObject("UserInterface").statusBar().showMessage( |
|
500 self.tr('Jedi: No mouse hover help found'), 5000) |
|
501 |
|
502 with contextlib.suppress(KeyError): |
|
503 del self.__editors[euuid] |
|
504 |
|
505 ####################################################################### |
|
506 ## Methods below handle the network connection |
|
507 ####################################################################### |
|
508 |
|
509 def handleCall(self, method, params): |
|
510 """ |
|
511 Public method to handle a method call from the client. |
|
512 |
|
513 @param method requested method name |
|
514 @type str |
|
515 @param params dictionary with method specific parameters |
|
516 @type dict |
|
517 """ |
|
518 self.__methodMapping[method](params) |
|
519 |
|
520 def __processClientException(self, params): |
|
521 """ |
|
522 Private method to handle exceptions of the refactoring client. |
|
523 |
|
524 @param params dictionary containing the exception data |
|
525 @type dict |
|
526 """ |
|
527 if params["ExceptionType"] == "ProtocolError": |
|
528 self.__ui.appendToStderr( |
|
529 self.tr("The data received from the Jedi server could not be" |
|
530 " decoded. Please report this issue with the received" |
|
531 " data to the eric bugs email address.\n" |
|
532 "Error: {0}\n" |
|
533 "Data:\n{1}\n").format( |
|
534 params["ExceptionValue"], |
|
535 params["ProtocolData"])) |
|
536 else: |
|
537 self.__ui.appendToStderr( |
|
538 self.tr("An exception happened in the Jedi client. Please" |
|
539 " report it to the eric bugs email address.\n" |
|
540 "Exception: {0}\n" |
|
541 "Value: {1}\n" |
|
542 "Traceback: {2}\n").format( |
|
543 params["ExceptionType"], |
|
544 params["ExceptionValue"], |
|
545 params["Traceback"])) |
|
546 |
|
547 def __startJediClient(self, interpreter, idString, clientEnv): |
|
548 """ |
|
549 Private method to start the Jedi client with the given interpreter. |
|
550 |
|
551 @param interpreter interpreter to be used for the Jedi client |
|
552 @type str |
|
553 @param idString id of the client to be started |
|
554 @type str |
|
555 @param clientEnv dictionary with environment variables to run the |
|
556 interpreter with |
|
557 @type dict |
|
558 @return flag indicating a successful start of the client |
|
559 @rtype bool |
|
560 """ |
|
561 ok = False |
|
562 |
|
563 if interpreter: |
|
564 client = os.path.join(os.path.dirname(__file__), |
|
565 "JediClient.py") |
|
566 ok, exitCode = self.startClient( |
|
567 interpreter, client, |
|
568 [Globals.getPythonLibraryDirectory()], |
|
569 idString=idString, environment=clientEnv) |
|
570 if not ok: |
|
571 if exitCode == 42: |
|
572 self.__ui.appendToStderr("JediServer: " + self.tr( |
|
573 "The jedi and/or parso library is not installed.\n" |
|
574 )) |
|
575 else: |
|
576 self.__ui.appendToStderr("JediServer: " + self.tr( |
|
577 "'{0}' is not supported because the configured" |
|
578 " interpreter could not be started.\n" |
|
579 ).format(idString)) |
|
580 else: |
|
581 self.__ui.appendToStderr("JediServer: " + self.tr( |
|
582 "'{0}' is not supported because no suitable interpreter is" |
|
583 " configured.\n" |
|
584 ).format(idString)) |
|
585 |
|
586 return ok |
|
587 |
|
588 def __ensureActive(self, idString): |
|
589 """ |
|
590 Private method to ensure, that the requested client is active. |
|
591 |
|
592 A non-active client will be started. |
|
593 |
|
594 @param idString id of the client to be checked |
|
595 @type str |
|
596 @return flag indicating an active client |
|
597 @rtype bool |
|
598 """ |
|
599 ok = idString in self.connectionNames() |
|
600 if not ok: |
|
601 # client is not running |
|
602 if idString == JediServer.IdProject: |
|
603 interpreter, clientEnv = self.__interpreterForProject() |
|
604 else: |
|
605 interpreter = "" |
|
606 venvName = "" |
|
607 clientEnv = os.environ.copy() |
|
608 if "PATH" in clientEnv: |
|
609 clientEnv["PATH"] = self.__ui.getOriginalPathString() |
|
610 # new code using virtual environments |
|
611 venvManager = ericApp().getObject("VirtualEnvManager") |
|
612 if idString == "Python3": |
|
613 venvName = Preferences.getDebugger("Python3VirtualEnv") |
|
614 if not venvName: |
|
615 venvName, _ = venvManager.getDefaultEnvironment() |
|
616 if venvName: |
|
617 interpreter = venvManager.getVirtualenvInterpreter( |
|
618 venvName) |
|
619 execPath = venvManager.getVirtualenvExecPath(venvName) |
|
620 |
|
621 # build a suitable environment |
|
622 if execPath: |
|
623 if "PATH" in clientEnv: |
|
624 clientEnv["PATH"] = os.pathsep.join( |
|
625 [execPath, clientEnv["PATH"]]) |
|
626 else: |
|
627 clientEnv["PATH"] = execPath |
|
628 if interpreter: |
|
629 ok = self.__startJediClient(interpreter, idString, clientEnv) |
|
630 else: |
|
631 ok = False |
|
632 return ok |
|
633 |
|
634 def __interpreterForProject(self): |
|
635 """ |
|
636 Private method to determine the interpreter for the current project and |
|
637 the environment to run it. |
|
638 |
|
639 @return tuple containing the interpreter of the current project and the |
|
640 environment variables |
|
641 @rtype tuple of (str, dict) |
|
642 """ |
|
643 projectLanguage = self.__ericProject.getProjectLanguage() |
|
644 interpreter = "" |
|
645 clientEnv = os.environ.copy() |
|
646 if "PATH" in clientEnv: |
|
647 clientEnv["PATH"] = self.__ui.getOriginalPathString() |
|
648 |
|
649 if (projectLanguage.startswith("Python") or |
|
650 projectLanguage == "MicroPython"): |
|
651 # new code using virtual environments |
|
652 venvManager = ericApp().getObject("VirtualEnvManager") |
|
653 |
|
654 # get virtual environment from project first |
|
655 venvName = self.__ericProject.getDebugProperty("VIRTUALENV") |
|
656 if not venvName: |
|
657 # get it from debugger settings next |
|
658 if projectLanguage in ("Python3", "MicroPython", "Cython"): |
|
659 venvName = Preferences.getDebugger("Python3VirtualEnv") |
|
660 if not venvName: |
|
661 venvName, _ = venvManager.getDefaultEnvironment() |
|
662 else: |
|
663 venvName = "" |
|
664 if venvName: |
|
665 interpreter = venvManager.getVirtualenvInterpreter( |
|
666 venvName) |
|
667 execPath = venvManager.getVirtualenvExecPath(venvName) |
|
668 |
|
669 # build a suitable environment |
|
670 if execPath: |
|
671 if "PATH" in clientEnv: |
|
672 clientEnv["PATH"] = os.pathsep.join( |
|
673 [execPath, clientEnv["PATH"]]) |
|
674 else: |
|
675 clientEnv["PATH"] = execPath |
|
676 |
|
677 return interpreter, clientEnv |
|
678 |
|
679 @pyqtSlot() |
|
680 def handleNewConnection(self): |
|
681 """ |
|
682 Public slot for new incoming connections from a client. |
|
683 """ |
|
684 super().handleNewConnection() |
|
685 |
|
686 self.__updateEditorLanguageMapping() |
|
687 |
|
688 def activate(self): |
|
689 """ |
|
690 Public method to activate the Jedi server. |
|
691 """ |
|
692 self.__documentationViewer = self.__ui.documentationViewer() |
|
693 if self.__documentationViewer is not None: |
|
694 self.__documentationViewer.registerProvider( |
|
695 "jedi", self.tr("Jedi"), self.requestCodeDocumentation, |
|
696 self.isSupportedLanguage) |
|
697 |
|
698 self.__ericProject.projectOpened.connect(self.__projectOpened) |
|
699 self.__ericProject.projectClosed.connect(self.__projectClosed) |
|
700 |
|
701 def deactivate(self): |
|
702 """ |
|
703 Public method to deactivate the code assist server. |
|
704 """ |
|
705 """ |
|
706 Public method to shut down the code assist server. |
|
707 """ |
|
708 if self.__documentationViewer is not None: |
|
709 self.__documentationViewer.unregisterProvider("jedi") |
|
710 |
|
711 with contextlib.suppress(TypeError): |
|
712 self.__ericProject.projectOpened.disconnect(self.__projectOpened) |
|
713 self.__ericProject.projectClosed.disconnect(self.__projectClosed) |
|
714 |
|
715 self.stopAllClients() |
|
716 |
|
717 @pyqtSlot() |
|
718 def __projectOpened(self): |
|
719 """ |
|
720 Private slot to handle the projectOpened signal. |
|
721 """ |
|
722 self.__ensureActive(JediServer.IdProject) |
|
723 self.sendJson("openProject", { |
|
724 "ProjectPath": self.__ericProject.getProjectPath(), |
|
725 }, idString=JediServer.IdProject) |
|
726 |
|
727 @pyqtSlot() |
|
728 def __projectClosed(self): |
|
729 """ |
|
730 Private slot to handle the projectClosed signal. |
|
731 """ |
|
732 self.__ensureActive(JediServer.IdProject) |
|
733 self.sendJson("closeProject", {}, idString=JediServer.IdProject) |
|
734 |
|
735 self.stopClient(idString=JediServer.IdProject) |