src/eric7/JediInterface/JediServer.py

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

eric ide

mercurial