src/eric7/JediInterface/JediServer.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 9069
938039ea15ca
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2015 - 2022 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, QThread
15 from PyQt6.QtWidgets import QInputDialog, QLineEdit, QDialog
16
17 from EricWidgets.EricApplication import ericApp
18 from EricWidgets import EricMessageBox
19
20 from EricNetwork.EricJsonServer import EricJsonServer
21
22 from QScintilla.Editor import Editor
23
24 import Preferences
25 import Globals
26
27 from .RefactoringPreviewDialog import RefactoringPreviewDialog
28
29
30 class JediServer(EricJsonServer):
31 """
32 Class implementing the interface to the jedi library.
33 """
34 IdProject = "Project"
35
36 PictureIDs = {
37 "class": "?{0}".format(Editor.ClassID),
38 "_class": "?{0}".format(Editor.ClassProtectedID),
39 "__class": "?{0}".format(Editor.ClassPrivateID),
40 "instance": "?{0}".format(Editor.ClassID),
41 "_instance": "?{0}".format(Editor.ClassProtectedID),
42 "__instance": "?{0}".format(Editor.ClassPrivateID),
43 "function": "?{0}".format(Editor.MethodID),
44 "_function": "?{0}".format(Editor.MethodProtectedID),
45 "__function": "?{0}".format(Editor.MethodPrivateID),
46 "module": "?{0}".format(Editor.ModuleID),
47 "_module": "?{0}".format(Editor.ModuleID),
48 "__module": "?{0}".format(Editor.ModuleID),
49 "param": "?{0}".format(Editor.AttributeID),
50 "_param": "?{0}".format(Editor.AttributeProtectedID),
51 "__param": "?{0}".format(Editor.AttributePrivateID),
52 "statement": "?{0}".format(Editor.AttributeID),
53 "_statement": "?{0}".format(Editor.AttributeProtectedID),
54 "__statement": "?{0}".format(Editor.AttributePrivateID),
55 "import": "",
56 "None": "",
57 }
58
59 def __init__(self, viewManager, project, ui):
60 """
61 Constructor
62
63 @param viewManager reference to the viewmanager object
64 @type ViewManager
65 @param project reference to the project object
66 @type Project
67 @param ui reference to the user interface
68 @type UserInterface
69 """
70 super().__init__(
71 "JediServer", multiplex=True, parent=ui)
72
73 self.__ui = ui
74 self.__vm = viewManager
75 self.__ericProject = project
76
77 self.__editorLanguageMapping = {}
78
79 self.__documentationViewer = None
80
81 # attributes to store the resuls of the client side
82 self.__completions = None
83 self.__calltips = None
84
85 self.__methodMapping = {
86 "CompletionsResult": self.__processCompletionsResult,
87 "CallTipsResult": self.__processCallTipsResult,
88 "DocumentationResult": self.__processDocumentationResult,
89 "HoverHelpResult": self.__processHoverHelpResult,
90 "GotoDefinitionResult": self.__processGotoDefinitionResult,
91 "GotoReferencesResult": self.__processGotoReferencesResult,
92
93 "RefactoringDiff": self.__showRefactoringDiff,
94 "RefactoringApplyResult": self.__checkRefactoringResult,
95
96 "ClientException": self.__processClientException,
97 }
98
99 # temporary store for editor references indexed by Uuid
100 self.__editors = {}
101
102 # Python 3
103 self.__ensureActive("Python3")
104
105 def __updateEditorLanguageMapping(self):
106 """
107 Private method to update the editor language to connection mapping.
108 """
109 self.__editorLanguageMapping = {}
110 for name in self.connectionNames():
111 if name == "Python3":
112 self.__editorLanguageMapping.update({
113 "Python3": "Python3",
114 "MicroPython": "Python3",
115 "Pygments|Python": "Python3",
116 "Pygments|Python 2.x": "Python3",
117 "Cython": "Python3",
118 })
119
120 def isSupportedLanguage(self, language):
121 """
122 Public method to check, if the given language is supported.
123
124 @param language editor programming language to check
125 @type str
126 @return flag indicating the support status
127 @rtype bool
128 """
129 return language in self.__editorLanguageMapping
130
131 def __idString(self, editor):
132 """
133 Private method to determine the ID string for the back-end.
134
135 @param editor reference to the editor to determine the ID string for
136 @type Editor
137 @return ID string
138 @rtype str
139 """
140 idString = ""
141
142 language = editor.getLanguage()
143 if (
144 self.__ericProject.isOpen() and
145 self.__ericProject.getProjectLanguage() == language
146 ):
147 filename = editor.getFileName()
148 if self.__ericProject.isProjectSource(filename):
149 idString = JediServer.IdProject
150
151 if not idString and language in self.__editorLanguageMapping:
152 idString = self.__editorLanguageMapping[language]
153
154 return idString
155
156 def __prepareData(self, editor):
157 """
158 Private method to gather data about current cursor position.
159
160 @param editor reference to the editor object, that called this method
161 @type Editor
162 @return tuple of filename, line, index, source
163 @rtype tuple (str, int, int, str)
164 """
165 filename = editor.getFileName()
166 line, index = editor.getCursorPosition()
167 line += 1 # jedi line numbers are 1 based
168 source = editor.text()
169 return filename, line, index, source
170
171 def requestCompletions(self, editor, context, acText):
172 """
173 Public method to request a list of possible completions.
174
175 @param editor reference to the editor object, that called this method
176 @type Editor
177 @param context flag indicating to autocomplete a context
178 @type bool
179 @param acText text to be completed
180 @type str
181 """
182 if not Preferences.getJedi("JediCompletionsEnabled"):
183 return
184
185 idString = self.__idString(editor)
186 if not idString:
187 return
188
189 filename, line, index, source = self.__prepareData(editor)
190 fuzzy = Preferences.getJedi("JediFuzzyCompletionsEnabled")
191
192 self.__ensureActive(idString)
193
194 self.sendJson("getCompletions", {
195 "FileName": filename,
196 "Source": source,
197 "Line": line,
198 "Index": index,
199 "Fuzzy": fuzzy,
200 "CompletionText": acText,
201 }, idString=idString)
202
203 def __processCompletionsResult(self, result):
204 """
205 Private method to process the completions sent by the client.
206
207 @param result dictionary containing the result sent by the client
208 @type dict
209 """
210 names = []
211 for completion in result["Completions"]:
212 name = completion['Name']
213 context = completion['FullName']
214 if context:
215 if context.endswith(".{0}".format(name)):
216 context = context.rsplit(".", 1)[0]
217 name = "{0} ({1})".format(name, context)
218
219 name += JediServer.PictureIDs.get(completion['CompletionType'], '')
220 names.append(name)
221
222 if "Error" not in result:
223 editor = self.__vm.getOpenEditor(result["FileName"])
224 if editor is not None:
225 editor.completionsListReady(names,
226 result["CompletionText"])
227
228 def getCallTips(self, editor, pos, commas):
229 """
230 Public method to calculate calltips.
231
232 @param editor reference to the editor object, that called this method
233 @type Editor
234 @param pos position in the text for the calltip
235 @type int
236 @param commas minimum number of commas contained in the calltip
237 @type int
238 @return list of possible calltips
239 @rtype list of str
240 """
241 if not Preferences.getJedi("JediCalltipsEnabled"):
242 return []
243
244 # reset the calltips buffer
245 self.__calltips = None
246
247 idString = self.__idString(editor)
248 if not idString:
249 return []
250
251 filename, line, index, source = self.__prepareData(editor)
252
253 self.__ensureActive(idString)
254 self.sendJson("getCallTips", {
255 "FileName": filename,
256 "Source": source,
257 "Line": line,
258 "Index": index,
259 }, idString=idString)
260
261 # emulate the synchronous behaviour
262 timer = QTimer()
263 timer.setSingleShot(True)
264 timer.start(5000) # 5s timeout
265 while self.__calltips is None and timer.isActive():
266 QCoreApplication.processEvents()
267 QThread.msleep(100)
268
269 return [] if self.__calltips is None else self.__calltips
270
271 def __processCallTipsResult(self, result):
272 """
273 Private method to process the calltips sent by the client.
274
275 @param result dictionary containing the result sent by the client
276 @type dict
277 """
278 if "Error" in result:
279 self.__calltips = []
280 else:
281 self.__calltips = result["CallTips"]
282
283 def requestCodeDocumentation(self, editor):
284 """
285 Public method to request source code documentation for the given
286 editor.
287
288 @param editor reference to the editor to get source code documentation
289 for
290 @type Editor
291 """
292 if self.__documentationViewer is None:
293 return
294
295 idString = self.__idString(editor)
296
297 if not idString:
298 language = editor.getLanguage()
299 warning = (
300 self.tr("Language <b>{0}</b> is not supported.")
301 .format(language)
302 )
303 self.__documentationViewer.documentationReady(
304 warning, isWarning=True)
305 return
306
307 filename, line, index, source = self.__prepareData(editor)
308 sourceLines = source.splitlines()
309 # Correct index if cursor is standing after an opening bracket
310 if line > 0 and index > 0 and sourceLines[line - 1][index - 1] == '(':
311 index -= 1
312
313 self.__ensureActive(idString)
314 self.sendJson("getDocumentation", {
315 "FileName": filename,
316 "Source": source,
317 "Line": line,
318 "Index": index,
319 }, idString=idString)
320
321 def __processDocumentationResult(self, result):
322 """
323 Private method to process the documentation sent by the client.
324
325 @param result dictionary containing the result sent by the client
326 @type dict with keys 'name', 'module', 'argspec', 'docstring'
327 """
328 if self.__documentationViewer is None:
329 return
330
331 docu = None
332
333 if "Error" not in result:
334 docu = result["DocumentationDict"]
335 docu["note"] = (
336 self.tr("Present in <i>{0}</i> module")
337 .format(docu["module"]))
338
339 if docu is None:
340 msg = self.tr("No documentation available.")
341 self.__documentationViewer.documentationReady(
342 msg, isDocWarning=True)
343 else:
344 self.__documentationViewer.documentationReady(docu)
345
346 def gotoDefinition(self, editor):
347 """
348 Public slot to find the definition for the word at the cursor position
349 and go to it.
350
351 Note: This is executed upon a mouse click sequence.
352
353 @param editor reference to the calling editor
354 @type Editor
355 """
356 if not Preferences.getJedi("MouseClickEnabled"):
357 return
358
359 idString = self.__idString(editor)
360 if not idString:
361 return
362
363 filename, line, index, source = self.__prepareData(editor)
364
365 self.__ensureActive(idString)
366
367 euuid = str(uuid.uuid4())
368 self.__editors[euuid] = editor
369
370 self.sendJson("gotoDefinition", {
371 "FileName": filename,
372 "Source": source,
373 "Line": line,
374 "Index": index,
375 "Uuid": euuid,
376 }, idString=idString)
377
378 def __processGotoDefinitionResult(self, result):
379 """
380 Private method callback for the goto definition result.
381
382 @param result dictionary containing the result data
383 @type dict
384 """
385 euuid = result["Uuid"]
386 if "Error" not in result:
387 # ignore errors silently
388 location = result["GotoDefinitionDict"]
389 if location:
390 self.__vm.openSourceFile(location["ModulePath"],
391 location["Line"],
392 addNext=True)
393 else:
394 ericApp().getObject("UserInterface").statusBar().showMessage(
395 self.tr('Jedi: No definition found'), 5000)
396
397 with contextlib.suppress(KeyError):
398 del self.__editors[euuid]
399
400 def __processGotoReferencesResult(self, result):
401 """
402 Private method callback for the goto references result.
403
404 @param result dictionary containing the result data
405 @type dict
406 """
407 euuid = result["Uuid"]
408 with contextlib.suppress(ImportError):
409 from QScintilla.Editor import ReferenceItem
410
411 if "Error" not in result:
412 # ignore errors silently
413 references = result["GotoReferencesList"]
414 if references:
415 try:
416 editor = self.__editors[euuid]
417 except KeyError:
418 editor = None
419 if editor is not None:
420 referenceItemsList = [
421 ReferenceItem(
422 modulePath=ref["ModulePath"],
423 codeLine=ref["Code"],
424 line=ref["Line"],
425 column=ref["Column"],
426 ) for ref in references
427 ]
428 editor.gotoReferenceHandler(referenceItemsList)
429
430 with contextlib.suppress(KeyError):
431 del self.__editors[euuid]
432
433 def hoverHelp(self, editor, line, index):
434 """
435 Public method to initiate the display of mouse hover help.
436
437 @param editor reference to the calling editor
438 @type Editor
439 @param line line number (zero based)
440 @type int
441 @param index index within the line (zero based)
442 @type int
443 """
444 idString = self.__idString(editor)
445 if not idString:
446 return
447
448 filename = editor.getFileName()
449 line += 1 # jedi line numbers are 1 based
450 source = editor.text()
451
452 self.__ensureActive(idString)
453
454 euuid = str(uuid.uuid4())
455 self.__editors[euuid] = editor
456
457 self.sendJson("hoverHelp", {
458 "FileName": filename,
459 "Source": source,
460 "Line": line,
461 "Index": index,
462 "Uuid": euuid,
463 }, idString=idString)
464
465 def __processHoverHelpResult(self, result):
466 """
467 Private method callback for the goto definition result.
468
469 @param result dictionary containing the result data
470 @type dict
471 """
472 euuid = result["Uuid"]
473 if "Error" not in result:
474 # ignore errors silently
475 helpText = result["HoverHelp"]
476 if helpText:
477 with contextlib.suppress(KeyError):
478 self.__editors[euuid].showMouseHoverHelpData(
479 result["Line"] - 1,
480 result["Index"],
481 helpText
482 )
483 else:
484 ericApp().getObject("UserInterface").statusBar().showMessage(
485 self.tr('Jedi: No mouse hover help found'), 5000)
486
487 with contextlib.suppress(KeyError):
488 del self.__editors[euuid]
489
490 #######################################################################
491 ## Refactoring methods below
492 #######################################################################
493
494 @pyqtSlot()
495 def refactoringRenameVariable(self):
496 """
497 Public slot to rename the selected variable.
498 """
499 editor = self.__vm.activeWindow()
500 if editor:
501 idString = self.__idString(editor)
502 if not idString:
503 return
504
505 newName, ok = QInputDialog.getText(
506 None,
507 self.tr("Rename Variable"),
508 self.tr("Enter the new name for the variable:"),
509 QLineEdit.EchoMode.Normal,
510 editor.selectedText()
511 )
512
513 if ok and newName and self.__vm.checkAllDirty():
514 filename = editor.getFileName()
515 line, index = editor.getCursorPosition()
516 source = editor.text()
517
518 self.__ensureActive(idString)
519
520 euuid = str(uuid.uuid4())
521 self.__editors[euuid] = editor
522
523 self.sendJson("renameVariable", {
524 "FileName": filename,
525 "Source": source,
526 "Line": line + 1,
527 "Index": index,
528 "Uuid": euuid,
529 "NewName": newName,
530 }, idString=idString)
531
532 @pyqtSlot()
533 def refactoringExtractNewVariable(self):
534 """
535 Public slot to extract a statement to a new variable.
536 """
537 editor = self.__vm.activeWindow()
538 if editor:
539 idString = self.__idString(editor)
540 if not idString:
541 return
542
543 newName, ok = QInputDialog.getText(
544 None,
545 self.tr("Extract Variable"),
546 self.tr("Enter the name for the new variable:"),
547 QLineEdit.EchoMode.Normal
548 )
549
550 if ok and newName and editor.checkDirty():
551 filename = editor.getFileName()
552 sLine, sIndex, eLine, eIndex = editor.getSelection()
553 source = editor.text()
554
555 self.__ensureActive(idString)
556
557 euuid = str(uuid.uuid4())
558 self.__editors[euuid] = editor
559
560 self.sendJson("extractVariable", {
561 "FileName": filename,
562 "Source": source,
563 "Line": sLine + 1,
564 "Index": sIndex,
565 "EndLine": eLine + 1,
566 "EndIndex": eIndex,
567 "Uuid": euuid,
568 "NewName": newName,
569 }, idString=idString)
570
571 @pyqtSlot()
572 def refactoringInlineVariable(self):
573 """
574 Public slot to inline the selected variable.
575
576 Note: This is the opposite to Extract New Variable.
577 """
578 editor = self.__vm.activeWindow()
579 if editor:
580 idString = self.__idString(editor)
581 if not idString:
582 return
583
584 if editor.checkDirty():
585 filename = editor.getFileName()
586 line, index = editor.getCursorPosition()
587 source = editor.text()
588
589 self.__ensureActive(idString)
590
591 euuid = str(uuid.uuid4())
592 self.__editors[euuid] = editor
593
594 self.sendJson("inlineVariable", {
595 "FileName": filename,
596 "Source": source,
597 "Line": line + 1,
598 "Index": index,
599 "Uuid": euuid,
600 }, idString=idString)
601
602 @pyqtSlot()
603 def refactoringExtractFunction(self):
604 """
605 Public slot to extract an expression to a function.
606 """
607 editor = self.__vm.activeWindow()
608 if editor:
609 idString = self.__idString(editor)
610 if not idString:
611 return
612
613 newName, ok = QInputDialog.getText(
614 None,
615 self.tr("Extract Function"),
616 self.tr("Enter the name for the function:"),
617 QLineEdit.EchoMode.Normal
618 )
619
620 if ok and newName and editor.checkDirty():
621 filename = editor.getFileName()
622 sLine, sIndex, eLine, eIndex = editor.getSelection()
623 source = editor.text()
624
625 self.__ensureActive(idString)
626
627 euuid = str(uuid.uuid4())
628 self.__editors[euuid] = editor
629
630 self.sendJson("extractFunction", {
631 "FileName": filename,
632 "Source": source,
633 "Line": sLine + 1,
634 "Index": sIndex,
635 "EndLine": eLine + 1,
636 "EndIndex": eIndex,
637 "Uuid": euuid,
638 "NewName": newName,
639 }, idString=idString)
640
641 def __showRefactoringDiff(self, result):
642 """
643 Private method to show the diff of a refactoring.
644
645 @param result dictionary containing the result data
646 @type dict
647 """
648 if "Error" not in result:
649 euuid = result["Uuid"]
650 diff = result["Diff"]
651 dlg = RefactoringPreviewDialog(self.tr("Rename Variable"), diff)
652 if dlg.exec() == QDialog.DialogCode.Accepted:
653 self.__applyRefactoring(euuid)
654 else:
655 self.__cancelRefactoring(euuid)
656 else:
657 EricMessageBox.critical(
658 None,
659 self.tr("Refactoring"),
660 self.tr("<p>The refactoring could not be performed.</p>"
661 "<p>Reason: {0}</p>").format(result["ErrorString"])
662 )
663
664 def __applyRefactoring(self, uid):
665 """
666 Private method to apply a given refactoring.
667
668 @param uid UID of the calculated refactoring
669 @type str
670 """
671 with contextlib.suppress(KeyError):
672 editor = self.__editors[uid]
673 idString = self.__idString(editor)
674
675 self.sendJson("applyRefactoring", {
676 "Uuid": uid,
677 }, idString=idString)
678
679 del self.__editors[uid]
680
681 def __cancelRefactoring(self, uid):
682 """
683 Private method to cancel a given refactoring.
684
685 @param uid UID of the calculated refactoring
686 @type str
687 """
688 with contextlib.suppress(KeyError):
689 editor = self.__editors[uid]
690 idString = self.__idString(editor)
691
692 self.sendJson("cancelRefactoring", {
693 "Uuid": uid,
694 }, idString=idString)
695
696 del self.__editors[uid]
697
698 def __checkRefactoringResult(self, result):
699 """
700 Private method to check the refactoring result for errors.
701
702 @param result dictionary containing the result data
703 @type dict
704 """
705 if "Error" in result:
706 EricMessageBox.critical(
707 None,
708 self.tr("Apply Refactoring"),
709 self.tr("<p>The refactoring could not be applied.</p>"
710 "<p>Reason: {0}</p>").format(result["ErrorString"])
711 )
712
713 #######################################################################
714 ## Methods below handle the network connection
715 #######################################################################
716
717 def handleCall(self, method, params):
718 """
719 Public method to handle a method call from the client.
720
721 @param method requested method name
722 @type str
723 @param params dictionary with method specific parameters
724 @type dict
725 """
726 self.__methodMapping[method](params)
727
728 def __processClientException(self, params):
729 """
730 Private method to handle exceptions of the refactoring client.
731
732 @param params dictionary containing the exception data
733 @type dict
734 """
735 if params["ExceptionType"] == "ProtocolError":
736 self.__ui.appendToStderr(
737 self.tr("The data received from the Jedi server could not be"
738 " decoded. Please report this issue with the received"
739 " data to the eric bugs email address.\n"
740 "Error: {0}\n"
741 "Data:\n{1}\n").format(
742 params["ExceptionValue"],
743 params["ProtocolData"]))
744 else:
745 self.__ui.appendToStderr(
746 self.tr("An exception happened in the Jedi client. Please"
747 " report it to the eric bugs email address.\n"
748 "Exception: {0}\n"
749 "Value: {1}\n"
750 "Traceback: {2}\n").format(
751 params["ExceptionType"],
752 params["ExceptionValue"],
753 params["Traceback"]))
754
755 def __startJediClient(self, interpreter, idString, clientEnv):
756 """
757 Private method to start the Jedi client with the given interpreter.
758
759 @param interpreter interpreter to be used for the Jedi client
760 @type str
761 @param idString id of the client to be started
762 @type str
763 @param clientEnv dictionary with environment variables to run the
764 interpreter with
765 @type dict
766 @return flag indicating a successful start of the client
767 @rtype bool
768 """
769 ok = False
770
771 if interpreter:
772 client = os.path.join(os.path.dirname(__file__),
773 "JediClient.py")
774 ok, exitCode = self.startClient(
775 interpreter, client,
776 [Globals.getPythonLibraryDirectory()],
777 idString=idString, environment=clientEnv)
778 if not ok:
779 if exitCode == 42:
780 self.__ui.appendToStderr("JediServer: " + self.tr(
781 "The jedi and/or parso library is not installed.\n"
782 ))
783 else:
784 self.__ui.appendToStderr("JediServer: " + self.tr(
785 "'{0}' is not supported because the configured"
786 " interpreter could not be started.\n"
787 ).format(idString))
788 else:
789 self.__ui.appendToStderr("JediServer: " + self.tr(
790 "'{0}' is not supported because no suitable interpreter is"
791 " configured.\n"
792 ).format(idString))
793
794 return ok
795
796 def __ensureActive(self, idString):
797 """
798 Private method to ensure, that the requested client is active.
799
800 A non-active client will be started.
801
802 @param idString id of the client to be checked
803 @type str
804 @return flag indicating an active client
805 @rtype bool
806 """
807 ok = idString in self.connectionNames()
808 if not ok:
809 # client is not running
810 if idString == JediServer.IdProject:
811 interpreter, clientEnv = self.__interpreterForProject()
812 else:
813 interpreter = ""
814 venvName = ""
815 clientEnv = os.environ.copy()
816 if "PATH" in clientEnv:
817 clientEnv["PATH"] = self.__ui.getOriginalPathString()
818 # new code using virtual environments
819 venvManager = ericApp().getObject("VirtualEnvManager")
820 if idString == "Python3":
821 venvName = Preferences.getDebugger("Python3VirtualEnv")
822 if not venvName:
823 venvName, _ = venvManager.getDefaultEnvironment()
824 if venvName:
825 interpreter = venvManager.getVirtualenvInterpreter(
826 venvName)
827 execPath = venvManager.getVirtualenvExecPath(venvName)
828
829 # build a suitable environment
830 if execPath:
831 if "PATH" in clientEnv:
832 clientEnv["PATH"] = os.pathsep.join(
833 [execPath, clientEnv["PATH"]])
834 else:
835 clientEnv["PATH"] = execPath
836 if interpreter:
837 ok = self.__startJediClient(interpreter, idString, clientEnv)
838 else:
839 ok = False
840 return ok
841
842 def __interpreterForProject(self):
843 """
844 Private method to determine the interpreter for the current project and
845 the environment to run it.
846
847 @return tuple containing the interpreter of the current project and the
848 environment variables
849 @rtype tuple of (str, dict)
850 """
851 projectLanguage = self.__ericProject.getProjectLanguage()
852 interpreter = ""
853 clientEnv = os.environ.copy()
854 if "PATH" in clientEnv:
855 clientEnv["PATH"] = self.__ui.getOriginalPathString()
856
857 if projectLanguage in ("Python3", "MicroPython", "Cython"):
858 interpreter = self.__ericProject.getProjectInterpreter(
859 resolveGlobal=False)
860 if interpreter:
861 execPath = self.__ericProject.getProjectExecPath()
862
863 # build a suitable environment
864 if execPath:
865 if "PATH" in clientEnv:
866 clientEnv["PATH"] = os.pathsep.join(
867 [execPath, clientEnv["PATH"]])
868 else:
869 clientEnv["PATH"] = execPath
870
871 return interpreter, clientEnv
872
873 @pyqtSlot()
874 def handleNewConnection(self):
875 """
876 Public slot for new incoming connections from a client.
877 """
878 super().handleNewConnection()
879
880 self.__updateEditorLanguageMapping()
881
882 def activate(self):
883 """
884 Public method to activate the Jedi server.
885 """
886 self.__documentationViewer = self.__ui.documentationViewer()
887 if self.__documentationViewer is not None:
888 self.__documentationViewer.registerProvider(
889 "jedi", self.tr("Jedi"), self.requestCodeDocumentation,
890 self.isSupportedLanguage)
891
892 self.__ericProject.projectOpened.connect(self.__projectOpened)
893 self.__ericProject.projectClosed.connect(self.__projectClosed)
894
895 def deactivate(self):
896 """
897 Public method to deactivate the code assist server.
898 """
899 """
900 Public method to shut down the code assist server.
901 """
902 if self.__documentationViewer is not None:
903 self.__documentationViewer.unregisterProvider("jedi")
904
905 with contextlib.suppress(TypeError):
906 self.__ericProject.projectOpened.disconnect(self.__projectOpened)
907 self.__ericProject.projectClosed.disconnect(self.__projectClosed)
908
909 self.stopAllClients()
910
911 @pyqtSlot()
912 def __projectOpened(self):
913 """
914 Private slot to handle the projectOpened signal.
915 """
916 self.__ensureActive(JediServer.IdProject)
917 self.sendJson("openProject", {
918 "ProjectPath": self.__ericProject.getProjectPath(),
919 }, idString=JediServer.IdProject)
920
921 @pyqtSlot()
922 def __projectClosed(self):
923 """
924 Private slot to handle the projectClosed signal.
925 """
926 self.__ensureActive(JediServer.IdProject)
927 self.sendJson("closeProject", {}, idString=JediServer.IdProject)
928
929 self.stopClient(idString=JediServer.IdProject)

eric ide

mercurial